altos: Bring up basic TeleTerra v0.2 UI
[fw/altos] / src / product / ao_terraui.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include <math.h>
20
21 static __xdata struct ao_telemetry_sensor               ao_tel_sensor;
22 static __xdata struct ao_telemetry_location             ao_tel_location;
23 static __xdata struct ao_telemetry_configuration        ao_tel_config;
24 static __xdata int16_t                                  ao_tel_max_speed;
25 static __xdata int16_t                                  ao_tel_max_height;
26 static int8_t ao_tel_rssi;
27
28 static __xdata char ao_lcd_line[17];
29 static __xdata char ao_state_name[] = "SIPBFCDMLI";
30
31 static void
32 ao_terraui_line(uint8_t addr)
33 {
34         ao_lcd_goto(addr);
35         ao_lcd_putstring(ao_lcd_line);
36 }
37
38 #define ao_terraui_state()      (ao_state_name[ao_tel_sensor.state])
39
40 static char
41 ao_terraui_igniter(int16_t sense)
42 {
43         if (sense < AO_IGNITER_OPEN)
44                 return '-';
45         if (sense > AO_IGNITER_CLOSED)
46                 return '+';
47         return '?';
48 }
49
50 static char
51 ao_terraui_battery(void)
52 {
53         if (ao_tel_sensor.v_batt > 25558)
54                 return '+';
55         return '-';
56 }
57
58 static char
59 ao_terraui_gps(void)
60 {
61         if (ao_tel_location.flags & (1 << 4)) {
62                 if ((ao_tel_location.flags & 0xf) >= 4)
63                         return '+';
64         }
65         return '-';
66 }
67
68 static char
69 ao_terraui_local_gps(void)
70 {
71         if (ao_gps_data.flags & (1 << 4)) {
72                 if ((ao_gps_data.flags & 0xf) >= 4)
73                         return '+';
74         }
75         return '-';
76 }
77
78 static char
79 ao_terraui_logging(void)
80 {
81         if (ao_tel_config.flight != 0)
82                 return '+';
83         return '-';
84 }
85
86 static __code char ao_progress[4] = { '\011', '\012', '\014', '\013' };
87
88 static uint8_t  ao_telem_progress;
89 static uint8_t  ao_gps_progress;
90
91 static void
92 ao_terraui_info(void)
93 {
94         sprintf(ao_lcd_line, "S %4d %7.7s %c",
95                 ao_tel_sensor.serial,
96                 ao_tel_config.callsign,
97                 ao_terraui_state());
98         ao_terraui_line(AO_LCD_ADDR(0,0));
99         sprintf(ao_lcd_line, "F %4d RSSI%4d%c",
100                 ao_tel_config.flight,
101                 ao_tel_rssi,
102                 ao_progress[ao_telem_progress]);
103         ao_terraui_line(AO_LCD_ADDR(1,0));
104 }
105
106 static void
107 ao_terraui_pad(void)
108 {
109         sprintf(ao_lcd_line, "B%c A%c M%c L%c G%c %c",
110                 ao_terraui_battery(),
111                 ao_terraui_igniter(ao_tel_sensor.sense_d),
112                 ao_terraui_igniter(ao_tel_sensor.sense_m),
113                 ao_terraui_logging(),
114                 ao_terraui_gps(),
115                 ao_terraui_state());
116         ao_terraui_line(AO_LCD_ADDR(0,0));
117         sprintf(ao_lcd_line, "SAT %2d RSSI%4d%c",
118                 ao_tel_location.flags & 0xf,
119                 ao_tel_rssi,
120                 ao_progress[ao_telem_progress]);
121         ao_terraui_line(AO_LCD_ADDR(1,0));
122 }
123
124 static void
125 ao_terraui_ascent(void)
126 {
127         sprintf(ao_lcd_line, "S %5d S\011%5d%c",
128                 ao_tel_sensor.speed >> 4,
129                 ao_tel_max_speed >> 4,
130                 ao_terraui_state());
131         ao_terraui_line(AO_LCD_ADDR(0,0));
132         sprintf(ao_lcd_line, "H %5d H\011%5d%c",
133                 ao_tel_sensor.height >> 4,
134                 ao_tel_max_height >> 4,
135                 ao_progress[ao_telem_progress]);
136         ao_terraui_line(AO_LCD_ADDR(1,0));
137 }
138
139 static int16_t mag(int32_t d)
140 {
141         if (d < 0)
142                 d = -d;
143         if (d > 0x7fff)
144                 d = 0x7fff;
145         return d;
146 }
147
148 static uint32_t
149 dist(int32_t d)
150 {
151         int32_t m;
152
153         if (d < 0)
154                 d = -d;
155
156         m = 1000000;
157         while (d >= (2147483647 / 111198)) {
158                 d /= 10;
159                 m /= 10;
160         }
161         return (d * 111198) / m;
162 }
163
164 static __code uint8_t cos_table[] = {
165    0, /*  0 */
166    0, /*  1 */
167    0, /*  2 */
168  255, /*  3 */
169  254, /*  4 */
170  253, /*  5 */
171  252, /*  6 */
172  251, /*  7 */
173  249, /*  8 */
174  247, /*  9 */
175  245, /* 10 */
176  243, /* 11 */
177  240, /* 12 */
178  238, /* 13 */
179  235, /* 14 */
180  232, /* 15 */
181  228, /* 16 */
182  225, /* 17 */
183  221, /* 18 */
184  217, /* 19 */
185  213, /* 20 */
186  209, /* 21 */
187  205, /* 22 */
188  200, /* 23 */
189  195, /* 24 */
190  190, /* 25 */
191  185, /* 26 */
192  180, /* 27 */
193  175, /* 28 */
194  169, /* 29 */
195  163, /* 30 */
196  158, /* 31 */
197  152, /* 32 */
198  145, /* 33 */
199  139, /* 34 */
200  133, /* 35 */
201  126, /* 36 */
202  120, /* 37 */
203  113, /* 38 */
204  106, /* 39 */
205  100, /* 40 */
206   93, /* 41 */
207   86, /* 42 */
208   79, /* 43 */
209   71, /* 44 */
210   64, /* 45 */
211   57, /* 46 */
212   49, /* 47 */
213   42, /* 48 */
214   35, /* 49 */
215   27, /* 50 */
216   20, /* 51 */
217   12, /* 52 */
218    5, /* 53 */
219    1, /* 54 */
220 };
221
222 static __code uint8_t tan_table[] = {
223     0, /*  0 */
224     4, /*  1 */
225     9, /*  2 */
226    13, /*  3 */
227    18, /*  4 */
228    22, /*  5 */
229    27, /*  6 */
230    31, /*  7 */
231    36, /*  8 */
232    41, /*  9 */
233    45, /* 10 */
234    50, /* 11 */
235    54, /* 12 */
236    59, /* 13 */
237    64, /* 14 */
238    69, /* 15 */
239    73, /* 16 */
240    78, /* 17 */
241    83, /* 18 */
242    88, /* 19 */
243    93, /* 20 */
244    98, /* 21 */
245   103, /* 22 */
246   109, /* 23 */
247   114, /* 24 */
248   119, /* 25 */
249   125, /* 26 */
250   130, /* 27 */
251   136, /* 28 */
252   142, /* 29 */
253   148, /* 30 */
254   154, /* 31 */
255   160, /* 32 */
256   166, /* 33 */
257   173, /* 34 */
258   179, /* 35 */
259   186, /* 36 */
260   193, /* 37 */
261   200, /* 38 */
262   207, /* 39 */
263   215, /* 40 */
264   223, /* 41 */
265   231, /* 42 */
266   239, /* 43 */
267   247, /* 44 */
268 };
269         
270 int16_t ao_atan2(int32_t dy, int32_t dx) __reentrant
271 {
272         int8_t  m = 1;
273         int16_t a = 0;
274         uint8_t r;
275         int8_t  t;
276
277         if (dx == 0) {
278                 if (dy > 0)
279                         return 90;
280                 if (dy < 0)
281                         return -90;
282                 return 0;
283         }
284
285         if (dx < 0) {
286                 a = 180;
287                 m = -m;
288                 dx = -dx;
289         }
290
291         if (dy < 0) {
292                 m = -m;
293                 a = -a;
294                 dy = -dy;
295         }
296
297         if (dy > dx) {
298                 int     t;
299
300                 t = dy; dy = dx; dx = t;
301                 a = a + m * 90;
302                 m = -m;
303         }
304
305         r = ((dy << 8) + (dx >> 1)) / dx;
306         for (t = 0; t < 44; t++)
307                 if (tan_table[t] >= r)
308                         break;
309         return t * m + a;
310 }
311
312 static __pdata uint32_t lon_dist, lat_dist;
313 static __pdata uint32_t ground_dist, range;
314 static __pdata int16_t bearing, elevation;
315
316 static void
317 ao_terraui_lat_dist(void)
318 {
319         lat_dist = dist (ao_tel_location.latitude - ao_gps_data.latitude);
320 }
321
322 static void
323 ao_terraui_lon_dist(void)
324 {
325         uint8_t c = cos_table[ao_gps_data.latitude >> 24];
326         lon_dist = dist(ao_tel_location.longitude - ao_gps_data.longitude);
327         if (c)
328                 lon_dist = ((uint32_t) lon_dist * c) >> 8;
329 }
330
331 static void
332 ao_terraui_compute(void)
333 {
334         ao_terraui_lat_dist();
335         ao_terraui_lon_dist();
336         ground_dist = ao_sqrt (lat_dist * lat_dist + lon_dist * lon_dist);
337         range = ao_sqrt(ground_dist * ground_dist + ao_tel_sensor.height * ao_tel_sensor.height);
338         bearing = ao_atan2(lat_dist, lon_dist);
339         elevation = ao_atan2(ao_tel_sensor.height, ground_dist);
340 }
341
342 static void
343 ao_terraui_descent(void)
344 {
345         ao_terraui_compute();
346         sprintf(ao_lcd_line, "\007 %4d  \005 %3d  %c",
347                 bearing, elevation,
348                 ao_terraui_state());
349         ao_terraui_line(AO_LCD_ADDR(0,0));
350         sprintf(ao_lcd_line, "H:%5d S:%5d%c",
351                 ao_tel_sensor.height, ao_tel_sensor.speed >> 4,
352                 ao_progress[ao_telem_progress]);
353         ao_terraui_line(AO_LCD_ADDR(1,0));
354 }
355
356 static void
357 ao_terraui_coord(int32_t c, char plus, char minus, char extra) __reentrant
358 {
359         uint16_t        d;
360         uint8_t         m;
361         uint16_t        f;
362
363         if (c < 0) {
364                 plus = minus;
365                 c = -c;
366         }
367         d = c / 10000000;
368         c = c % 10000000;
369         c = c * 60;
370         m = c / 10000000;
371         c = c % 10000000;
372         f = (c + 500) / 1000;
373         sprintf(ao_lcd_line, "%c %3d\362 %2d.%04d\"%c",
374                 plus, d, m, f, extra);
375 }
376
377 static void
378 ao_terraui_remote(void)
379 {
380         ao_terraui_coord(ao_tel_location.latitude, 'N', 'S', ao_terraui_state());
381         ao_terraui_line(AO_LCD_ADDR(0,0));
382         ao_terraui_coord(ao_tel_location.longitude, 'E', 'W', ao_progress[ao_telem_progress]);
383         ao_terraui_line(AO_LCD_ADDR(1,0));
384 }
385
386 static void
387 ao_terraui_local(void) __reentrant
388 {
389         ao_terraui_coord(ao_gps_data.latitude, 'n', 's',
390                          ao_terraui_local_gps());
391         ao_terraui_line(AO_LCD_ADDR(0,0));
392         ao_terraui_coord(ao_gps_data.longitude, 'e', 'w', ao_progress[ao_gps_progress]);
393         ao_terraui_line(AO_LCD_ADDR(1,0));
394 }
395
396 static void
397 ao_terraui_config(void)
398 {
399         
400 }
401
402 enum ao_page {
403         ao_page_info,
404         ao_page_pad,
405         ao_page_ascent,
406         ao_page_descent,
407         ao_page_remote,
408         ao_page_local,
409 };
410
411 static void
412 ao_terraui(void)
413 {
414         enum ao_page    cur_page = ao_page_info;
415
416         ao_lcd_start();
417         for (;;) {
418                 char    b;
419                 switch (cur_page) {
420                 case ao_page_info:
421                         ao_terraui_info();
422                         break;
423                 case ao_page_pad:
424                         ao_terraui_pad();
425                         break;
426                 case ao_page_ascent:
427                         ao_terraui_ascent();
428                         break;
429                 case ao_page_descent:
430                         ao_terraui_descent();
431                         break;
432                 case ao_page_remote:
433                         ao_terraui_remote();
434                         break;
435                 case ao_page_local:
436                         ao_terraui_local();
437                         break;
438                 }
439
440                 ao_alarm(AO_SEC_TO_TICKS(1));
441                 b = ao_button_get();
442                 ao_clear_alarm();
443
444                 switch (b) {
445                 case 0:
446                         break;
447                 case 1:
448                         if (cur_page == ao_page_local)
449                                 cur_page = ao_page_info;
450                         else
451                                 cur_page++;
452                         ao_beep_for(AO_BEEP_HIGH, AO_MS_TO_TICKS(50));
453                         break;
454                 case 2:
455                         ao_beep_for(AO_BEEP_LOW, AO_MS_TO_TICKS(200));
456                         break;
457                 case 3:
458                         if (cur_page == ao_page_info)
459                                 cur_page = ao_page_local;
460                         else
461                                 cur_page--;
462                         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(50));
463                         break;
464                 }
465         }
466 }
467
468 __xdata static struct ao_task ao_terraui_task;
469
470 static void
471 ao_terramonitor(void)
472 {
473         uint8_t monitor;
474
475         monitor = ao_monitor_head;
476         for (monitor = ao_monitor_head;;
477              monitor = ao_monitor_ring_next(monitor))
478         {
479                 while (monitor == ao_monitor_head)
480                         ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
481                 if (ao_monitoring != sizeof (union ao_telemetry_all))
482                         continue;
483                 if (!(ao_monitor_ring[monitor].all.status & PKT_APPEND_STATUS_1_CRC_OK))
484                         continue;
485                 ao_tel_rssi = (ao_monitor_ring[monitor].all.rssi >> 1) - 74;
486                 switch (ao_monitor_ring[monitor].all.telemetry.generic.type) {
487                 case AO_TELEMETRY_SENSOR_TELEMETRUM:
488                 case AO_TELEMETRY_SENSOR_TELEMINI:
489                 case AO_TELEMETRY_SENSOR_TELENANO:
490                         ao_xmemcpy(&ao_tel_sensor, &ao_monitor_ring[monitor], sizeof (ao_tel_sensor));
491                         if (ao_tel_sensor.state < ao_flight_boost) {
492                                 ao_tel_max_speed = 0;
493                                 ao_tel_max_height = 0;
494                         } else {
495                                 if (ao_tel_sensor.speed > ao_tel_max_speed)
496                                         ao_tel_max_speed = ao_tel_sensor.speed;
497                                 if (ao_tel_sensor.height > ao_tel_max_height)
498                                         ao_tel_max_height = ao_tel_sensor.height;
499                         }
500                         ao_telem_progress = (ao_telem_progress + 1) & 0x3;
501                         break;
502                 case AO_TELEMETRY_LOCATION:
503                         ao_xmemcpy(&ao_tel_location, &ao_monitor_ring[monitor], sizeof (ao_tel_location));
504                         break;
505                 case AO_TELEMETRY_CONFIGURATION:
506                         ao_xmemcpy(&ao_tel_config, &ao_monitor_ring[monitor], sizeof (ao_tel_config));
507                 }
508         }
509 }
510
511 __xdata static struct ao_task ao_terramonitor_task;
512
513 static void
514 ao_terragps(void)
515 {
516         uint16_t        gps_tick = ao_gps_progress;
517
518         for (;;) {
519                 while (ao_gps_tick == gps_tick)
520                         ao_sleep(&ao_gps_data);
521                 gps_tick = ao_gps_tick;
522                 ao_gps_progress = (ao_gps_progress + 1) & 3;
523         }
524 }
525
526 __xdata static struct ao_task ao_terragps_task;
527
528 void
529 ao_terraui_init(void)
530 {
531         ao_add_task(&ao_terraui_task, ao_terraui, "ui");
532         ao_add_task(&ao_terramonitor_task, ao_terramonitor, "monitor");
533         ao_add_task(&ao_terragps_task, ao_terragps, "gps");
534 }