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 static void
887 ao_config_report_feet_show(void)
888 {
889         printf ("Report in feet: %d\n", ao_config.report_feet);
890 }
891
892 static void
893 ao_config_report_feet_set(void)
894 {
895         uint32_t r = ao_cmd_decimal();
896         if (ao_cmd_status != ao_cmd_success)
897                 return;
898         _ao_config_edit_start();
899         ao_config.report_feet = !!r;
900         _ao_config_edit_finish();
901 }
902
903
904 #if HAS_BEEP
905 static void
906 ao_config_beep_show(void)
907 {
908         printf ("Beeper setting: %d\n", ao_config.mid_beep);
909 }
910
911 static void
912 ao_config_beep_set(void)
913 {
914         uint32_t r = ao_cmd_decimal();
915         if (ao_cmd_status != ao_cmd_success)
916                 return;
917         _ao_config_edit_start();
918         ao_config.mid_beep = (uint8_t) r;
919         _ao_config_edit_finish();
920 }
921 #endif
922
923 #if HAS_TRACKER
924 static void
925 ao_config_tracker_show(void)
926 {
927         printf ("Tracker setting: %d %d\n",
928                 ao_config.tracker_motion,
929                 ao_config.tracker_interval);
930 }
931
932 static void
933 ao_config_tracker_set(void)
934 {
935         uint16_t        m;
936         uint8_t         i;
937         m = (uint16_t) ao_cmd_decimal();
938         if (ao_cmd_status != ao_cmd_success)
939                 return;
940         i = (uint8_t) ao_cmd_decimal();
941         if (ao_cmd_status != ao_cmd_success)
942                 return;
943         _ao_config_edit_start();
944         ao_config.tracker_motion = m;
945         ao_config.tracker_interval = i;
946         _ao_config_edit_finish();
947 #if HAS_TELEMETRY
948         ao_telemetry_reset_interval();
949 #endif
950 }
951 #endif /* HAS_TRACKER */
952
953 #if AO_PYRO_NUM
954 static void
955 ao_config_pyro_time_show(void)
956 {
957         printf ("Pyro time: %d\n", ao_config.pyro_time);
958 }
959
960 static void
961 ao_config_pyro_time_set(void)
962 {
963         uint32_t r = ao_cmd_decimal();
964         if (ao_cmd_status != ao_cmd_success)
965                 return;
966         _ao_config_edit_start();
967         ao_config.pyro_time = (uint16_t) r;
968         _ao_config_edit_finish();
969 }
970 #endif
971
972 #if HAS_APRS
973 static void
974 ao_config_aprs_ssid_show(void)
975 {
976         printf ("APRS SSID: %d\n",
977                 ao_config.aprs_ssid);
978 }
979
980 static void
981 ao_config_aprs_ssid_set(void)
982 {
983         uint32_t r = ao_cmd_decimal();
984         if (ao_cmd_status != ao_cmd_success)
985                 return;
986         if (15 < r) {
987                 ao_cmd_status = ao_cmd_lex_error;
988                 return;
989         }
990         _ao_config_edit_start();
991         ao_config.aprs_ssid = (uint8_t) r;
992         _ao_config_edit_finish();
993 }
994
995 static void
996 ao_config_aprs_format_set(void)
997 {
998         uint32_t r = ao_cmd_decimal();
999         if (ao_cmd_status != ao_cmd_success)
1000                 return;
1001         _ao_config_edit_start();
1002         ao_config.aprs_format = r != 0;
1003         _ao_config_edit_finish();
1004 }
1005
1006 static void
1007 ao_config_aprs_format_show(void)
1008 {
1009         printf ("APRS format: %d\n", ao_config.aprs_format);
1010 }
1011 #endif /* HAS_APRS */
1012
1013 #if HAS_FIXED_PAD_BOX
1014 static void
1015 ao_config_pad_box_show(void)
1016 {
1017         printf ("Pad box: %d\n", ao_config.pad_box);
1018 }
1019
1020 static void
1021 ao_config_pad_box_set(void)
1022 {
1023         uint32_t r = ao_cmd_decimal();
1024         if (ao_cmd_status != ao_cmd_success)
1025                 return;
1026         _ao_config_edit_start();
1027         ao_config.pad_box = (uint8_t) r;
1028         _ao_config_edit_finish();
1029 }
1030
1031 static void
1032 ao_config_pad_idle_show(void)
1033 {
1034         printf ("Idle timeout: %d\n", ao_config.pad_idle);
1035 }
1036
1037 static void
1038 ao_config_pad_idle_set(void)
1039 {
1040         uint32_t r = ao_cmd_decimal();
1041         if (ao_cmd_status != ao_cmd_success)
1042                 return;
1043         _ao_config_edit_start();
1044         ao_config.pad_idle = (uint8_t) r;
1045         _ao_config_edit_finish();
1046 }
1047 #endif
1048
1049 struct ao_config_var {
1050         const char      *str;
1051         void            (*set)(void);
1052         void            (*show)(void);
1053 };
1054
1055 static void
1056 ao_config_help(void);
1057
1058 static void
1059 ao_config_show(void);
1060
1061 #if HAS_CONFIG_SAVE
1062 static void
1063 ao_config_save(void);
1064 #endif
1065
1066 const struct ao_config_var ao_config_vars[] = {
1067 #if HAS_FLIGHT && HAS_BARO
1068         { "m <meters>\0Main deploy (m)",
1069           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
1070         { "d <delay>\0Apogee delay (s)",
1071           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
1072         { "L <seconds>\0Apogee detect lockout (s)",
1073           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
1074 #endif /* HAS_FLIGHT */
1075 #if HAS_RADIO
1076         { "F <freq>\0Frequency (kHz)",
1077           ao_config_frequency_set, ao_config_frequency_show },
1078 #if HAS_RADIO_FORWARD
1079         { "R <freq>\0Repeater output frequency (kHz)",
1080           ao_config_send_frequency_set, ao_config_send_frequency_show },
1081 #endif
1082         { "c <call>\0Callsign (8 char max)",
1083           ao_config_callsign_set,       ao_config_callsign_show },
1084         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
1085           ao_config_radio_enable_set, ao_config_radio_enable_show },
1086         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
1087           ao_config_radio_cal_set,      ao_config_radio_cal_show },
1088 #if HAS_RADIO_RATE
1089         { "T <rate>\0Telemetry rate (0=38.4, 1=9.6, 2=2.4)",
1090           ao_config_radio_rate_set,     ao_config_radio_rate_show },
1091 #endif
1092 #if HAS_RADIO_POWER
1093         { "p <setting>\0Radio power setting (0-255)",
1094           ao_config_radio_power_set,    ao_config_radio_power_show },
1095 #endif
1096 #if HAS_RADIO_AMP
1097         { "d <setting>\0Radio amplifier setting (0-3)",
1098           ao_config_radio_amp_set,      ao_config_radio_amp_show },
1099 #endif
1100 #endif /* HAS_RADIO */
1101 #if HAS_ACCEL
1102         { "a <+g> <-g>\0Accel calib (0 for auto)",
1103           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
1104         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
1105           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
1106 #endif /* HAS_ACCEL */
1107 #if HAS_LOG
1108         { "l <size>\0Flight log size (kB)",
1109           ao_config_log_set,            ao_config_log_show },
1110 #endif
1111 #if HAS_IGNITE
1112         { "i <0 dual, 1 apogee, 2 main, 3 booster>\0Igniter mode",
1113           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
1114 #endif
1115 #if HAS_AES
1116         { "k <32 hex digits>\0AES encryption key",
1117           ao_config_key_set, ao_config_key_show },
1118 #endif
1119 #if AO_PYRO_NUM
1120         { "P <n,?>\0Pyro channels",
1121           ao_pyro_set, ao_pyro_show },
1122         { "I <ticks>\0Pyro firing time",
1123           ao_config_pyro_time_set, ao_config_pyro_time_show },
1124 #endif
1125 #if HAS_APRS
1126         { "A <secs>\0APRS packet interval (0 disable)",
1127           ao_config_aprs_set, ao_config_aprs_show },
1128 #endif
1129 #if HAS_BEEP
1130         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
1131           ao_config_beep_set, ao_config_beep_show },
1132 #endif
1133 #if HAS_TRACKER
1134         { "t <motion> <interval>\0Tracker configuration",
1135           ao_config_tracker_set, ao_config_tracker_show },
1136 #endif
1137 #if HAS_APRS
1138         { "S <ssid>\0Set APRS SSID (0-15)",
1139           ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
1140         { "C <0 compressed, 1 uncompressed>\0APRS format",
1141           ao_config_aprs_format_set, ao_config_aprs_format_show },
1142         { "O <aprs-offset>\0APRS Offset from top of minute",
1143           ao_config_aprs_offset_set, ao_config_aprs_offset_show },
1144 #endif
1145 #if HAS_FIXED_PAD_BOX
1146         { "B <box>\0Set pad box (1-99)",
1147           ao_config_pad_box_set, ao_config_pad_box_show },
1148         { "i <seconds>\0Set idle timeout (0 disable)",
1149           ao_config_pad_idle_set, ao_config_pad_idle_show },
1150 #endif
1151 #if HAS_RADIO_10MW
1152         { "p <0 no limit, 1 limit>\0Limit radio power to 10mW",
1153           ao_config_radio_10mw_set,     ao_config_radio_10mw_show },
1154 #endif
1155         { "u <0 meters, 1 feet>\0Units to report height after landing",
1156           ao_config_report_feet_set,    ao_config_report_feet_show },
1157         { "s\0Show",
1158           ao_config_show,               0 },
1159 #if HAS_CONFIG_SAVE
1160         { "w\0Write to eeprom",
1161           ao_config_save,               0 },
1162 #endif
1163         { "?\0Help",
1164           ao_config_help,               0 },
1165         { 0, 0, 0 }
1166 };
1167
1168 static void
1169 ao_config_set(void)
1170 {
1171         char    c;
1172         uint8_t cmd;
1173
1174         ao_cmd_white();
1175         c = ao_cmd_lex_c;
1176         ao_cmd_lex();
1177         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1178                 if (ao_config_vars[cmd].str[0] == c) {
1179                         (*ao_config_vars[cmd].set)();
1180                         return;
1181                 }
1182         ao_cmd_status = ao_cmd_syntax_error;
1183 }
1184
1185 static void
1186 ao_config_help(void) 
1187 {
1188         uint8_t cmd;
1189         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1190                 printf("%-20s %s\n",
1191                        ao_config_vars[cmd].str,
1192                        ao_config_vars[cmd].str+1+
1193                        strlen(ao_config_vars[cmd].str));
1194 }
1195
1196 static void
1197 ao_config_show(void) 
1198 {
1199         uint8_t cmd;
1200         ao_config_get();
1201         printf("Config version: %d.%d\n",
1202                ao_config.major, ao_config.minor);
1203         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1204                 if (ao_config_vars[cmd].show)
1205                         (*ao_config_vars[cmd].show)();
1206 #if HAS_MS5607
1207         ao_ms5607_info();
1208 #endif
1209 }
1210
1211 #if HAS_CONFIG_SAVE
1212 static void
1213 ao_config_save(void) 
1214 {
1215         uint8_t saved = 0;
1216         ao_mutex_get(&ao_config_mutex);
1217         if (ao_config_dirty) {
1218                 _ao_config_put();
1219                 ao_config_dirty = 0;
1220                 saved = 1;
1221         }
1222         ao_mutex_put(&ao_config_mutex);
1223         if (saved)
1224                 puts("Saved");
1225         else
1226                 puts("Nothing to save");
1227 }
1228 #endif
1229
1230 const struct ao_cmds ao_config_cmds[] = {
1231         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
1232         { 0, NULL },
1233 };
1234
1235 void
1236 ao_config_init(void)
1237 {
1238         ao_cmd_register(&ao_config_cmds[0]);
1239 }