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