x9e with horus bt module (#5214)
[opentx.git] / radio / src / simu.cpp
blobd3ba4744e62bcfed495da67adb4ce8ba898a5b33
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(PCBFLAMENCO)
254 KEY_Page_Up, KEY_MENU,
255 KEY_Page_Down, KEY_PAGE,
256 KEY_Return, KEY_ENTER,
257 KEY_BackSpace, KEY_EXIT,
258 #elif defined(PCBTARANIS)
259 KEY_Page_Up, KEY_MENU,
260 KEY_Page_Down, KEY_PAGE,
261 KEY_Return, KEY_ENTER,
262 KEY_BackSpace, KEY_EXIT,
263 KEY_Up, KEY_PLUS,
264 KEY_Down, KEY_MINUS,
265 #else
266 KEY_Return, KEY_MENU,
267 KEY_BackSpace, KEY_EXIT,
268 KEY_Right, KEY_RIGHT,
269 KEY_Left, KEY_LEFT,
270 KEY_Up, KEY_UP,
271 KEY_Down, KEY_DOWN,
272 #if defined(ROTARY_ENCODER_NAVIGATION)
273 KEY_F, BTN_REa,
274 #endif
275 #endif
278 for (unsigned int i=0; i<DIM(keys1); i+=2) {
279 simuSetKey(keys1[i+1], start ? false : getApp()->getKeyState(keys1[i]));
282 #ifdef __APPLE__
283 // gruvin: Can't use Function keys on the Mac -- too many other app conflicts.
284 // The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen
285 // order of trim sliders
286 static FXuint trimKeys[] = { KEY_E, KEY_R, KEY_U, KEY_I, KEY_R, KEY_E, KEY_Y, KEY_T, KEY_Q, KEY_W };
287 #else
288 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 };
289 #endif
291 for (unsigned i=0; i<DIM(trimKeys); i++) {
292 simuSetTrim(i, getApp()->getKeyState(trimKeys[i]));
295 #define SWITCH_KEY(key, swtch, states) \
296 static bool state##key = 0; \
297 static int8_t state_##swtch = 2; \
298 static int8_t inc_##swtch = 1; \
299 if (getApp()->getKeyState(KEY_##key)) { \
300 if (!state##key) { \
301 state_##swtch = (state_##swtch+inc_##swtch); \
302 if (state_##swtch == 1+states) inc_##swtch = -1; \
303 else if (state_##swtch == 2) inc_##swtch = 1; \
304 state##key = true; \
307 else { \
308 state##key = false; \
310 simuSetSwitch(swtch, state_##swtch-states);
312 #if defined(PCBFLAMENCO)
313 SWITCH_KEY(A, 0, 3);
314 SWITCH_KEY(B, 1, 2);
315 // SWITCH_KEY(C, 2, 3);
316 // SWITCH_KEY(D, 3, 3);
317 SWITCH_KEY(E, 4, 2);
318 SWITCH_KEY(F, 5, 3);
319 #elif defined(PCBX9E)
320 SWITCH_KEY(A, 0, 3);
321 SWITCH_KEY(B, 1, 3);
322 SWITCH_KEY(C, 2, 3);
323 SWITCH_KEY(D, 3, 3);
324 SWITCH_KEY(E, 4, 3);
325 SWITCH_KEY(F, 5, 3);
326 SWITCH_KEY(G, 6, 3);
327 SWITCH_KEY(H, 7, 3);
328 SWITCH_KEY(I, 8, 3);
329 SWITCH_KEY(J, 9, 3);
330 SWITCH_KEY(K, 10, 3);
331 SWITCH_KEY(L, 11, 3);
332 SWITCH_KEY(M, 12, 3);
333 SWITCH_KEY(N, 13, 3);
334 SWITCH_KEY(O, 14, 3);
335 SWITCH_KEY(P, 15, 3);
336 SWITCH_KEY(Q, 16, 3);
337 SWITCH_KEY(R, 17, 3);
338 #elif defined(PCBTARANIS) || defined(PCBHORUS)
339 SWITCH_KEY(A, 0, 3);
340 SWITCH_KEY(B, 1, 3);
341 SWITCH_KEY(C, 2, 3);
342 SWITCH_KEY(D, 3, 3);
343 SWITCH_KEY(E, 4, 3);
344 SWITCH_KEY(F, 5, 2);
345 SWITCH_KEY(G, 6, 3);
346 SWITCH_KEY(H, 7, 2);
347 #else
348 SWITCH_KEY(1, 0, 2);
349 SWITCH_KEY(2, 1, 2);
350 SWITCH_KEY(3, 2, 2);
351 SWITCH_KEY(4, 3, 3);
352 SWITCH_KEY(5, 4, 2);
353 SWITCH_KEY(6, 5, 2);
354 SWITCH_KEY(7, 6, 2);
355 #endif
358 long Open9xSim::onTimeout(FXObject*, FXSelector, void*)
360 if (hasFocus()) {
361 #if defined(COPROCESSOR)
362 Coproc_temp = 23;
363 Coproc_maxtemp = 28;
364 #endif
366 #if defined(PCBSKY9X)
367 temperature = 31;
368 maxTemperature = 42;
369 #endif
371 updateKeysAndSwitches();
373 #if defined(ROTARY_ENCODER_NAVIGATION)
374 static bool rotencAction = false;
375 if (getApp()->getKeyState(KEY_X)) {
376 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE += ROTARY_ENCODER_GRANULARITY;
377 rotencAction = true;
379 else if (getApp()->getKeyState(KEY_W)) {
380 if (!rotencAction) ROTARY_ENCODER_NAVIGATION_VALUE -= ROTARY_ENCODER_GRANULARITY;
381 rotencAction = true;
383 else {
384 rotencAction = false;
386 #endif
388 #if defined(PCBX9E)
389 SWITCH_KEY(A, 0, 3);
390 SWITCH_KEY(B, 1, 3);
391 SWITCH_KEY(C, 2, 3);
392 SWITCH_KEY(D, 3, 3);
393 SWITCH_KEY(E, 4, 3);
394 SWITCH_KEY(F, 5, 2);
395 SWITCH_KEY(G, 6, 3);
396 SWITCH_KEY(H, 7, 2);
397 SWITCH_KEY(I, 8, 3);
398 SWITCH_KEY(J, 9, 3);
399 SWITCH_KEY(K, 10, 3);
400 SWITCH_KEY(L, 11, 3);
401 SWITCH_KEY(M, 12, 3);
402 SWITCH_KEY(N, 13, 3);
403 SWITCH_KEY(O, 14, 3);
404 SWITCH_KEY(P, 15, 3);
405 SWITCH_KEY(Q, 16, 3);
406 SWITCH_KEY(R, 17, 3);
407 #elif defined(PCBTARANIS) || defined(PCBHORUS)
408 SWITCH_KEY(A, 0, 3);
409 SWITCH_KEY(B, 1, 3);
410 SWITCH_KEY(C, 2, 3);
411 SWITCH_KEY(D, 3, 3);
412 #if defined(PCBX7)
413 SWITCH_KEY(F, 4, 2);
414 SWITCH_KEY(H, 5, 2);
415 #else
416 SWITCH_KEY(E, 4, 3);
417 SWITCH_KEY(F, 5, 2);
418 SWITCH_KEY(G, 6, 3);
419 SWITCH_KEY(H, 7, 2);
420 #endif
421 #else
422 SWITCH_KEY(1, 0, 2);
423 SWITCH_KEY(2, 1, 2);
424 SWITCH_KEY(3, 2, 2);
425 SWITCH_KEY(4, 3, 3);
426 SWITCH_KEY(5, 4, 2);
427 SWITCH_KEY(6, 5, 2);
428 SWITCH_KEY(7, 6, 2);
429 #endif
432 per10ms();
433 static int timeToRefresh;
434 if (++timeToRefresh >= 5) {
435 timeToRefresh = 0;
436 refreshDisplay();
438 getApp()->addTimeout(this, 2, 10);
439 return 0;
442 #if LCD_W >= 212
443 #define BL_COLOR FXRGB(47, 123, 227)
444 #else
445 #define BL_COLOR FXRGB(150, 200, 152)
446 #endif
448 void Open9xSim::setPixel(int x, int y, FXColor color)
450 #if LCD_ZOOM > 1
451 for (int i=0; i<LCD_ZOOM; ++i) {
452 for (int j=0; j<LCD_ZOOM; ++j) {
453 bmp->setPixel(LCD_ZOOM*x+i, LCD_ZOOM*y+j, color);
456 #else
457 bmp->setPixel(x, y, color);
458 #endif
461 void Open9xSim::refreshDisplay()
463 if (simuLcdRefresh) {
464 simuLcdRefresh = false;
465 FXColor offColor = isBacklightEnabled() ? BL_COLOR : FXRGB(200, 200, 200);
466 #if LCD_W == 128
467 FXColor onColor = FXRGB(0, 0, 0);
468 #endif
469 for (int x=0; x<LCD_W; x++) {
470 for (int y=0; y<LCD_H; y++) {
471 #if defined(PCBHORUS)
472 display_t z = simuLcdBuf[y * LCD_W + x];
473 if (1) {
474 if (z == 0) {
475 setPixel(x, y, FXRGB(0,0,0));
477 else if (z == 0xFFFF) {
478 setPixel(x, y, FXRGB(255,255,255));
480 else {
481 FXColor color = FXRGB(255*((z&0xF800)>>11)/0x1f, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x01F);
482 setPixel(x, y, color);
485 #elif defined(PCBFLAMENCO)
486 display_t z = simuLcdBuf[y * LCD_W + x];
487 if (1) {
488 FXColor color = FXRGB(255*((z&0xF00)>>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f);
489 setPixel(x, y, color);
491 #elif LCD_W >= 212
492 display_t * p = &simuLcdBuf[y / 2 * LCD_W + x];
493 uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F);
494 if (z) {
495 FXColor color;
496 if (isBacklightEnabled())
497 color = FXRGB(47-(z*47)/15, 123-(z*123)/15, 227-(z*227)/15);
498 else
499 color = FXRGB(200-(z*200)/15, 200-(z*200)/15, 200-(z*200)/15);
500 setPixel(x, y, color);
502 #else
503 if (simuLcdBuf[x+(y/8)*LCD_W] & (1<<(y%8))) {
504 setPixel(x, y, onColor);
506 #endif
507 else {
508 setPixel(x, y, offColor);
513 bmp->render();
514 bmf->setImage(bmp);
518 Open9xSim *th9xSim;
519 void doFxEvents()
521 //puts("doFxEvents");
522 th9xSim->getApp()->runOneEvent(false);
523 th9xSim->refreshDisplay();
526 int main(int argc,char **argv)
528 // Each FOX GUI program needs one, and only one, application object.
529 // The application objects coordinates some common stuff shared between
530 // all the widgets; for example, it dispatches events, keeps track of
531 // all the windows, and so on.
532 // We pass the "name" of the application, and its "vendor", the name
533 // and vendor are used to search the registry database (which stores
534 // persistent information e.g. fonts and colors).
535 FXApp application("OpenTX Simu", "OpenTX");
537 // Here we initialize the application. We pass the command line arguments
538 // because FOX may sometimes need to filter out some of the arguments.
539 // This opens up the display as well, and reads the registry database
540 // so that persistent settings are now available.
541 application.init(argc,argv);
543 // This creates the main window. We pass in the title to be displayed
544 // above the window, and possibly some icons for when its iconified.
545 // The decorations determine stuff like the borders, close buttons,
546 // drag handles, and so on the Window Manager is supposed to give this
547 // window.
548 //FXMainWindow *main=new FXMainWindow(&application,"Hello",NULL,NULL,DECOR_ALL);
549 th9xSim = new Open9xSim(&application);
550 application.create();
552 // Pretty self-explanatory:- this shows the window, and places it in the
553 // middle of the screen.
554 #ifndef __APPLE__
555 th9xSim->show(PLACEMENT_SCREEN);
556 #else
557 th9xSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
558 #endif
560 #if defined(TELEMETRY_FRSKY) && !defined(TELEMETRY_FRSKY_SPORT)
561 telemetryStreaming = 1;
562 #endif
564 printf("Model size = %d\n", (int)sizeof(g_model));
566 simuInit();
568 #if defined(EEPROM)
569 StartEepromThread(argc >= 2 ? argv[1] : "eeprom.bin");
570 #endif
571 StartAudioThread();
572 StartSimu(false, argc >= 3 ? argv[2] : 0, argc >= 4 ? argv[3] : 0);
574 return application.run();
577 uint16_t anaIn(uint8_t chan)
579 if (chan<NUM_STICKS)
580 return th9xSim->sliders[chan]->getValue();
581 else if (chan<NUM_STICKS+NUM_POTS+NUM_SLIDERS)
582 return th9xSim->knobs[chan-NUM_STICKS]->getValue();
583 #if defined(PCBHORUS)
584 else if (chan == TX_VOLTAGE)
585 return 1737; //~10.6V
586 #elif defined(PCBX9E)
587 else if (chan == TX_VOLTAGE)
588 return 1420; //~10.6V
589 #elif defined(PCBTARANIS) || defined(PCBFLAMENCO)
590 else if (chan == TX_VOLTAGE)
591 return 1000; //~7.4V
592 #elif defined(PCBSKY9X)
593 else if (chan == TX_VOLTAGE)
594 return 5.1*1500/11.3;
595 else if (chan == TX_CURRENT)
596 return 100;
597 #elif defined(PCBGRUVIN9X)
598 else if (chan == TX_VOLTAGE)
599 return 150;
600 #else
601 else if (chan == TX_VOLTAGE)
602 return 1500;
603 #endif
604 else
605 return 0;
608 uint16_t getAnalogValue(uint8_t index)
610 return anaIn(index);
613 void createBitmap(int index, uint16_t *data, int x, int y, int w, int h)
615 th9xSim->createBitmap(index, data, x, y, w, h);