altos: Fix up telemetry delay computations
[fw/altos] / src / kernel / ao_config.c
1 /*
2  * Copyright © 2009 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 "ao_log.h"
20 #include <ao_config.h>
21 #if HAS_FLIGHT
22 #include <ao_sample.h>
23 #include <ao_data.h>
24 #endif
25 #if HAS_BEEP
26 #include <ao_beep.h>
27 #endif
28 #if HAS_TRACKER
29 #include <ao_tracker.h>
30 #endif
31
32 __xdata struct ao_config ao_config;
33 __pdata uint8_t ao_config_loaded;
34 __pdata uint8_t ao_config_dirty;
35 __xdata uint8_t ao_config_mutex;
36
37 #ifndef AO_CONFIG_DEFAULT_APRS_INTERVAL
38 #define AO_CONFIG_DEFAULT_APRS_INTERVAL 0
39 #endif
40 #define AO_CONFIG_DEFAULT_MAIN_DEPLOY   250
41 #define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
42 #define AO_CONFIG_DEFAULT_CALLSIGN      "N0CALL"
43 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G  16000
44 #define AO_CONFIG_DEFAULT_APOGEE_DELAY  0
45 #define AO_CONFIG_DEFAULT_IGNITE_MODE   AO_IGNITE_MODE_DUAL
46 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION       AO_PAD_ORIENTATION_ANTENNA_UP
47 #define AO_CONFIG_DEFAULT_PYRO_TIME     AO_MS_TO_TICKS(50)
48 #if HAS_EEPROM
49 #ifndef USE_INTERNAL_FLASH
50 #error Please define USE_INTERNAL_FLASH
51 #endif
52 #endif
53
54 #ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
55 # if FLIGHT_LOG_APPEND
56 #  define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX      ao_storage_log_max
57 # else
58 #  if USE_INTERNAL_FLASH
59 #   define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX     ao_storage_config
60 #  else
61 #   define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX     ((uint32_t) 192 * (uint32_t) 1024)
62 #  endif
63 # endif
64 #endif
65
66 #ifndef AO_CONFIG_DEFAULT_RADIO_POWER
67 #define AO_CONFIG_DEFAULT_RADIO_POWER           0x60
68 #endif
69 #define AO_CONFIG_DEFAULT_RADIO_AMP             0
70 #define AO_CONFIG_DEFAULT_APRS_SSID             (ao_serial_number % 10)
71 #define AO_CONFIG_DEFAULT_RADIO_RATE            AO_RADIO_RATE_38400
72
73 #if HAS_EEPROM
74 static void
75 _ao_config_put(void)
76 {
77         ao_config_setup();
78         ao_config_erase();
79         ao_config_write(0, &ao_config, sizeof (ao_config));
80 #if HAS_FLIGHT
81         ao_log_write_erase(0);
82 #endif
83         ao_config_flush();
84 }
85
86 void
87 ao_config_put(void)
88 {
89         ao_mutex_get(&ao_config_mutex);
90         _ao_config_put();
91         ao_mutex_put(&ao_config_mutex);
92 }
93 #endif
94
95 #if HAS_RADIO
96
97 #if HAS_RADIO_FORWARD
98 __xdata uint32_t        ao_send_radio_setting;
99 #endif
100
101 void
102 ao_config_set_radio(void)
103 {
104         ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
105 #if HAS_RADIO_FORWARD
106         ao_send_radio_setting = ao_freq_to_set(ao_config.send_frequency, ao_config.radio_cal);
107 #endif
108 }
109 #endif /* HAS_RADIO */
110
111 static void
112 _ao_config_get(void)
113 {
114         uint8_t minor;
115
116         if (ao_config_loaded)
117                 return;
118 #if HAS_EEPROM
119         /* Yes, I know ao_storage_read calls ao_storage_setup,
120          * but ao_storage_setup *also* sets ao_storage_config, which we
121          * need before calling ao_storage_read here
122          */
123         ao_config_setup();
124         ao_config_read(0, &ao_config, sizeof (ao_config));
125 #endif
126         if (ao_config.major != AO_CONFIG_MAJOR) {
127                 ao_config.major = AO_CONFIG_MAJOR;
128                 ao_config.minor = 0;
129
130                 /* Version 0 stuff */
131                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
132                 ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
133                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
134                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
135                 ao_config._legacy_radio_channel = 0;
136         }
137         minor = ao_config.minor;
138         if (minor != AO_CONFIG_MINOR) {
139                 /* Fixups for minor version 1 */
140                 if (minor < 1)
141                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
142                 /* Fixups for minor version 2 */
143                 if (minor < 2) {
144                         ao_config.accel_plus_g = 0;
145                         ao_config.accel_minus_g = 0;
146                 }
147                 /* Fixups for minor version 3 */
148 #if HAS_RADIO
149                 if (minor < 3)
150                         ao_config.radio_cal = ao_radio_cal;
151 #endif
152                 /* Fixups for minor version 4 */
153 #if HAS_LOG
154                 if (minor < 4)
155                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
156 #endif
157                 /* Fixupes for minor version 5 */
158                 if (minor < 5)
159                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
160                 if (minor < 6)
161                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
162                 if (minor < 8)
163                         ao_config.radio_enable = AO_RADIO_ENABLE_CORE;
164                 if (minor < 9)
165                         ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
166                 if (minor < 10)
167                         ao_config.frequency = 434550 + ao_config._legacy_radio_channel * 100;
168                 if (minor < 11)
169                         ao_config.apogee_lockout = 0;
170 #if AO_PYRO_NUM
171                 if (minor < 12)
172                         memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro));
173 #endif
174                 if (minor < 13)
175                         ao_config.aprs_interval = AO_CONFIG_DEFAULT_APRS_INTERVAL;
176 #if HAS_RADIO_POWER
177                 if (minor < 14)
178                         ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
179                 #endif
180 #if HAS_RADIO_AMP
181                 if (minor  < 14)
182                         ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
183 #endif
184 #if HAS_GYRO
185                 if (minor < 15) {
186                         ao_config.accel_zero_along = 0;
187                         ao_config.accel_zero_across = 0;
188                         ao_config.accel_zero_through = 0;
189
190                         /* Reset the main accel offsets to force
191                          * re-calibration
192                          */
193                         ao_config.accel_plus_g = 0;
194                         ao_config.accel_minus_g = 0;
195                 }
196 #endif
197 #if HAS_BEEP_CONFIG
198                 if (minor < 16)
199                         ao_config.mid_beep = AO_BEEP_MID_DEFAULT;
200 #endif
201 #if HAS_TRACKER
202                 if (minor < 17) {
203                         ao_config.tracker_motion = AO_TRACKER_MOTION_DEFAULT;
204                         ao_config.tracker_interval = AO_TRACKER_INTERVAL_DEFAULT;
205                 }
206 #endif
207 #if AO_PYRO_NUM
208                 if (minor < 18)
209                         ao_config.pyro_time = AO_CONFIG_DEFAULT_PYRO_TIME;
210 #endif
211 #if HAS_APRS
212                 if (minor < 19)
213                         ao_config.aprs_ssid = AO_CONFIG_DEFAULT_APRS_SSID;
214 #endif
215 #if HAS_RADIO_RATE
216                 if (minor < 20)
217                         ao_config.radio_rate = AO_CONFIG_DEFAULT_RADIO_RATE;
218 #endif
219 #if HAS_RADIO_FORWARD
220                 if (minor < 21)
221                         ao_config.send_frequency = 434550;
222 #endif
223                 ao_config.minor = AO_CONFIG_MINOR;
224                 ao_config_dirty = 1;
225         }
226 #if HAS_RADIO
227 #if HAS_FORCE_FREQ
228         if (ao_force_freq) {
229                 ao_config.frequency = 434550;
230                 ao_config.radio_cal = ao_radio_cal;
231                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
232                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
233         }
234 #endif
235         ao_config_set_radio();
236 #endif
237         ao_config_loaded = 1;
238 }
239
240 void
241 _ao_config_edit_start(void)
242 {
243         ao_mutex_get(&ao_config_mutex);
244         _ao_config_get();
245 }
246
247 void
248 _ao_config_edit_finish(void)
249 {
250         ao_config_dirty = 1;
251         ao_mutex_put(&ao_config_mutex);
252 }
253
254 void
255 ao_config_get(void)
256 {
257         _ao_config_edit_start();
258         ao_mutex_put(&ao_config_mutex);
259 }
260
261 void
262 ao_config_callsign_show(void)
263 {
264         printf ("Callsign: \"%s\"\n", ao_config.callsign);
265 }
266
267 void
268 ao_config_callsign_set(void) __reentrant
269 {
270         uint8_t c;
271         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
272
273         ao_xmemset(callsign, '\0', sizeof callsign);
274         ao_cmd_white();
275         c = 0;
276         while (ao_cmd_lex_c != '\n') {
277                 if (c < AO_MAX_CALLSIGN)
278                         callsign[c++] = ao_cmd_lex_c;
279                 else
280                         ao_cmd_status = ao_cmd_lex_error;
281                 ao_cmd_lex();
282         }
283         if (ao_cmd_status != ao_cmd_success)
284                 return;
285         _ao_config_edit_start();
286         ao_xmemcpy(&ao_config.callsign, &callsign,
287                AO_MAX_CALLSIGN + 1);
288         _ao_config_edit_finish();
289 }
290
291 #if HAS_RADIO
292
293 void
294 ao_config_frequency_show(void) __reentrant
295 {
296         printf("Frequency: %ld\n",
297                ao_config.frequency);
298 }
299
300 void
301 ao_config_frequency_set(void) __reentrant
302 {
303         ao_cmd_decimal();
304         if (ao_cmd_status != ao_cmd_success)
305                 return;
306         _ao_config_edit_start();
307         ao_config.frequency = ao_cmd_lex_u32;
308         ao_config_set_radio();
309         _ao_config_edit_finish();
310 #if HAS_RADIO_RECV
311         ao_radio_recv_abort();
312 #endif
313 }
314
315 #endif
316
317 #if HAS_RADIO_FORWARD
318 void
319 ao_config_send_frequency_show(void) __reentrant
320 {
321         printf("Send frequency: %ld\n",
322                ao_config.send_frequency);
323 }
324
325 void
326 ao_config_send_frequency_set(void) __reentrant
327 {
328         ao_cmd_decimal();
329         if (ao_cmd_status != ao_cmd_success)
330                 return;
331         _ao_config_edit_start();
332         ao_config.send_frequency = ao_cmd_lex_u32;
333         ao_config_set_radio();
334         _ao_config_edit_finish();
335 #if HAS_RADIO_RECV
336         ao_radio_recv_abort();
337 #endif
338 }
339
340 #endif
341
342 #if HAS_FLIGHT
343
344 void
345 ao_config_main_deploy_show(void) __reentrant
346 {
347         printf("Main deploy: %d meters\n",
348                ao_config.main_deploy);
349 }
350
351 void
352 ao_config_main_deploy_set(void) __reentrant
353 {
354         ao_cmd_decimal();
355         if (ao_cmd_status != ao_cmd_success)
356                 return;
357         _ao_config_edit_start();
358         ao_config.main_deploy = ao_cmd_lex_i;
359         _ao_config_edit_finish();
360 }
361
362 #if HAS_ACCEL
363 void
364 ao_config_accel_calibrate_show(void) __reentrant
365 {
366         printf("Accel cal +1g: %d -1g: %d\n",
367                ao_config.accel_plus_g, ao_config.accel_minus_g);
368 #if HAS_GYRO
369         printf ("IMU cal along %d across %d through %d\n",
370                 ao_config.accel_zero_along,
371                 ao_config.accel_zero_across,
372                 ao_config.accel_zero_through);
373 #endif
374 }
375
376 #define ACCEL_CALIBRATE_SAMPLES 1024
377 #define ACCEL_CALIBRATE_SHIFT   10
378
379 #if HAS_GYRO
380 static int16_t accel_cal_along;
381 static int16_t accel_cal_across;
382 static int16_t accel_cal_through;
383 #endif
384
385 static int16_t
386 ao_config_accel_calibrate_auto(char *orientation) __reentrant
387 {
388         uint16_t        i;
389         int32_t         accel_total;
390         uint8_t         cal_data_ring;
391 #if HAS_GYRO
392         int32_t         accel_along_total = 0;
393         int32_t         accel_across_total = 0;
394         int32_t         accel_through_total = 0;
395 #endif
396
397         printf("Orient antenna %s and press a key...", orientation);
398         flush();
399         (void) getchar();
400         puts("\r\n"); flush();
401         puts("Calibrating..."); flush();
402         i = ACCEL_CALIBRATE_SAMPLES;
403         accel_total = 0;
404         cal_data_ring = ao_sample_data;
405         while (i) {
406                 ao_sleep(DATA_TO_XDATA(&ao_sample_data));
407                 while (i && cal_data_ring != ao_sample_data) {
408                         accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
409 #if HAS_GYRO
410                         accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
411                         accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
412                         accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
413 #endif
414                         cal_data_ring = ao_data_ring_next(cal_data_ring);
415                         i--;
416                 }
417         }
418 #if HAS_GYRO
419         accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
420         accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
421         accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
422 #endif
423         return accel_total >> ACCEL_CALIBRATE_SHIFT;
424 }
425
426 void
427 ao_config_accel_calibrate_set(void) __reentrant
428 {
429         int16_t up, down;
430 #if HAS_GYRO
431         int16_t accel_along_up = 0, accel_along_down = 0;
432         int16_t accel_across_up = 0, accel_across_down = 0;
433         int16_t accel_through_up = 0, accel_through_down = 0;
434 #endif
435
436         ao_cmd_decimal();
437         if (ao_cmd_status != ao_cmd_success)
438                 return;
439         if (ao_cmd_lex_i == 0) {
440                 up = ao_config_accel_calibrate_auto("up");
441 #if HAS_GYRO
442                 accel_along_up = accel_cal_along;
443                 accel_across_up = accel_cal_across;
444                 accel_through_up = accel_cal_through;
445 #endif
446                 down = ao_config_accel_calibrate_auto("down");
447 #if HAS_GYRO
448                 accel_along_down = accel_cal_along;
449                 accel_across_down = accel_cal_across;
450                 accel_through_down = accel_cal_through;
451 #endif
452         } else {
453                 up = ao_cmd_lex_i;
454                 ao_cmd_decimal();
455                 if (ao_cmd_status != ao_cmd_success)
456                         return;
457                 down = ao_cmd_lex_i;
458         }
459         if (up >= down) {
460                 printf("Invalid accel: up (%d) down (%d)\n",
461                        up, down);
462                 return;
463         }
464         _ao_config_edit_start();
465         ao_config.accel_plus_g = up;
466         ao_config.accel_minus_g = down;
467 #if HAS_GYRO
468         if (ao_cmd_lex_i == 0) {
469                 ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
470                 ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
471                 ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
472         }
473 #endif
474         _ao_config_edit_finish();
475 }
476 #endif /* HAS_ACCEL */
477
478 void
479 ao_config_apogee_delay_show(void) __reentrant
480 {
481         printf("Apogee delay: %d seconds\n",
482                ao_config.apogee_delay);
483 }
484
485 void
486 ao_config_apogee_delay_set(void) __reentrant
487 {
488         ao_cmd_decimal();
489         if (ao_cmd_status != ao_cmd_success)
490                 return;
491         _ao_config_edit_start();
492         ao_config.apogee_delay = ao_cmd_lex_i;
493         _ao_config_edit_finish();
494 }
495
496 void
497 ao_config_apogee_lockout_show(void) __reentrant
498 {
499         printf ("Apogee lockout: %d seconds\n",
500                 ao_config.apogee_lockout);
501 }
502
503 void
504 ao_config_apogee_lockout_set(void) __reentrant
505 {
506         ao_cmd_decimal();
507         if (ao_cmd_status != ao_cmd_success)
508                 return;
509         _ao_config_edit_start();
510         ao_config.apogee_lockout = ao_cmd_lex_i;
511         _ao_config_edit_finish();
512 }
513
514 #endif /* HAS_FLIGHT */
515
516 #if HAS_RADIO
517 void
518 ao_config_radio_cal_show(void) __reentrant
519 {
520         printf("Radio cal: %ld\n", ao_config.radio_cal);
521 }
522
523 void
524 ao_config_radio_cal_set(void) __reentrant
525 {
526         ao_cmd_decimal();
527         if (ao_cmd_status != ao_cmd_success)
528                 return;
529         _ao_config_edit_start();
530         ao_config.radio_cal = ao_cmd_lex_u32;
531         ao_config_set_radio();
532         _ao_config_edit_finish();
533 }
534
535 #endif
536
537 #if HAS_RADIO_RATE
538 #ifndef HAS_TELEMETRY
539 #error Please define HAS_TELEMETRY
540 #endif
541
542 void
543 ao_config_radio_rate_show(void) __reentrant
544 {
545         printf("Telemetry rate: %d\n", ao_config.radio_rate);
546 }
547
548 void
549 ao_config_radio_rate_set(void) __reentrant
550 {
551         ao_cmd_decimal();
552         if (ao_cmd_status != ao_cmd_success)
553                 return;
554         if (AO_RADIO_RATE_MAX < ao_cmd_lex_i) {
555                 ao_cmd_status = ao_cmd_lex_error;
556                 return;
557         }
558         _ao_config_edit_start();
559         ao_config.radio_rate = ao_cmd_lex_i;
560         _ao_config_edit_finish();
561 #if HAS_TELEMETRY
562         ao_telemetry_reset_interval();
563 #endif
564 #if HAS_RADIO_RECV
565         ao_radio_recv_abort();
566 #endif
567 }
568 #endif
569
570 #if HAS_LOG
571
572 void
573 ao_config_log_show(void) __reentrant
574 {
575         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
576 #if FLIGHT_LOG_APPEND
577         printf("Log fixed: 1\n");
578 #endif
579 }
580
581 #if FLIGHT_LOG_APPEND
582 void
583 ao_config_log_fix_append(void)
584 {
585         _ao_config_edit_start();
586         ao_config.flight_log_max = ao_storage_log_max;
587         _ao_config_edit_finish();
588         ao_mutex_get(&ao_config_mutex);
589         _ao_config_put();
590         ao_config_dirty = 0;
591         ao_mutex_put(&ao_config_mutex);
592 }
593 #endif
594
595 void
596 ao_config_log_set(void) __reentrant
597 {
598 #if FLIGHT_LOG_APPEND
599         printf("Flight log fixed size %d kB\n", ao_storage_log_max >> 10);
600 #else
601         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
602         uint16_t        log_max = (uint16_t) (ao_storage_log_max >> 10);
603
604         ao_cmd_decimal();
605         if (ao_cmd_status != ao_cmd_success)
606                 return;
607         if (ao_log_present())
608                 printf("Storage must be empty before changing log size\n");
609         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
610                 printf("Flight log size must be multiple of %d kB\n", block);
611         else if (ao_cmd_lex_i > log_max)
612                 printf("Flight log max %d kB\n", log_max);
613         else {
614                 _ao_config_edit_start();
615                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
616                 _ao_config_edit_finish();
617         }
618 #endif
619 }
620 #endif /* HAS_LOG */
621
622 #if HAS_IGNITE
623 void
624 ao_config_ignite_mode_show(void) __reentrant
625 {
626         printf("Ignite mode: %d\n", ao_config.ignite_mode);
627 }
628
629 void
630 ao_config_ignite_mode_set(void) __reentrant
631 {
632         ao_cmd_decimal();
633         if (ao_cmd_status != ao_cmd_success)
634                 return;
635         _ao_config_edit_start();
636         ao_config.ignite_mode = ao_cmd_lex_i;
637         _ao_config_edit_finish();
638 }
639 #endif
640
641 #if HAS_ACCEL
642 void
643 ao_config_pad_orientation_show(void) __reentrant
644 {
645         printf("Pad orientation: %d\n", ao_config.pad_orientation);
646 }
647
648 #ifndef AO_ACCEL_INVERT
649 #define AO_ACCEL_INVERT 0x7fff
650 #endif
651
652 void
653 ao_config_pad_orientation_set(void) __reentrant
654 {
655         ao_cmd_decimal();
656         if (ao_cmd_status != ao_cmd_success)
657                 return;
658         _ao_config_edit_start();
659         ao_cmd_lex_i &= 1;
660         if (ao_config.pad_orientation != ao_cmd_lex_i) {
661                 int16_t t;
662                 t = ao_config.accel_plus_g;
663                 ao_config.accel_plus_g = AO_ACCEL_INVERT - ao_config.accel_minus_g;
664                 ao_config.accel_minus_g = AO_ACCEL_INVERT - t;
665         }
666         ao_config.pad_orientation = ao_cmd_lex_i;
667         _ao_config_edit_finish();
668 }
669 #endif
670
671 #if HAS_RADIO
672 void
673 ao_config_radio_enable_show(void) __reentrant
674 {
675         printf("Radio enable: %d\n", ao_config.radio_enable);
676 }
677
678 void
679 ao_config_radio_enable_set(void) __reentrant
680 {
681         ao_cmd_decimal();
682         if (ao_cmd_status != ao_cmd_success)
683                 return;
684         _ao_config_edit_start();
685         ao_config.radio_enable = ao_cmd_lex_i;
686         _ao_config_edit_finish();
687 #if HAS_TELEMETRY && HAS_RADIO_RATE
688         ao_telemetry_reset_interval();
689 #endif
690 }
691 #endif /* HAS_RADIO */
692
693 #if HAS_AES
694
695 __xdata uint8_t ao_config_aes_seq = 1;
696
697 void
698 ao_config_key_show(void) __reentrant
699 {
700         uint8_t i;
701         printf("AES key: ");
702         for (i = 0; i < AO_AES_LEN; i++)
703                 printf ("%02x", ao_config.aes_key[i]);
704         printf("\n");
705 }
706
707 void
708 ao_config_key_set(void) __reentrant
709 {
710         uint8_t i;
711
712         _ao_config_edit_start();
713         for (i = 0; i < AO_AES_LEN; i++) {
714                 ao_cmd_hexbyte();
715                 if (ao_cmd_status != ao_cmd_success)
716                         break;
717                 ao_config.aes_key[i] = ao_cmd_lex_i;
718         }
719         ++ao_config_aes_seq;
720         _ao_config_edit_finish();
721 }
722 #endif
723
724 #if HAS_APRS
725
726 void
727 ao_config_aprs_show(void)
728 {
729         printf ("APRS interval: %d\n", ao_config.aprs_interval);
730 }
731
732 void
733 ao_config_aprs_set(void)
734 {
735         ao_cmd_decimal();
736         if (ao_cmd_status != ao_cmd_success)
737                 return;
738         _ao_config_edit_start();
739         ao_config.aprs_interval = ao_cmd_lex_i;
740         _ao_config_edit_finish();
741         ao_telemetry_reset_interval();
742 }
743
744 #endif /* HAS_APRS */
745
746 #if HAS_RADIO_AMP
747
748 void
749 ao_config_radio_amp_show(void)
750 {
751         printf ("Radio amp setting: %d\n", ao_config.radio_amp);
752 }
753
754 void
755 ao_config_radio_amp_set(void)
756 {
757         ao_cmd_decimal();
758         if (ao_cmd_status != ao_cmd_success)
759                 return;
760         _ao_config_edit_start();
761         ao_config.radio_amp = ao_cmd_lex_i;
762         _ao_config_edit_finish();
763 }
764
765 #endif
766
767 #if HAS_RADIO_POWER
768
769 void
770 ao_config_radio_power_show(void)
771 {
772         printf ("Radio power setting: %d\n", ao_config.radio_power);
773 }
774
775 void
776 ao_config_radio_power_set(void)
777 {
778         ao_cmd_decimal();
779         if (ao_cmd_status != ao_cmd_success)
780                 return;
781         _ao_config_edit_start();
782         ao_config.radio_power = ao_cmd_lex_i;
783         _ao_config_edit_finish();
784 }
785
786 #endif
787
788 #if HAS_BEEP_CONFIG
789 void
790 ao_config_beep_show(void)
791 {
792         printf ("Beeper setting: %d\n", ao_config.mid_beep);
793 }
794
795 void
796 ao_config_beep_set(void)
797 {
798         ao_cmd_decimal();
799         if (ao_cmd_status != ao_cmd_success)
800                 return;
801         _ao_config_edit_start();
802         ao_config.mid_beep = ao_cmd_lex_i;
803         _ao_config_edit_finish();
804 }
805 #endif
806
807 #if HAS_TRACKER
808 void
809 ao_config_tracker_show(void)
810 {
811         printf ("Tracker setting: %d %d\n",
812                 ao_config.tracker_motion,
813                 ao_config.tracker_interval);
814 }
815
816 void
817 ao_config_tracker_set(void)
818 {
819         uint16_t        m, i;
820         ao_cmd_decimal();
821         if (ao_cmd_status != ao_cmd_success)
822                 return;
823         m = ao_cmd_lex_i;
824         ao_cmd_decimal();
825         if (ao_cmd_status != ao_cmd_success)
826                 return;
827         i = ao_cmd_lex_i;
828         _ao_config_edit_start();
829         ao_config.tracker_motion = m;
830         ao_config.tracker_interval = i;
831         _ao_config_edit_finish();
832 #if HAS_TELEMETRY
833         ao_telemetry_reset_interval();
834 #endif
835 }
836 #endif /* HAS_TRACKER */
837
838 #if AO_PYRO_NUM
839 void
840 ao_config_pyro_time_show(void)
841 {
842         printf ("Pyro time: %d\n", ao_config.pyro_time);
843 }
844
845 void
846 ao_config_pyro_time_set(void)
847 {
848         ao_cmd_decimal();
849         if (ao_cmd_status != ao_cmd_success)
850                 return;
851         _ao_config_edit_start();
852         ao_config.pyro_time = ao_cmd_lex_i;
853         _ao_config_edit_finish();
854 }
855 #endif
856
857 #if HAS_APRS
858 void
859 ao_config_aprs_ssid_show(void)
860 {
861         printf ("APRS SSID: %d\n",
862                 ao_config.aprs_ssid);
863 }
864
865 void
866 ao_config_aprs_ssid_set(void)
867 {
868         ao_cmd_decimal();
869         if (ao_cmd_status != ao_cmd_success)
870                 return;
871         if (15 < ao_cmd_lex_i) {
872                 ao_cmd_status = ao_cmd_lex_error;
873                 return;
874         }
875         _ao_config_edit_start();
876         ao_config.aprs_ssid = ao_cmd_lex_i;
877         _ao_config_edit_finish();
878 }
879 #endif /* HAS_APRS */
880
881 struct ao_config_var {
882         __code char     *str;
883         void            (*set)(void) __reentrant;
884         void            (*show)(void) __reentrant;
885 };
886
887 static void
888 ao_config_help(void) __reentrant;
889
890 static void
891 ao_config_show(void) __reentrant;
892
893 #if HAS_EEPROM
894 static void
895 ao_config_save(void) __reentrant;
896 #endif
897
898 __code struct ao_config_var ao_config_vars[] = {
899 #if HAS_FLIGHT
900         { "m <meters>\0Main deploy (m)",
901           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
902         { "d <delay>\0Apogee delay (s)",
903           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
904         { "L <seconds>\0Apogee detect lockout (s)",
905           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
906 #endif /* HAS_FLIGHT */
907 #if HAS_RADIO
908         { "F <freq>\0Frequency (kHz)",
909           ao_config_frequency_set, ao_config_frequency_show },
910 #if HAS_RADIO_FORWARD
911         { "R <freq>\0Repeater output frequency (kHz)",
912           ao_config_send_frequency_set, ao_config_send_frequency_show },
913 #endif
914         { "c <call>\0Callsign (8 char max)",
915           ao_config_callsign_set,       ao_config_callsign_show },
916         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
917           ao_config_radio_enable_set, ao_config_radio_enable_show },
918         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
919           ao_config_radio_cal_set,      ao_config_radio_cal_show },
920 #if HAS_RADIO_RATE
921         { "T <rate>\0Telemetry rate (0=38.4, 1=9.6, 2=2.4)",
922           ao_config_radio_rate_set,     ao_config_radio_rate_show },
923 #endif
924 #if HAS_RADIO_POWER
925         { "p <setting>\0Radio power setting (0-255)",
926           ao_config_radio_power_set,    ao_config_radio_power_show },
927 #endif
928 #if HAS_RADIO_AMP
929         { "d <setting>\0Radio amplifier setting (0-3)",
930           ao_config_radio_amp_set,      ao_config_radio_amp_show },
931 #endif
932 #endif /* HAS_RADIO */
933 #if HAS_ACCEL
934         { "a <+g> <-g>\0Accel calib (0 for auto)",
935           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
936         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
937           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
938 #endif /* HAS_ACCEL */
939 #if HAS_LOG
940         { "l <size>\0Flight log size (kB)",
941           ao_config_log_set,            ao_config_log_show },
942 #endif
943 #if HAS_IGNITE
944         { "i <0 dual, 1 apogee, 2 main>\0Igniter mode",
945           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
946 #endif
947 #if HAS_AES
948         { "k <32 hex digits>\0AES encryption key",
949           ao_config_key_set, ao_config_key_show },
950 #endif
951 #if AO_PYRO_NUM
952         { "P <n,?>\0Pyro channels",
953           ao_pyro_set, ao_pyro_show },
954         { "I <ticks>\0Pyro firing time",
955           ao_config_pyro_time_set, ao_config_pyro_time_show },
956 #endif
957 #if HAS_APRS
958         { "A <secs>\0APRS packet interval (0 disable)",
959           ao_config_aprs_set, ao_config_aprs_show },
960 #endif
961 #if HAS_BEEP_CONFIG
962         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
963           ao_config_beep_set, ao_config_beep_show },
964 #endif
965 #if HAS_TRACKER
966         { "t <motion> <interval>\0Tracker configuration",
967           ao_config_tracker_set, ao_config_tracker_show },
968 #endif
969 #if HAS_APRS
970         { "S <ssid>\0Set APRS SSID (0-15)",
971           ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
972 #endif
973         { "s\0Show",
974           ao_config_show,               0 },
975 #if HAS_EEPROM
976         { "w\0Write to eeprom",
977           ao_config_save,               0 },
978 #endif
979         { "?\0Help",
980           ao_config_help,               0 },
981         { 0, 0, 0 }
982 };
983
984 void
985 ao_config_set(void)
986 {
987         char    c;
988         uint8_t cmd;
989
990         ao_cmd_white();
991         c = ao_cmd_lex_c;
992         ao_cmd_lex();
993         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
994                 if (ao_config_vars[cmd].str[0] == c) {
995                         (*ao_config_vars[cmd].set)();
996                         return;
997                 }
998         ao_cmd_status = ao_cmd_syntax_error;
999 }
1000
1001 static void
1002 ao_config_help(void) __reentrant
1003 {
1004         uint8_t cmd;
1005         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1006                 printf("%-20s %s\n",
1007                        ao_config_vars[cmd].str,
1008                        ao_config_vars[cmd].str+1+
1009                        strlen(ao_config_vars[cmd].str));
1010 }
1011
1012 static void
1013 ao_config_show(void) __reentrant
1014 {
1015         uint8_t cmd;
1016         ao_config_get();
1017         printf("Config version: %d.%d\n",
1018                ao_config.major, ao_config.minor);
1019         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1020                 if (ao_config_vars[cmd].show)
1021                         (*ao_config_vars[cmd].show)();
1022 #if HAS_MS5607
1023         ao_ms5607_info();
1024 #endif
1025 }
1026
1027 #if HAS_EEPROM
1028 static void
1029 ao_config_save(void) __reentrant
1030 {
1031         uint8_t saved = 0;
1032         ao_mutex_get(&ao_config_mutex);
1033         if (ao_config_dirty) {
1034                 _ao_config_put();
1035                 ao_config_dirty = 0;
1036                 saved = 1;
1037         }
1038         ao_mutex_put(&ao_config_mutex);
1039         if (saved)
1040                 puts("Saved");
1041         else
1042                 puts("Nothing to save");
1043 }
1044 #endif
1045
1046 __code struct ao_cmds ao_config_cmds[] = {
1047         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
1048         { 0, NULL },
1049 };
1050
1051 void
1052 ao_config_init(void)
1053 {
1054         ao_cmd_register(&ao_config_cmds[0]);
1055 }