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