Massive cleaning (#5538)
[opentx.git] / radio / src / simu.cpp
blob2346870e68bd88b8d18c1bf99b1aaa629ef4c853
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "fx.h"
22 #include "FXExpression.h"
23 #include "FXPNGImage.h"
24 #include <unistd.h>
25 #include "fxkeys.h"
26 #include "opentx.h"
27 #include <time.h>
28 #include <ctype.h>
29 #include "targets/simu/simulcd.h"
31 #if defined(SIMU_AUDIO)
32 #include <SDL.h>
33 #undef main
34 #endif
36 #if LCD_W > 212
37 #define LCD_ZOOM 1
38 #else
39 #define LCD_ZOOM 2
40 #endif
42 #define W2 LCD_W*LCD_ZOOM
43 #define H2 LCD_H*LCD_ZOOM
45 class Open9xSim: public FXMainWindow
47 FXDECLARE(Open9xSim)
49 public:
50 Open9xSim(){};
51 Open9xSim(FXApp* a);
52 ~Open9xSim();
53 void updateKeysAndSwitches(bool start=false);
54 long onKeypress(FXObject*,FXSelector,void*);
55 long onTimeout(FXObject*,FXSelector,void*);
56 void createBitmap(int index, uint16_t *data, int x, int y, int w, int h);
57 void makeSnapshot(const FXDrawable* drawable);
58 void doEvents();
59 void refreshDisplay();
60 void setPixel(int x, int y, FXColor color);
62 private:
63 FXImage *bmp;
64 FXImageFrame *bmf;
65 bool firstTime;
67 public:
68 FXSlider *sliders[NUM_STICKS];
69 FXKnob *knobs[NUM_POTS+NUM_SLIDERS];
72 // Message Map
73 FXDEFMAP(Open9xSim) Open9xSimMap[] = {
74 //Message_Type _________ ID____Message_Handler_______
75 FXMAPFUNC(SEL_TIMEOUT, 2, Open9xSim::onTimeout),
76 FXMAPFUNC(SEL_KEYPRESS, 0, Open9xSim::onKeypress),
79 FXIMPLEMENT(Open9xSim,FXMainWindow,Open9xSimMap,ARRAYNUMBER(Open9xSimMap))
81 Open9xSim::Open9xSim(FXApp* a):
82 FXMainWindow(a, "OpenTX Simu", NULL, NULL, DECOR_ALL, 20, 90, 0, 0)
84 firstTime = true;
85 memset(displayBuf, 0, DISPLAY_BUFFER_SIZE);
86 bmp = new FXPPMImage(getApp(),NULL,IMAGE_OWNED|IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP, W2, H2);
88 #if defined(SIMU_AUDIO)
89 SDL_Init(SDL_INIT_AUDIO);
90 #endif
92 FXHorizontalFrame *hf11=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
93 FXHorizontalFrame *hf1=new FXHorizontalFrame(this,LAYOUT_FILL_X);
95 //rh lv rv lh
96 for (int i=0; i<4; i++) {
97 switch (i) {
98 #define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
99 case 0:
100 sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,10,110,100,20);
101 break;
102 case 1:
103 sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,110,10,20,100);
104 break;
105 case 2:
106 sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,130,10,20,100);
107 break;
108 case 3:
109 sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,150,110,100,20);
110 break;
111 default:;
113 sliders[i]->setRange(-1024, 1024);
114 sliders[i]->setTickDelta(7);
115 sliders[i]->setValue(0);
118 for(int i=0; i<NUM_POTS+NUM_SLIDERS; i++){
119 knobs[i]= new FXKnob(hf11,NULL,0,KNOB_TICKS|LAYOUT_LEFT);
120 knobs[i]->setValue(0);
122 #if defined(PCBHORUS)
123 if (i == 1) { // 6-pos switch
124 knobs[i]->setRange(0, 2048);
125 knobs[i]->setIncrement(2048 / 5);
126 knobs[i]->setTickDelta(2048 / 5);
127 continue;
129 #endif
130 knobs[i]->setRange(-1024, 1024);
133 bmf = new FXImageFrame(this,bmp);
135 updateKeysAndSwitches(true);
137 getApp()->addTimeout(this, 2, 100);
140 Open9xSim::~Open9xSim()
142 StopSimu();
143 StopAudioThread();
144 #if defined(EEPROM)
145 StopEepromThread();
146 #endif
148 delete bmp;
149 delete sliders[0];
150 delete sliders[1];
151 delete sliders[2];
152 delete sliders[3];
154 for(int i=0; i<NUM_POTS+NUM_SLIDERS; i++){
155 delete knobs[i];
158 delete bmf;
160 #if defined(SIMU_AUDIO)
161 SDL_Quit();
162 #endif
165 void Open9xSim::createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
167 FXPNGImage snapshot(getApp(), NULL, IMAGE_OWNED, w, h);
169 for (int i=0; i<w; i++) {
170 for (int j=0; j<h; j++) {
171 display_t z = data[(y+j) * LCD_W + (x+i)];
172 FXColor color = FXRGB(255*((z&0xF00)>>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f);
173 snapshot.setPixel(i, j, color);
177 FXFileStream stream;
178 char buf[32];
179 sprintf(buf,"%02d.png", index);
180 if (stream.open(buf, FXStreamSave)) {
181 snapshot.savePixels(stream);
182 stream.close();
183 TRACE("Bitmap %d (w=%d, h=%d) created", index, w, h);
185 else {
186 TRACE("Bitmap %d (w=%d, h=%d) error", index, w, h);
190 void Open9xSim::makeSnapshot(const FXDrawable* drawable)
192 // Construct and create an FXImage object
193 FXPNGImage snapshot(getApp(), NULL, 0, drawable->getWidth(), drawable->getHeight());
194 snapshot.create();
196 // Create a window device context and lock it onto the image
197 FXDCWindow dc(&snapshot);
199 // Draw from the widget to this
200 dc.drawArea(drawable, 0, 0, drawable->getWidth(), drawable->getHeight(), 0, 0);
202 // Release lock
203 dc.end();
205 // Grab pixels from server side back to client side
206 snapshot.restore();
208 // Save recovered pixels to a file
209 FXFileStream stream;
210 char buf[100];
212 do {
213 stream.close();
214 sprintf(buf,"snapshot_%02d.png", ++g_snapshot_idx);
215 } while (stream.open(buf, FXStreamLoad));
217 if (stream.open(buf, FXStreamSave)) {
218 snapshot.savePixels(stream);
219 stream.close();
220 printf("Snapshot written: %s\n", buf);
222 else {
223 printf("Cannot create snapshot %s\n", buf);
227 void Open9xSim::doEvents()
229 getApp()->runOneEvent(false);
232 long Open9xSim::onKeypress(FXObject*,FXSelector,void*v)
234 FXEvent *evt=(FXEvent*)v;
235 // printf("keypress %x\n", evt->code);
236 if (evt->code=='s') {
237 makeSnapshot(bmf);
239 return 0;
242 void Open9xSim::updateKeysAndSwitches(bool start)
244 static int keys1[] = {
245 #if defined(PCBHORUS)
246 KEY_Page_Up, KEY_PGUP,
247 KEY_Page_Down, KEY_PGDN,
248 KEY_Return, KEY_ENTER,
249 KEY_Up, KEY_UP,
250 KEY_Down, KEY_DOWN,
251 KEY_Right, KEY_RIGHT,
252 KEY_Left, KEY_LEFT,
253 #elif defined(PCBTARANIS)
254 KEY_Page_Up, KEY_MENU,
255 KEY_Page_Down, KEY_PAGE,
256 KEY_Return, KEY_ENTER,
257 KEY_BackSpace, KEY_EXIT,
258 KEY_Up, KEY_PLUS,
259 KEY_Down, KEY_MINUS,
260 #else
261 KEY_Return, KEY_MENU,
262 KEY_BackSpace, KEY_EXIT,
263 KEY_Right, KEY_RIGHT,
264 KEY_Left, KEY_LEFT,
265 KEY_Up, KEY_UP,
266 KEY_Down, KEY_DOWN,
267 #if defined(ROTARY_ENCODER_NAVIGATION)
268 KEY_F, BTN_REa,
269 #endif
270 #endif
273 for (unsigned int i=0; i<DIM(keys1); i+=2) {
274 simuSetKey(keys1[i+1], start ? false : getApp()->getKeyState(keys1[i]));
277 #ifdef __APPLE__
278 // gruvin: Can't use Function keys on the Mac -- too many other app conflicts.
279 // The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen
280 // order of trim sliders
281 static FXuint trimKeys[] = { KEY_E, KEY_R, KEY_U, KEY_I, KEY_R, KEY_E, KEY_Y, KEY_T, KEY_Q, KEY_W };
282 #else
283 static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12 };
284 #endif
286 for (unsigned i=0; i<DIM(trimKeys); i++) {
287 simuSetTrim(i, getApp()->getKeyState(trimKeys[i]));
290 #define SWITCH_KEY(key, swtch, states) \
291 static bool state##key = 0; \
292 static int8_t state_##swtch = 2; \
293 static int8_t inc_##swtch = 1; \
294 if (getApp()->getKeyState(KEY_##key)) { \
295 if (!state##key) { \
296 state_##swtch = (state_##swtch+inc_##swtch); \
297 if (state_##swtch == 1+states) inc_##swtch = -1; \
298 else if (state_##swtch == 2) inc_##swtch = 1; \
299 state##key = true; \
302 else { \
303 state##key = false; \
305 simuSetSwitch(swtch, state_##swtch-states);
307 #if defined(PCBX9E)
308 SWITCH_KEY(A, 0, 3);
309 SWITCH_KEY(B, 1, 3);
310 SWITCH_KEY(C, 2, 3);
311 SWITCH_KEY(D, 3, 3);
312 SWITCH_KEY(E, 4, 3);
313 SWITCH_KEY(F, 5, 3);
314 SWITCH_KEY(G, 6, 3);
315 SWITCH_KEY(H, 7, 3);
316 SWITCH_KEY(I, 8, 3);
317 SWITCH_KEY(J, 9, 3);
318 SWITCH_KEY(K, 10, 3);
319 SWITCH_KEY(L, 11, 3);
320 SWITCH_KEY(M, 12, 3);
321 SWITCH_KEY(N, 13, 3);
322 SWITCH_KEY(O, 14, 3);
323 SWITCH_KEY(P, 15, 3);
324 SWITCH_KEY(Q, 16, 3);
325 SWITCH_KEY(R, 17, 3);
326 #elif defined(PCBTARANIS) || defined(PCBHORUS)
327 SWITCH_KEY(A, 0, 3);
328 SWITCH_KEY(B, 1, 3);
329 SWITCH_KEY(C, 2, 3);
330 SWITCH_KEY(D, 3, 3);
331 SWITCH_KEY(E, 4, 3);
332 SWITCH_KEY(F, 5, 2);
333 SWITCH_KEY(G, 6, 3);
334 SWITCH_KEY(H, 7, 2);
335 #else
336 SWITCH_KEY(1, 0, 2);
337 SWITCH_KEY(2, 1, 2);
338 SWITCH_KEY(3, 2, 2);
339 SWITCH_KEY(4, 3, 3);
340 SWITCH_KEY(5, 4, 2);
341 SWITCH_KEY(6, 5, 2);
342 SWITCH_KEY(7, 6, 2);
343 #endif
346 long Open9xSim::onTimeout(FXObject*, FXSelector, void*)
348 if (hasFocus()) {
349 #if defined(COPROCESSOR)
350 Coproc_temp = 23;
351 Coproc_maxtemp = 28;
352 #endif
354 #if defined(PCBSKY9X)
355 temperature = 31;
356 maxTemperature = 42;
357 #endif
359 updateKeysAndSwitches();
361 #if defined(ROTARY_ENCODER_NAVIGATION)
362 static bool rotencAction = false;
363 if (getApp()->getKeyState(KEY_X)) {
364 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE += ROTARY_ENCODER_GRANULARITY;
365 rotencAction = true;
367 else if (getApp()->getKeyState(KEY_W)) {
368 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE -= ROTARY_ENCODER_GRANULARITY;
369 rotencAction = true;
371 else {
372 rotencAction = false;
374 #endif
376 #if defined(PCBX9E)
377 SWITCH_KEY(A, 0, 3);
378 SWITCH_KEY(B, 1, 3);
379 SWITCH_KEY(C, 2, 3);
380 SWITCH_KEY(D, 3, 3);
381 SWITCH_KEY(E, 4, 3);
382 SWITCH_KEY(F, 5, 2);
383 SWITCH_KEY(G, 6, 3);
384 SWITCH_KEY(H, 7, 2);
385 SWITCH_KEY(I, 8, 3);
386 SWITCH_KEY(J, 9, 3);
387 SWITCH_KEY(K, 10, 3);
388 SWITCH_KEY(L, 11, 3);
389 SWITCH_KEY(M, 12, 3);
390 SWITCH_KEY(N, 13, 3);
391 SWITCH_KEY(O, 14, 3);
392 SWITCH_KEY(P, 15, 3);
393 SWITCH_KEY(Q, 16, 3);
394 SWITCH_KEY(R, 17, 3);
395 #elif defined(PCBTARANIS) || defined(PCBHORUS)
396 SWITCH_KEY(A, 0, 3);
397 SWITCH_KEY(B, 1, 3);
398 SWITCH_KEY(C, 2, 3);
399 SWITCH_KEY(D, 3, 3);
400 #if defined(PCBX7)
401 SWITCH_KEY(F, 4, 2);
402 SWITCH_KEY(H, 5, 2);
403 #else
404 SWITCH_KEY(E, 4, 3);
405 SWITCH_KEY(F, 5, 2);
406 SWITCH_KEY(G, 6, 3);
407 SWITCH_KEY(H, 7, 2);
408 #endif
409 #else
410 SWITCH_KEY(1, 0, 2);
411 SWITCH_KEY(2, 1, 2);
412 SWITCH_KEY(3, 2, 2);
413 SWITCH_KEY(4, 3, 3);
414 SWITCH_KEY(5, 4, 2);
415 SWITCH_KEY(6, 5, 2);
416 SWITCH_KEY(7, 6, 2);
417 #endif
420 per10ms();
421 static int timeToRefresh;
422 if (++timeToRefresh >= 5) {
423 timeToRefresh = 0;
424 refreshDisplay();
426 getApp()->addTimeout(this, 2, 10);
427 return 0;
430 #if LCD_W >= 212
431 #define BL_COLOR FXRGB(47, 123, 227)
432 #else
433 #define BL_COLOR FXRGB(150, 200, 152)
434 #endif
436 void Open9xSim::setPixel(int x, int y, FXColor color)
438 #if LCD_ZOOM > 1
439 for (int i=0; i<LCD_ZOOM; ++i) {
440 for (int j=0; j<LCD_ZOOM; ++j) {
441 bmp->setPixel(LCD_ZOOM*x+i, LCD_ZOOM*y+j, color);
444 #else
445 bmp->setPixel(x, y, color);
446 #endif
449 void Open9xSim::refreshDisplay()
451 if (simuLcdRefresh) {
452 simuLcdRefresh = false;
453 FXColor offColor = isBacklightEnabled() ? BL_COLOR : FXRGB(200, 200, 200);
454 #if LCD_W == 128
455 FXColor onColor = FXRGB(0, 0, 0);
456 #endif
457 for (int x=0; x<LCD_W; x++) {
458 for (int y=0; y<LCD_H; y++) {
459 #if defined(PCBHORUS)
460 display_t z = simuLcdBuf[y * LCD_W + x];
461 if (1) {
462 if (z == 0) {
463 setPixel(x, y, FXRGB(0,0,0));
465 else if (z == 0xFFFF) {
466 setPixel(x, y, FXRGB(255,255,255));
468 else {
469 FXColor color = FXRGB(255*((z&0xF800)>>11)/0x1f, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x01F);
470 setPixel(x, y, color);
473 #elif LCD_W >= 212
474 display_t * p = &simuLcdBuf[y / 2 * LCD_W + x];
475 uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F);
476 if (z) {
477 FXColor color;
478 if (isBacklightEnabled())
479 color = FXRGB(47-(z*47)/15, 123-(z*123)/15, 227-(z*227)/15);
480 else
481 color = FXRGB(200-(z*200)/15, 200-(z*200)/15, 200-(z*200)/15);
482 setPixel(x, y, color);
484 #else
485 if (simuLcdBuf[x+(y/8)*LCD_W] & (1<<(y%8))) {
486 setPixel(x, y, onColor);
488 #endif
489 else {
490 setPixel(x, y, offColor);
495 bmp->render();
496 bmf->setImage(bmp);
500 Open9xSim *th9xSim;
501 void doFxEvents()
503 //puts("doFxEvents");
504 th9xSim->getApp()->runOneEvent(false);
505 th9xSim->refreshDisplay();
508 int main(int argc,char **argv)
510 // Each FOX GUI program needs one, and only one, application object.
511 // The application objects coordinates some common stuff shared between
512 // all the widgets; for example, it dispatches events, keeps track of
513 // all the windows, and so on.
514 // We pass the "name" of the application, and its "vendor", the name
515 // and vendor are used to search the registry database (which stores
516 // persistent information e.g. fonts and colors).
517 FXApp application("OpenTX Simu", "OpenTX");
519 // Here we initialize the application. We pass the command line arguments
520 // because FOX may sometimes need to filter out some of the arguments.
521 // This opens up the display as well, and reads the registry database
522 // so that persistent settings are now available.
523 application.init(argc,argv);
525 // This creates the main window. We pass in the title to be displayed
526 // above the window, and possibly some icons for when its iconified.
527 // The decorations determine stuff like the borders, close buttons,
528 // drag handles, and so on the Window Manager is supposed to give this
529 // window.
530 //FXMainWindow *main=new FXMainWindow(&application,"Hello",NULL,NULL,DECOR_ALL);
531 th9xSim = new Open9xSim(&application);
532 application.create();
534 // Pretty self-explanatory:- this shows the window, and places it in the
535 // middle of the screen.
536 #ifndef __APPLE__
537 th9xSim->show(PLACEMENT_SCREEN);
538 #else
539 th9xSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
540 #endif
542 #if defined(TELEMETRY_FRSKY) && !defined(TELEMETRY_FRSKY_SPORT)
543 telemetryStreaming = 1;
544 #endif
546 printf("Model size = %d\n", (int)sizeof(g_model));
548 simuInit();
550 #if defined(EEPROM)
551 StartEepromThread(argc >= 2 ? argv[1] : "eeprom.bin");
552 #endif
553 StartAudioThread();
554 StartSimu(false, argc >= 3 ? argv[2] : 0, argc >= 4 ? argv[3] : 0);
556 return application.run();
559 uint16_t anaIn(uint8_t chan)
561 if (chan<NUM_STICKS)
562 return th9xSim->sliders[chan]->getValue();
563 else if (chan<NUM_STICKS+NUM_POTS+NUM_SLIDERS)
564 return th9xSim->knobs[chan-NUM_STICKS]->getValue();
565 #if defined(PCBHORUS)
566 else if (chan == TX_VOLTAGE)
567 return 1737; //~10.6V
568 #elif defined(PCBX9E)
569 else if (chan == TX_VOLTAGE)
570 return 1420; //~10.6V
571 #elif defined(PCBTARANIS)
572 else if (chan == TX_VOLTAGE)
573 return 1000; //~7.4V
574 #elif defined(PCBSKY9X)
575 else if (chan == TX_VOLTAGE)
576 return 5.1*1500/11.3;
577 else if (chan == TX_CURRENT)
578 return 100;
579 #elif defined(PCBGRUVIN9X)
580 else if (chan == TX_VOLTAGE)
581 return 150;
582 #else
583 else if (chan == TX_VOLTAGE)
584 return 1500;
585 #endif
586 else
587 return 0;
590 uint16_t getAnalogValue(uint8_t index)
592 return anaIn(index);
595 void createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
597 th9xSim->createBitmap(index, data, x, y, w, h);