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