b5dd7ddf8f1c32f47de032be6d65d8904b43944e
[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         ao_cmd_status = ao_cmd_success;
481         if (auto_cal) {
482                 up = ao_config_accel_calibrate_auto("up");
483 #if HAS_IMU
484                 accel_along_up = accel_cal_along;
485                 accel_across_up = accel_cal_across;
486                 accel_through_up = accel_cal_through;
487 #endif
488                 down = ao_config_accel_calibrate_auto("down");
489 #if HAS_IMU
490                 accel_along_down = accel_cal_along;
491                 accel_across_down = accel_cal_across;
492                 accel_through_down = accel_cal_through;
493 #endif
494         }
495         if (up >= down) {
496                 printf("Invalid accel: up (%d) down (%d)\n",
497                        up, down);
498                 return;
499         }
500         _ao_config_edit_start();
501         ao_config.accel_plus_g = up;
502         ao_config.accel_minus_g = down;
503 #if HAS_IMU
504         if (auto_cal) {
505                 ao_config.accel_zero_along = (int16_t) ((accel_along_up + accel_along_down) / 2);
506                 ao_config.accel_zero_across = (int16_t) ((accel_across_up + accel_across_down) / 2);
507                 ao_config.accel_zero_through = (int16_t) ((accel_through_up + accel_through_down) / 2);
508         } else {
509                 int16_t v;
510
511                 v = (int16_t) ao_cmd_decimal();
512                 if (ao_cmd_status == ao_cmd_success) {
513                         ao_config.accel_zero_along = v;
514                         v = (int16_t) ao_cmd_decimal();
515                         if (ao_cmd_status == ao_cmd_success) {
516                                 ao_config.accel_zero_across = v;
517                                 v = (int16_t) ao_cmd_decimal();
518                                 if (ao_cmd_status == ao_cmd_success)
519                                         ao_config.accel_zero_through = v;
520                         }
521                 }
522         }
523 #endif
524         _ao_config_edit_finish();
525 }
526 #endif /* HAS_ACCEL */
527
528 #if HAS_BARO
529 static void
530 ao_config_apogee_delay_show(void) 
531 {
532         printf("Apogee delay: %d seconds\n",
533                ao_config.apogee_delay);
534 }
535
536 static void
537 ao_config_apogee_delay_set(void) 
538 {
539         uint32_t r = ao_cmd_decimal();
540         if (ao_cmd_status != ao_cmd_success)
541                 return;
542         if (r > 255) {
543                 ao_cmd_status = ao_cmd_lex_error;
544                 return;
545         }
546         _ao_config_edit_start();
547         ao_config.apogee_delay = (uint8_t) r;
548         _ao_config_edit_finish();
549 }
550
551 static void
552 ao_config_apogee_lockout_show(void) 
553 {
554         printf ("Apogee lockout: %d seconds\n",
555                 ao_config.apogee_lockout);
556 }
557
558 static void
559 ao_config_apogee_lockout_set(void) 
560 {
561         uint32_t r = ao_cmd_decimal();
562         if (ao_cmd_status != ao_cmd_success)
563                 return;
564         if (r > 65535) {
565                 ao_cmd_status = ao_cmd_lex_error;
566                 return;
567         }
568         _ao_config_edit_start();
569         ao_config.apogee_lockout = (uint16_t) r;
570         _ao_config_edit_finish();
571 }
572 #endif
573
574 #endif /* HAS_FLIGHT */
575
576 #if HAS_RADIO
577 static void
578 ao_config_radio_cal_show(void) 
579 {
580         printf("Radio cal: %ld\n", ao_config.radio_cal);
581 }
582
583 static void
584 ao_config_radio_cal_set(void) 
585 {
586         uint32_t r = ao_cmd_decimal();
587         if (ao_cmd_status != ao_cmd_success)
588                 return;
589         _ao_config_edit_start();
590         ao_config.radio_cal = r;
591         ao_config_set_radio();
592         _ao_config_edit_finish();
593 }
594
595 #endif
596
597 #if HAS_RADIO_RATE
598 #ifndef HAS_TELEMETRY
599 #error Please define HAS_TELEMETRY
600 #endif
601
602 static void
603 ao_config_radio_rate_show(void) 
604 {
605         printf("Telemetry rate: %d\n", ao_config.radio_rate);
606 }
607
608 static void
609 ao_config_radio_rate_set(void) 
610 {
611         uint32_t r = ao_cmd_decimal();
612         if (ao_cmd_status != ao_cmd_success)
613                 return;
614         if (AO_RADIO_RATE_MAX < r) {
615                 ao_cmd_status = ao_cmd_lex_error;
616                 return;
617         }
618         _ao_config_edit_start();
619         ao_config.radio_rate = (uint8_t) r;
620         _ao_config_edit_finish();
621 #if HAS_TELEMETRY
622         ao_telemetry_reset_interval();
623 #endif
624 #if HAS_RADIO_RECV
625         ao_radio_recv_abort();
626 #endif
627 }
628 #endif
629
630 #if HAS_LOG
631
632 static void
633 ao_config_log_show(void) 
634 {
635         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
636 #if FLIGHT_LOG_APPEND
637         printf("Log fixed: 1\n");
638 #endif
639 }
640
641 #if FLIGHT_LOG_APPEND && HAS_CONFIG_SAVE
642 void
643 ao_config_log_fix_append(void)
644 {
645         _ao_config_edit_start();
646         ao_config.flight_log_max = ao_storage_log_max;
647         _ao_config_edit_finish();
648         ao_mutex_get(&ao_config_mutex);
649         _ao_config_put();
650         ao_config_dirty = 0;
651         ao_mutex_put(&ao_config_mutex);
652 }
653 #endif
654
655 static void
656 ao_config_log_set(void) 
657 {
658 #if FLIGHT_LOG_APPEND
659         printf("Flight log fixed size %u kB\n", (unsigned) (ao_storage_log_max >> 10));
660 #else
661         uint32_t        r;
662
663         r = ao_cmd_decimal();
664         if (ao_cmd_status != ao_cmd_success)
665                 return;
666         r = r << 10;
667         if (ao_log_present()) {
668                 if (r != ao_config.flight_log_max)
669                         printf("Storage must be empty before changing log size\n");
670                 return;
671         }
672         if (r > ao_storage_log_max) {
673                 printf("Flight log max %u kB\n", (unsigned) (ao_storage_log_max >> 10));
674                 return;
675         }
676         _ao_config_edit_start();
677         ao_config.flight_log_max = r & ~(ao_storage_block - 1);
678         _ao_config_edit_finish();
679 #endif
680 }
681 #endif /* HAS_LOG */
682
683 #if HAS_IGNITE
684 static void
685 ao_config_ignite_mode_show(void) 
686 {
687         printf("Ignite mode: %d\n", ao_config.ignite_mode);
688 }
689
690 static void
691 ao_config_ignite_mode_set(void) 
692 {
693         uint32_t r = ao_cmd_decimal();
694         if (ao_cmd_status != ao_cmd_success)
695                 return;
696         _ao_config_edit_start();
697         ao_config.ignite_mode = (uint8_t) r;
698         _ao_config_edit_finish();
699 }
700 #endif
701
702 #if HAS_ACCEL
703 static void
704 ao_config_pad_orientation_show(void) 
705 {
706         printf("Pad orientation: %d\n", ao_config.pad_orientation);
707 }
708
709 static void
710 ao_config_pad_orientation_set(void) 
711 {
712         uint8_t r = ao_cmd_decimal() & 1;
713         if (ao_cmd_status != ao_cmd_success)
714                 return;
715         _ao_config_edit_start();
716         if (ao_config.pad_orientation != r) {
717                 accel_t t;
718                 t = ao_config.accel_plus_g;
719                 ao_config.accel_plus_g = ao_data_accel_invert(ao_config.accel_minus_g);
720                 ao_config.accel_minus_g = ao_data_accel_invert(t);
721         }
722         ao_config.pad_orientation = r;
723         _ao_config_edit_finish();
724 }
725 #endif
726
727 #if HAS_RADIO
728 static void
729 ao_config_radio_enable_show(void) 
730 {
731         printf("Radio enable: %d\n", ao_config.radio_enable);
732 }
733
734 static void
735 ao_config_radio_enable_set(void) 
736 {
737         uint32_t r = ao_cmd_decimal();
738         if (ao_cmd_status != ao_cmd_success)
739                 return;
740         _ao_config_edit_start();
741         ao_config.radio_enable = r != 0;
742         _ao_config_edit_finish();
743 #if HAS_TELEMETRY && HAS_RADIO_RATE
744         ao_telemetry_reset_interval();
745 #endif
746 }
747 #endif /* HAS_RADIO */
748
749 #if HAS_AES
750
751 uint8_t ao_config_aes_seq = 1;
752
753 static void
754 ao_config_key_show(void) 
755 {
756         uint8_t i;
757         printf("AES key: ");
758         for (i = 0; i < AO_AES_LEN; i++)
759                 printf ("%02x", ao_config.aes_key[i]);
760         printf("\n");
761 }
762
763 static void
764 ao_config_key_set(void) 
765 {
766         uint8_t i;
767
768         _ao_config_edit_start();
769         for (i = 0; i < AO_AES_LEN; i++) {
770                 uint8_t b = ao_cmd_hexbyte();
771                 if (ao_cmd_status != ao_cmd_success)
772                         break;
773                 ao_config.aes_key[i] = b;
774         }
775         ++ao_config_aes_seq;
776         _ao_config_edit_finish();
777 }
778 #endif
779
780 #if HAS_APRS
781
782 static void
783 ao_config_aprs_show(void)
784 {
785         printf ("APRS interval: %d\n", ao_config.aprs_interval);
786 }
787
788 static void
789 ao_config_aprs_set(void)
790 {
791         uint32_t r = ao_cmd_decimal();
792         if (ao_cmd_status != ao_cmd_success)
793                 return;
794         _ao_config_edit_start();
795         ao_config.aprs_interval = (uint16_t) r;
796         _ao_config_edit_finish();
797         ao_telemetry_reset_interval();
798 }
799
800 static void
801 ao_config_aprs_offset_show(void)
802 {
803         printf ("APRS offset: %d\n", ao_config.aprs_offset);
804 }
805
806 static void
807 ao_config_aprs_offset_set(void)
808 {
809         uint32_t r = ao_cmd_decimal();
810         if (ao_cmd_status != ao_cmd_success)
811                 return;
812         _ao_config_edit_start();
813         ao_config.aprs_offset = (uint8_t) r;
814         _ao_config_edit_finish();
815         ao_telemetry_reset_interval();
816 }
817
818 #endif /* HAS_APRS */
819
820 #if HAS_RADIO_AMP
821
822 void
823 ao_config_radio_amp_show(void)
824 {
825         printf ("Radio amp setting: %d\n", ao_config.radio_amp);
826 }
827
828 void
829 ao_config_radio_amp_set(void)
830 {
831         uint16_t r = ao_cmd_decimal();
832         if (ao_cmd_status != ao_cmd_success)
833                 return;
834         _ao_config_edit_start();
835         ao_config.radio_amp = r;
836         _ao_config_edit_finish();
837 }
838
839 #endif
840
841 #if HAS_RADIO_POWER
842
843 void
844 ao_config_radio_power_show(void)
845 {
846         printf ("Radio power setting: %d\n", ao_config.radio_power);
847 }
848
849 void
850 ao_config_radio_power_set(void)
851 {
852         uint16_t r = ao_cmd_decimal();
853         if (ao_cmd_status != ao_cmd_success)
854                 return;
855         _ao_config_edit_start();
856         ao_config.radio_power = r;
857         _ao_config_edit_finish();
858 }
859
860 #endif
861
862 #if HAS_RADIO_10MW
863
864 static void
865 ao_config_radio_10mw_show(void)
866 {
867         printf ("Radio 10mw limit: %d\n", ao_config.radio_10mw);
868 }
869
870 static void
871 ao_config_radio_10mw_set(void)
872 {
873         uint32_t r = ao_cmd_decimal();
874         if (ao_cmd_status != ao_cmd_success)
875                 return;
876         _ao_config_edit_start();
877         ao_config.radio_10mw = !!r;
878         _ao_config_edit_finish();
879 }
880
881 #endif
882
883 #if HAS_BEEP
884 static void
885 ao_config_beep_show(void)
886 {
887         printf ("Beeper setting: %d\n", ao_config.mid_beep);
888 }
889
890 static void
891 ao_config_beep_set(void)
892 {
893         uint32_t r = ao_cmd_decimal();
894         if (ao_cmd_status != ao_cmd_success)
895                 return;
896         _ao_config_edit_start();
897         ao_config.mid_beep = (uint8_t) r;
898         _ao_config_edit_finish();
899 }
900 #endif
901
902 #if HAS_TRACKER
903 static void
904 ao_config_tracker_show(void)
905 {
906         printf ("Tracker setting: %d %d\n",
907                 ao_config.tracker_motion,
908                 ao_config.tracker_interval);
909 }
910
911 static void
912 ao_config_tracker_set(void)
913 {
914         uint16_t        m;
915         uint8_t         i;
916         m = (uint16_t) ao_cmd_decimal();
917         if (ao_cmd_status != ao_cmd_success)
918                 return;
919         i = (uint8_t) ao_cmd_decimal();
920         if (ao_cmd_status != ao_cmd_success)
921                 return;
922         _ao_config_edit_start();
923         ao_config.tracker_motion = m;
924         ao_config.tracker_interval = i;
925         _ao_config_edit_finish();
926 #if HAS_TELEMETRY
927         ao_telemetry_reset_interval();
928 #endif
929 }
930 #endif /* HAS_TRACKER */
931
932 #if AO_PYRO_NUM
933 static void
934 ao_config_pyro_time_show(void)
935 {
936         printf ("Pyro time: %d\n", ao_config.pyro_time);
937 }
938
939 static void
940 ao_config_pyro_time_set(void)
941 {
942         uint32_t r = ao_cmd_decimal();
943         if (ao_cmd_status != ao_cmd_success)
944                 return;
945         _ao_config_edit_start();
946         ao_config.pyro_time = (uint16_t) r;
947         _ao_config_edit_finish();
948 }
949 #endif
950
951 #if HAS_APRS
952 static void
953 ao_config_aprs_ssid_show(void)
954 {
955         printf ("APRS SSID: %d\n",
956                 ao_config.aprs_ssid);
957 }
958
959 static void
960 ao_config_aprs_ssid_set(void)
961 {
962         uint32_t r = ao_cmd_decimal();
963         if (ao_cmd_status != ao_cmd_success)
964                 return;
965         if (15 < r) {
966                 ao_cmd_status = ao_cmd_lex_error;
967                 return;
968         }
969         _ao_config_edit_start();
970         ao_config.aprs_ssid = (uint8_t) r;
971         _ao_config_edit_finish();
972 }
973
974 static void
975 ao_config_aprs_format_set(void)
976 {
977         uint32_t r = ao_cmd_decimal();
978         if (ao_cmd_status != ao_cmd_success)
979                 return;
980         _ao_config_edit_start();
981         ao_config.aprs_format = r != 0;
982         _ao_config_edit_finish();
983 }
984
985 static void
986 ao_config_aprs_format_show(void)
987 {
988         printf ("APRS format: %d\n", ao_config.aprs_format);
989 }
990 #endif /* HAS_APRS */
991
992 #if HAS_FIXED_PAD_BOX
993 static void
994 ao_config_pad_box_show(void)
995 {
996         printf ("Pad box: %d\n", ao_config.pad_box);
997 }
998
999 static void
1000 ao_config_pad_box_set(void)
1001 {
1002         uint32_t r = ao_cmd_decimal();
1003         if (ao_cmd_status != ao_cmd_success)
1004                 return;
1005         _ao_config_edit_start();
1006         ao_config.pad_box = (uint8_t) r;
1007         _ao_config_edit_finish();
1008 }
1009
1010 static void
1011 ao_config_pad_idle_show(void)
1012 {
1013         printf ("Idle timeout: %d\n", ao_config.pad_idle);
1014 }
1015
1016 static void
1017 ao_config_pad_idle_set(void)
1018 {
1019         uint32_t r = ao_cmd_decimal();
1020         if (ao_cmd_status != ao_cmd_success)
1021                 return;
1022         _ao_config_edit_start();
1023         ao_config.pad_idle = (uint8_t) r;
1024         _ao_config_edit_finish();
1025 }
1026 #endif
1027
1028 struct ao_config_var {
1029         const char      *str;
1030         void            (*set)(void);
1031         void            (*show)(void);
1032 };
1033
1034 static void
1035 ao_config_help(void);
1036
1037 static void
1038 ao_config_show(void);
1039
1040 #if HAS_CONFIG_SAVE
1041 static void
1042 ao_config_save(void);
1043 #endif
1044
1045 const struct ao_config_var ao_config_vars[] = {
1046 #if HAS_FLIGHT && HAS_BARO
1047         { "m <meters>\0Main deploy (m)",
1048           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
1049         { "d <delay>\0Apogee delay (s)",
1050           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
1051         { "L <seconds>\0Apogee detect lockout (s)",
1052           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
1053 #endif /* HAS_FLIGHT */
1054 #if HAS_RADIO
1055         { "F <freq>\0Frequency (kHz)",
1056           ao_config_frequency_set, ao_config_frequency_show },
1057 #if HAS_RADIO_FORWARD
1058         { "R <freq>\0Repeater output frequency (kHz)",
1059           ao_config_send_frequency_set, ao_config_send_frequency_show },
1060 #endif
1061         { "c <call>\0Callsign (8 char max)",
1062           ao_config_callsign_set,       ao_config_callsign_show },
1063         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
1064           ao_config_radio_enable_set, ao_config_radio_enable_show },
1065         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
1066           ao_config_radio_cal_set,      ao_config_radio_cal_show },
1067 #if HAS_RADIO_RATE
1068         { "T <rate>\0Telemetry rate (0=38.4, 1=9.6, 2=2.4)",
1069           ao_config_radio_rate_set,     ao_config_radio_rate_show },
1070 #endif
1071 #if HAS_RADIO_POWER
1072         { "p <setting>\0Radio power setting (0-255)",
1073           ao_config_radio_power_set,    ao_config_radio_power_show },
1074 #endif
1075 #if HAS_RADIO_AMP
1076         { "d <setting>\0Radio amplifier setting (0-3)",
1077           ao_config_radio_amp_set,      ao_config_radio_amp_show },
1078 #endif
1079 #endif /* HAS_RADIO */
1080 #if HAS_ACCEL
1081         { "a <+g> <-g>\0Accel calib (0 for auto)",
1082           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
1083         { "o <0 antenna up, 1 antenna down>\0Pad orientation",
1084           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
1085 #endif /* HAS_ACCEL */
1086 #if HAS_LOG
1087         { "l <size>\0Flight log size (kB)",
1088           ao_config_log_set,            ao_config_log_show },
1089 #endif
1090 #if HAS_IGNITE
1091         { "i <0 dual, 1 apogee, 2 main, 3 booster>\0Igniter mode",
1092           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
1093 #endif
1094 #if HAS_AES
1095         { "k <32 hex digits>\0AES encryption key",
1096           ao_config_key_set, ao_config_key_show },
1097 #endif
1098 #if AO_PYRO_NUM
1099         { "P <n,?>\0Pyro channels",
1100           ao_pyro_set, ao_pyro_show },
1101         { "I <ticks>\0Pyro firing time",
1102           ao_config_pyro_time_set, ao_config_pyro_time_show },
1103 #endif
1104 #if HAS_APRS
1105         { "A <secs>\0APRS packet interval (0 disable)",
1106           ao_config_aprs_set, ao_config_aprs_show },
1107 #endif
1108 #if HAS_BEEP
1109         { "b <val>\0Beeper tone (freq = 1/2 (24e6/32) / beep",
1110           ao_config_beep_set, ao_config_beep_show },
1111 #endif
1112 #if HAS_TRACKER
1113         { "t <motion> <interval>\0Tracker configuration",
1114           ao_config_tracker_set, ao_config_tracker_show },
1115 #endif
1116 #if HAS_APRS
1117         { "S <ssid>\0Set APRS SSID (0-15)",
1118           ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
1119         { "C <0 compressed, 1 uncompressed>\0APRS format",
1120           ao_config_aprs_format_set, ao_config_aprs_format_show },
1121         { "O <aprs-offset>\0APRS Offset from top of minute",
1122           ao_config_aprs_offset_set, ao_config_aprs_offset_show },
1123 #endif
1124 #if HAS_FIXED_PAD_BOX
1125         { "B <box>\0Set pad box (1-99)",
1126           ao_config_pad_box_set, ao_config_pad_box_show },
1127         { "i <seconds>\0Set idle timeout (0 disable)",
1128           ao_config_pad_idle_set, ao_config_pad_idle_show },
1129 #endif
1130 #if HAS_RADIO_10MW
1131         { "p <0 no limit, 1 limit>\0Limit radio power to 10mW",
1132           ao_config_radio_10mw_set,     ao_config_radio_10mw_show },
1133 #endif
1134         { "s\0Show",
1135           ao_config_show,               0 },
1136 #if HAS_CONFIG_SAVE
1137         { "w\0Write to eeprom",
1138           ao_config_save,               0 },
1139 #endif
1140         { "?\0Help",
1141           ao_config_help,               0 },
1142         { 0, 0, 0 }
1143 };
1144
1145 static void
1146 ao_config_set(void)
1147 {
1148         char    c;
1149         uint8_t cmd;
1150
1151         ao_cmd_white();
1152         c = ao_cmd_lex_c;
1153         ao_cmd_lex();
1154         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1155                 if (ao_config_vars[cmd].str[0] == c) {
1156                         (*ao_config_vars[cmd].set)();
1157                         return;
1158                 }
1159         ao_cmd_status = ao_cmd_syntax_error;
1160 }
1161
1162 static void
1163 ao_config_help(void) 
1164 {
1165         uint8_t cmd;
1166         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1167                 printf("%-20s %s\n",
1168                        ao_config_vars[cmd].str,
1169                        ao_config_vars[cmd].str+1+
1170                        strlen(ao_config_vars[cmd].str));
1171 }
1172
1173 static void
1174 ao_config_show(void) 
1175 {
1176         uint8_t cmd;
1177         ao_config_get();
1178         printf("Config version: %d.%d\n",
1179                ao_config.major, ao_config.minor);
1180         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
1181                 if (ao_config_vars[cmd].show)
1182                         (*ao_config_vars[cmd].show)();
1183 #if HAS_MS5607
1184         ao_ms5607_info();
1185 #endif
1186 }
1187
1188 #if HAS_CONFIG_SAVE
1189 static void
1190 ao_config_save(void) 
1191 {
1192         uint8_t saved = 0;
1193         ao_mutex_get(&ao_config_mutex);
1194         if (ao_config_dirty) {
1195                 _ao_config_put();
1196                 ao_config_dirty = 0;
1197                 saved = 1;
1198         }
1199         ao_mutex_put(&ao_config_mutex);
1200         if (saved)
1201                 puts("Saved");
1202         else
1203                 puts("Nothing to save");
1204 }
1205 #endif
1206
1207 const struct ao_cmds ao_config_cmds[] = {
1208         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
1209         { 0, NULL },
1210 };
1211
1212 void
1213 ao_config_init(void)
1214 {
1215         ao_cmd_register(&ao_config_cmds[0]);
1216 }