Get megametrum ready to at least log flight data
[fw/altos] / src / core / ao_config.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include "ao_log.h"
20 #include <ao_sample.h>
21 #include <ao_data.h>
22
23 __xdata struct ao_config ao_config;
24 __pdata uint8_t ao_config_loaded;
25 __pdata uint8_t ao_config_dirty;
26 __xdata uint8_t ao_config_mutex;
27
28 #define AO_CONFIG_DEFAULT_MAIN_DEPLOY   250
29 #define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
30 #define AO_CONFIG_DEFAULT_CALLSIGN      "N0CALL"
31 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G  16000
32 #define AO_CONFIG_DEFAULT_APOGEE_DELAY  0
33 #define AO_CONFIG_DEFAULT_IGNITE_MODE   AO_IGNITE_MODE_DUAL
34 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION       AO_PAD_ORIENTATION_ANTENNA_UP
35 #if HAS_EEPROM
36 #ifndef USE_INTERNAL_FLASH
37 #error Please define USE_INTERNAL_FLASH
38 #endif
39 #endif
40 #ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
41 #if USE_INTERNAL_FLASH
42 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ao_storage_config
43 #else
44 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ((uint32_t) 192 * (uint32_t) 1024)
45 #endif
46 #endif
47
48 #if HAS_EEPROM
49 static void
50 _ao_config_put(void)
51 {
52         ao_storage_setup();
53         ao_storage_erase(ao_storage_config);
54         ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
55 #if HAS_FLIGHT
56         ao_log_write_erase(0);
57 #endif
58         ao_storage_flush();
59 }
60
61 void
62 ao_config_put(void)
63 {
64         ao_mutex_get(&ao_config_mutex);
65         _ao_config_put();
66         ao_mutex_put(&ao_config_mutex);
67 }
68 #endif
69
70 #if HAS_RADIO
71 void
72 ao_config_set_radio(void)
73 {
74         ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
75 }
76 #endif /* HAS_RADIO */
77
78 static void
79 _ao_config_get(void)
80 {
81         if (ao_config_loaded)
82                 return;
83 #if HAS_EEPROM
84         ao_storage_setup();
85         ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
86 #endif
87         if (ao_config.major != AO_CONFIG_MAJOR) {
88                 ao_config.major = AO_CONFIG_MAJOR;
89                 ao_config.minor = 0;
90
91                 /* Version 0 stuff */
92                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
93                 ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
94                 ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
95                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
96                 ao_config_dirty = 1;
97         }
98         if (ao_config.minor != AO_CONFIG_MINOR) {
99                 /* Fixups for minor version 1 */
100                 if (ao_config.minor < 1)
101                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
102                 /* Fixups for minor version 2 */
103                 if (ao_config.minor < 2) {
104                         ao_config.accel_plus_g = 0;
105                         ao_config.accel_minus_g = 0;
106                 }
107                 /* Fixups for minor version 3 */
108 #if HAS_RADIO
109                 if (ao_config.minor < 3)
110                         ao_config.radio_cal = ao_radio_cal;
111 #endif
112                 /* Fixups for minor version 4 */
113                 if (ao_config.minor < 4)
114                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
115                 /* Fixupes for minor version 5 */
116                 if (ao_config.minor < 5)
117                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
118                 if (ao_config.minor < 6)
119                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
120                 if (ao_config.minor < 8)
121                         ao_config.radio_enable = TRUE;
122                 if (ao_config.minor < 9)
123                         ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
124                 if (ao_config.minor < 10)
125                         ao_config.frequency = 434550;
126                 if (ao_config.minor < 11)
127                         ao_config.apogee_lockout = 0;
128                 ao_config.minor = AO_CONFIG_MINOR;
129                 ao_config_dirty = 1;
130         }
131 #if HAS_RADIO
132         ao_config_set_radio();
133 #endif
134         ao_config_loaded = 1;
135 }
136
137 static void
138 _ao_config_edit_start(void)
139 {
140         ao_mutex_get(&ao_config_mutex);
141         _ao_config_get();
142 }
143
144 static void
145 _ao_config_edit_finish(void)
146 {
147         ao_config_dirty = 1;
148         ao_mutex_put(&ao_config_mutex);
149 }
150
151 void
152 ao_config_get(void)
153 {
154         _ao_config_edit_start();
155         ao_mutex_put(&ao_config_mutex);
156 }
157
158 void
159 ao_config_callsign_show(void)
160 {
161         printf ("Callsign: \"%s\"\n", ao_config.callsign);
162 }
163
164 void
165 ao_config_callsign_set(void) __reentrant
166 {
167         uint8_t c;
168         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
169
170         ao_xmemset(callsign, '\0', sizeof callsign);
171         ao_cmd_white();
172         c = 0;
173         while (ao_cmd_lex_c != '\n') {
174                 if (c < AO_MAX_CALLSIGN)
175                         callsign[c++] = ao_cmd_lex_c;
176                 else
177                         ao_cmd_status = ao_cmd_lex_error;
178                 ao_cmd_lex();
179         }
180         if (ao_cmd_status != ao_cmd_success)
181                 return;
182         _ao_config_edit_start();
183         ao_xmemcpy(&ao_config.callsign, &callsign,
184                AO_MAX_CALLSIGN + 1);
185         _ao_config_edit_finish();
186 }
187
188 #if HAS_RADIO
189 void
190 ao_config_frequency_show(void) __reentrant
191 {
192         printf("Frequency: %ld\n",
193                ao_config.frequency);
194 }
195
196 void
197 ao_config_frequency_set(void) __reentrant
198 {
199         ao_cmd_decimal();
200         if (ao_cmd_status != ao_cmd_success)
201                 return;
202         _ao_config_edit_start();
203         ao_config.frequency = ao_cmd_lex_u32;
204         ao_config_set_radio();
205         _ao_config_edit_finish();
206         ao_radio_recv_abort();
207 }
208 #endif
209
210 #if HAS_FLIGHT
211
212 void
213 ao_config_main_deploy_show(void) __reentrant
214 {
215         printf("Main deploy: %d meters\n",
216                ao_config.main_deploy);
217 }
218
219 void
220 ao_config_main_deploy_set(void) __reentrant
221 {
222         ao_cmd_decimal();
223         if (ao_cmd_status != ao_cmd_success)
224                 return;
225         _ao_config_edit_start();
226         ao_config.main_deploy = ao_cmd_lex_i;
227         _ao_config_edit_finish();
228 }
229
230 #if HAS_ACCEL
231 void
232 ao_config_accel_calibrate_show(void) __reentrant
233 {
234         printf("Accel cal +1g: %d -1g: %d\n",
235                ao_config.accel_plus_g, ao_config.accel_minus_g);
236 }
237
238 #define ACCEL_CALIBRATE_SAMPLES 1024
239 #define ACCEL_CALIBRATE_SHIFT   10
240
241 static int16_t
242 ao_config_accel_calibrate_auto(char *orientation) __reentrant
243 {
244         uint16_t        i;
245         int32_t         accel_total;
246         uint8_t         cal_data_ring;
247
248         printf("Orient antenna %s and press a key...", orientation);
249         flush();
250         (void) getchar();
251         puts("\r\n"); flush();
252         puts("Calibrating..."); flush();
253         i = ACCEL_CALIBRATE_SAMPLES;
254         accel_total = 0;
255         cal_data_ring = ao_sample_data;
256         while (i) {
257                 ao_sleep(DATA_TO_XDATA(&ao_sample_data));
258                 while (i && cal_data_ring != ao_sample_data) {
259                         int16_t accel = ao_data_accel(&ao_data_ring[cal_data_ring]);
260                         printf ("accel %d\n", accel);
261                         accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
262                         cal_data_ring = ao_data_ring_next(cal_data_ring);
263                         i--;
264                 }
265         }
266         return accel_total >> ACCEL_CALIBRATE_SHIFT;
267 }
268
269 void
270 ao_config_accel_calibrate_set(void) __reentrant
271 {
272         int16_t up, down;
273         ao_cmd_decimal();
274         if (ao_cmd_status != ao_cmd_success)
275                 return;
276         if (ao_cmd_lex_i == 0) {
277                 up = ao_config_accel_calibrate_auto("up");
278                 down = ao_config_accel_calibrate_auto("down");
279         } else {
280                 up = ao_cmd_lex_i;
281                 ao_cmd_decimal();
282                 if (ao_cmd_status != ao_cmd_success)
283                         return;
284                 down = ao_cmd_lex_i;
285         }
286         if (up >= down) {
287                 printf("Invalid accel: up (%d) down (%d)\n",
288                        up, down);
289                 return;
290         }
291         _ao_config_edit_start();
292         ao_config.accel_plus_g = up;
293         ao_config.accel_minus_g = down;
294         _ao_config_edit_finish();
295 }
296 #endif /* HAS_ACCEL */
297
298 void
299 ao_config_apogee_delay_show(void) __reentrant
300 {
301         printf("Apogee delay: %d seconds\n",
302                ao_config.apogee_delay);
303 }
304
305 void
306 ao_config_apogee_delay_set(void) __reentrant
307 {
308         ao_cmd_decimal();
309         if (ao_cmd_status != ao_cmd_success)
310                 return;
311         _ao_config_edit_start();
312         ao_config.apogee_delay = ao_cmd_lex_i;
313         _ao_config_edit_finish();
314 }
315
316 void
317 ao_config_apogee_lockout_show(void) __reentrant
318 {
319         printf ("Apogee lockout: %d seconds\n",
320                 ao_config.apogee_lockout);
321 }
322
323 void
324 ao_config_apogee_lockout_set(void) __reentrant
325 {
326         ao_cmd_decimal();
327         if (ao_cmd_status != ao_cmd_success)
328                 return;
329         _ao_config_edit_start();
330         ao_config.apogee_lockout = ao_cmd_lex_i;
331         _ao_config_edit_finish();
332 }
333
334 #endif /* HAS_FLIGHT */
335
336 #if HAS_RADIO
337 void
338 ao_config_radio_cal_show(void) __reentrant
339 {
340         printf("Radio cal: %ld\n", ao_config.radio_cal);
341 }
342
343 void
344 ao_config_radio_cal_set(void) __reentrant
345 {
346         ao_cmd_decimal();
347         if (ao_cmd_status != ao_cmd_success)
348                 return;
349         _ao_config_edit_start();
350         ao_config.radio_cal = ao_cmd_lex_u32;
351         ao_config_set_radio();
352         _ao_config_edit_finish();
353 }
354 #endif
355
356 #if HAS_LOG
357 void
358 ao_config_log_show(void) __reentrant
359 {
360         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
361 }
362
363 void
364 ao_config_log_set(void) __reentrant
365 {
366         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
367         uint16_t        config = (uint16_t) (ao_storage_config >> 10);
368
369         ao_cmd_decimal();
370         if (ao_cmd_status != ao_cmd_success)
371                 return;
372         if (ao_log_present())
373                 printf("Storage must be empty before changing log size\n");
374         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
375                 printf("Flight log size must be multiple of %d kB\n", block);
376         else if (ao_cmd_lex_i > config)
377                 printf("Flight log max %d kB\n", config);
378         else {
379                 _ao_config_edit_start();
380                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
381                 _ao_config_edit_finish();
382         }
383 }
384 #endif /* HAS_LOG */
385
386 #if HAS_IGNITE
387 void
388 ao_config_ignite_mode_show(void) __reentrant
389 {
390         printf("Ignite mode: %d\n", ao_config.ignite_mode);
391 }
392
393 void
394 ao_config_ignite_mode_set(void) __reentrant
395 {
396         ao_cmd_decimal();
397         if (ao_cmd_status != ao_cmd_success)
398                 return;
399         _ao_config_edit_start();
400         ao_config.ignite_mode = ao_cmd_lex_i;
401         _ao_config_edit_finish();
402 }
403 #endif
404
405 #if HAS_ACCEL
406 void
407 ao_config_pad_orientation_show(void) __reentrant
408 {
409         printf("Pad orientation: %d\n", ao_config.pad_orientation);
410 }
411
412 void
413 ao_config_pad_orientation_set(void) __reentrant
414 {
415         ao_cmd_decimal();
416         if (ao_cmd_status != ao_cmd_success)
417                 return;
418         _ao_config_edit_start();
419         ao_cmd_lex_i &= 1;
420         if (ao_config.pad_orientation != ao_cmd_lex_i) {
421                 uint16_t t;
422                 t = ao_config.accel_plus_g;
423                 ao_config.accel_plus_g = 0x7fff - ao_config.accel_minus_g;
424                 ao_config.accel_minus_g = 0x7fff - t;
425         }
426         ao_config.pad_orientation = ao_cmd_lex_i;
427         _ao_config_edit_finish();
428 }
429 #endif
430
431 #if HAS_RADIO
432 void
433 ao_config_radio_enable_show(void) __reentrant
434 {
435         printf("Radio enable: %d\n", ao_config.radio_enable);
436 }
437
438 void
439 ao_config_radio_enable_set(void) __reentrant
440 {
441         ao_cmd_decimal();
442         if (ao_cmd_status != ao_cmd_success)
443                 return;
444         _ao_config_edit_start();
445         ao_config.radio_enable = ao_cmd_lex_i;
446         _ao_config_edit_finish();
447 }
448 #endif /* HAS_RADIO */
449         
450 #if HAS_AES
451 void
452 ao_config_key_show(void) __reentrant
453 {
454         uint8_t i;
455         printf("AES key: ");
456         for (i = 0; i < AO_AES_LEN; i++)
457                 printf ("%02x", ao_config.aes_key[i]);
458         printf("\n");
459 }
460
461 void
462 ao_config_key_set(void) __reentrant
463 {
464         uint8_t i;
465
466         _ao_config_edit_start();
467         for (i = 0; i < AO_AES_LEN; i++) {
468                 ao_cmd_hexbyte();
469                 if (ao_cmd_status != ao_cmd_success)
470                         break;
471                 ao_config.aes_key[i] = ao_cmd_lex_i;
472         }
473         _ao_config_edit_finish();
474 }
475 #endif
476
477 struct ao_config_var {
478         __code char     *str;
479         void            (*set)(void) __reentrant;
480         void            (*show)(void) __reentrant;
481 };
482
483 static void
484 ao_config_help(void) __reentrant;
485
486 static void
487 ao_config_show(void) __reentrant;
488
489 static void
490 ao_config_write(void) __reentrant;
491
492 __code struct ao_config_var ao_config_vars[] = {
493 #if HAS_FLIGHT
494         { "m <meters>\0Main deploy (m)",
495           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
496         { "d <delay>\0Apogee delay (s)",
497           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
498         { "L <seconds>\0Apogee detect lockout (s)",
499           ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
500 #endif /* HAS_FLIGHT */
501 #if HAS_RADIO
502         { "F <freq>\0Frequency (kHz)",
503           ao_config_frequency_set, ao_config_frequency_show },
504         { "c <call>\0Callsign (8 char max)",
505           ao_config_callsign_set,       ao_config_callsign_show },
506         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
507           ao_config_radio_enable_set, ao_config_radio_enable_show },
508 #endif /* HAS_RADIO */
509 #if HAS_ACCEL
510         { "a <+g> <-g>\0Accel calib (0 for auto)",
511           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
512 #endif /* HAS_ACCEL */
513 #if HAS_RADIO
514         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
515           ao_config_radio_cal_set,      ao_config_radio_cal_show },
516 #endif /* HAS_RADIO */
517 #if HAS_LOG
518         { "l <size>\0Flight log size (kB)",
519           ao_config_log_set,            ao_config_log_show },
520 #endif
521 #if HAS_IGNITE
522         { "i <0 dual, 1 apogee, 2 main>\0Set igniter mode",
523           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
524 #endif
525 #if HAS_ACCEL
526         { "o <0 antenna up, 1 antenna down>\0Set pad orientation",
527           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
528 #endif
529 #if HAS_AES
530         { "k <32 hex digits>\0Set AES encryption key",
531           ao_config_key_set, ao_config_key_show },
532 #endif
533         { "s\0Show",
534           ao_config_show,               0 },
535 #if HAS_EEPROM
536         { "w\0Write to eeprom",
537           ao_config_write,              0 },
538 #endif
539         { "?\0Help",
540           ao_config_help,               0 },
541         { 0, 0, 0 }
542 };
543
544 void
545 ao_config_set(void)
546 {
547         char    c;
548         uint8_t cmd;
549         void (*__xdata func)(void) __reentrant;
550
551         ao_cmd_white();
552         c = ao_cmd_lex_c;
553         ao_cmd_lex();
554         func = 0;
555         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
556                 if (ao_config_vars[cmd].str[0] == c) {
557                         (*ao_config_vars[cmd].set)();
558                         return;
559                 }
560         ao_cmd_status = ao_cmd_syntax_error;
561 }
562
563 static void
564 ao_config_help(void) __reentrant
565 {
566         uint8_t cmd;
567         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
568                 printf("%-20s %s\n",
569                        ao_config_vars[cmd].str,
570                        ao_config_vars[cmd].str+1+
571                        strlen(ao_config_vars[cmd].str));
572 }
573
574 static void
575 ao_config_show(void) __reentrant
576 {
577         uint8_t cmd;
578         ao_config_get();
579         printf("Config version: %d.%d\n",
580                ao_config.major, ao_config.minor);
581         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
582                 if (ao_config_vars[cmd].show)
583                         (*ao_config_vars[cmd].show)();
584 }
585
586 #if HAS_EEPROM
587 static void
588 ao_config_write(void) __reentrant
589 {
590         uint8_t saved = 0;
591         ao_mutex_get(&ao_config_mutex);
592         if (ao_config_dirty) {
593                 _ao_config_put();
594                 ao_config_dirty = 0;
595                 saved = 1;
596         }
597         ao_mutex_put(&ao_config_mutex);
598         if (saved)
599                 puts("Saved");
600         else
601                 puts("Nothing to save");
602 }
603 #endif
604
605 __code struct ao_cmds ao_config_cmds[] = {
606         { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
607         { 0, NULL },
608 };
609
610 void
611 ao_config_init(void)
612 {
613         ao_cmd_register(&ao_config_cmds[0]);
614 }