ec2b61f6b7c1367549a0601c766e9d5380607c85
[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
20 __xdata struct ao_config ao_config;
21 __pdata uint8_t ao_config_loaded;
22 __pdata uint8_t ao_config_dirty;
23 __xdata uint8_t ao_config_mutex;
24
25 #define AO_CONFIG_DEFAULT_MAIN_DEPLOY   250
26 #define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
27 #define AO_CONFIG_DEFAULT_CALLSIGN      "N0CALL"
28 #define AO_CONFIG_DEFAULT_ACCEL_ZERO_G  16000
29 #define AO_CONFIG_DEFAULT_APOGEE_DELAY  0
30 #define AO_CONFIG_DEFAULT_IGNITE_MODE   AO_IGNITE_MODE_DUAL
31 #define AO_CONFIG_DEFAULT_PAD_ORIENTATION       AO_PAD_ORIENTATION_ANTENNA_UP
32 #if HAS_EEPROM
33 #ifndef USE_INTERNAL_FLASH
34 #error Please define USE_INTERNAL_FLASH
35 #endif
36 #endif
37 #if USE_INTERNAL_FLASH
38 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ao_storage_config
39 #else
40 #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ((uint32_t) 192 * (uint32_t) 1024)
41 #endif
42
43 #if HAS_EEPROM
44 static void
45 _ao_config_put(void)
46 {
47         ao_storage_setup();
48         ao_storage_erase(ao_storage_config);
49         ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
50 #if HAS_FLIGHT
51         ao_log_write_erase(0);
52 #endif
53         ao_storage_flush();
54 }
55
56 void
57 ao_config_put(void)
58 {
59         ao_mutex_get(&ao_config_mutex);
60         _ao_config_put();
61         ao_mutex_put(&ao_config_mutex);
62 }
63 #endif
64
65 static void
66 _ao_config_get(void)
67 {
68         if (ao_config_loaded)
69                 return;
70 #if HAS_EEPROM
71         ao_storage_setup();
72         ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
73 #endif
74         if (ao_config.major != AO_CONFIG_MAJOR) {
75                 ao_config.major = AO_CONFIG_MAJOR;
76                 ao_config.minor = 0;
77                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
78                 ao_config.radio_channel = AO_CONFIG_DEFAULT_RADIO_CHANNEL;
79                 memset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
80                 memcpy(&ao_config.callsign, AO_CONFIG_DEFAULT_CALLSIGN,
81                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
82         }
83         if (ao_config.minor < AO_CONFIG_MINOR) {
84                 /* Fixups for minor version 1 */
85                 if (ao_config.minor < 1)
86                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
87                 /* Fixups for minor version 2 */
88                 if (ao_config.minor < 2) {
89                         ao_config.accel_plus_g = 0;
90                         ao_config.accel_minus_g = 0;
91                 }
92                 /* Fixups for minor version 3 */
93                 if (ao_config.minor < 3)
94                         ao_config.radio_cal = ao_radio_cal;
95                 /* Fixups for minor version 4 */
96                 if (ao_config.minor < 4)
97                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
98                 /* Fixupes for minor version 5 */
99                 if (ao_config.minor < 5)
100                         ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
101                 if (ao_config.minor < 6)
102                         ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
103                 if (ao_config.minor < 7)
104                         ao_config.radio_setting = ao_config.radio_cal;
105                 if (ao_config.minor < 8)
106                         ao_config.radio_enable = TRUE;
107                 ao_config.minor = AO_CONFIG_MINOR;
108                 ao_config_dirty = 1;
109         }
110         ao_config_loaded = 1;
111 }
112
113 static void
114 _ao_config_edit_start(void)
115 {
116         ao_mutex_get(&ao_config_mutex);
117         _ao_config_get();
118 }
119
120 static void
121 _ao_config_edit_finish(void)
122 {
123         ao_config_dirty = 1;
124         ao_mutex_put(&ao_config_mutex);
125 }
126
127 void
128 ao_config_get(void)
129 {
130         _ao_config_edit_start();
131         ao_mutex_put(&ao_config_mutex);
132 }
133
134 void
135 ao_config_callsign_show(void)
136 {
137         printf ("Callsign: \"%s\"\n", ao_config.callsign);
138 }
139
140 void
141 ao_config_callsign_set(void) __reentrant
142 {
143         uint8_t c;
144         static __xdata char callsign[AO_MAX_CALLSIGN + 1];
145
146         memset(callsign, '\0', sizeof callsign);
147         ao_cmd_white();
148         c = 0;
149         while (ao_cmd_lex_c != '\n') {
150                 if (c < AO_MAX_CALLSIGN)
151                         callsign[c++] = ao_cmd_lex_c;
152                 else
153                         ao_cmd_status = ao_cmd_lex_error;
154                 ao_cmd_lex();
155         }
156         if (ao_cmd_status != ao_cmd_success)
157                 return;
158         _ao_config_edit_start();
159         memcpy(&ao_config.callsign, &callsign,
160                AO_MAX_CALLSIGN + 1);
161         _ao_config_edit_finish();
162 }
163
164 void
165 ao_config_radio_channel_show(void) __reentrant
166 {
167         printf("Radio channel: %d\n",
168                ao_config.radio_channel);
169 }
170
171 void
172 ao_config_radio_channel_set(void) __reentrant
173 {
174         ao_cmd_decimal();
175         if (ao_cmd_status != ao_cmd_success)
176                 return;
177         _ao_config_edit_start();
178         ao_config.radio_channel = ao_cmd_lex_i;
179         _ao_config_edit_finish();
180         ao_radio_recv_abort();
181 }
182
183 #if HAS_ADC
184
185 void
186 ao_config_main_deploy_show(void) __reentrant
187 {
188         printf("Main deploy: %d meters\n",
189                ao_config.main_deploy);
190 }
191
192 void
193 ao_config_main_deploy_set(void) __reentrant
194 {
195         ao_cmd_decimal();
196         if (ao_cmd_status != ao_cmd_success)
197                 return;
198         _ao_config_edit_start();
199         ao_config.main_deploy = ao_cmd_lex_i;
200         _ao_config_edit_finish();
201 }
202
203 #if HAS_ACCEL
204 void
205 ao_config_accel_calibrate_show(void) __reentrant
206 {
207         printf("Accel cal +1g: %d -1g: %d\n",
208                ao_config.accel_plus_g, ao_config.accel_minus_g);
209 }
210
211 #define ACCEL_CALIBRATE_SAMPLES 1024
212 #define ACCEL_CALIBRATE_SHIFT   10
213
214 static int16_t
215 ao_config_accel_calibrate_auto(char *orientation) __reentrant
216 {
217         uint16_t        i;
218         int32_t         accel_total;
219         uint8_t         cal_adc_ring;
220
221         printf("Orient antenna %s and press a key...", orientation);
222         flush();
223         (void) getchar();
224         puts("\r\n"); flush();
225         puts("Calibrating..."); flush();
226         i = ACCEL_CALIBRATE_SAMPLES;
227         accel_total = 0;
228         cal_adc_ring = ao_sample_adc;
229         while (i) {
230                 ao_sleep(DATA_TO_XDATA(&ao_sample_adc));
231                 while (i && cal_adc_ring != ao_sample_adc) {
232                         accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
233                         cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
234                         i--;
235                 }
236         }
237         return accel_total >> ACCEL_CALIBRATE_SHIFT;
238 }
239
240 void
241 ao_config_accel_calibrate_set(void) __reentrant
242 {
243         int16_t up, down;
244         ao_cmd_decimal();
245         if (ao_cmd_status != ao_cmd_success)
246                 return;
247         if (ao_cmd_lex_i == 0) {
248                 up = ao_config_accel_calibrate_auto("up");
249                 down = ao_config_accel_calibrate_auto("down");
250         } else {
251                 up = ao_cmd_lex_i;
252                 ao_cmd_decimal();
253                 if (ao_cmd_status != ao_cmd_success)
254                         return;
255                 down = ao_cmd_lex_i;
256         }
257         if (up >= down) {
258                 printf("Invalid accel: up (%d) down (%d)\n",
259                        up, down);
260                 return;
261         }
262         _ao_config_edit_start();
263         ao_config.accel_plus_g = up;
264         ao_config.accel_minus_g = down;
265         _ao_config_edit_finish();
266 }
267 #endif /* HAS_ACCEL */
268
269 void
270 ao_config_apogee_delay_show(void) __reentrant
271 {
272         printf("Apogee delay: %d seconds\n",
273                ao_config.apogee_delay);
274 }
275
276 void
277 ao_config_apogee_delay_set(void) __reentrant
278 {
279         ao_cmd_decimal();
280         if (ao_cmd_status != ao_cmd_success)
281                 return;
282         _ao_config_edit_start();
283         ao_config.apogee_delay = ao_cmd_lex_i;
284         _ao_config_edit_finish();
285 }
286
287 #endif /* HAS_ADC */
288
289 void
290 ao_config_radio_cal_show(void) __reentrant
291 {
292         printf("Radio cal: %ld\n", ao_config.radio_cal);
293 }
294
295 void
296 ao_config_radio_cal_set(void) __reentrant
297 {
298         ao_cmd_decimal();
299         if (ao_cmd_status != ao_cmd_success)
300                 return;
301         _ao_config_edit_start();
302         ao_config.radio_setting = ao_config.radio_cal = ao_cmd_lex_u32;
303         _ao_config_edit_finish();
304 }
305
306 #if HAS_EEPROM && HAS_FLIGHT
307 void
308 ao_config_log_show(void) __reentrant
309 {
310         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
311 }
312
313 void
314 ao_config_log_set(void) __reentrant
315 {
316         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
317         uint16_t        config = (uint16_t) (ao_storage_config >> 10);
318
319         ao_cmd_decimal();
320         if (ao_cmd_status != ao_cmd_success)
321                 return;
322         if (ao_log_present())
323                 printf("Storage must be empty before changing log size\n");
324         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
325                 printf("Flight log size must be multiple of %d kB\n", block);
326         else if (ao_cmd_lex_i > config)
327                 printf("Flight log max %d kB\n", config);
328         else {
329                 _ao_config_edit_start();
330                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
331                 _ao_config_edit_finish();
332         }
333 }
334 #endif /* HAS_EEPROM && HAS_FLIGHT */
335
336 #if HAS_IGNITE
337 void
338 ao_config_ignite_mode_show(void) __reentrant
339 {
340         printf("Ignite mode: %d\n", ao_config.ignite_mode);
341 }
342
343 void
344 ao_config_ignite_mode_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.ignite_mode = ao_cmd_lex_i;
351         _ao_config_edit_finish();
352 }
353 #endif
354
355 #if HAS_ACCEL
356 void
357 ao_config_pad_orientation_show(void) __reentrant
358 {
359         printf("Pad orientation: %d\n", ao_config.pad_orientation);
360 }
361
362 void
363 ao_config_pad_orientation_set(void) __reentrant
364 {
365         ao_cmd_decimal();
366         if (ao_cmd_status != ao_cmd_success)
367                 return;
368         _ao_config_edit_start();
369         ao_cmd_lex_i &= 1;
370         if (ao_config.pad_orientation != ao_cmd_lex_i) {
371                 uint16_t t;
372                 t = ao_config.accel_plus_g;
373                 ao_config.accel_plus_g = 0x7fff - ao_config.accel_minus_g;
374                 ao_config.accel_minus_g = 0x7fff - t;
375         }
376         ao_config.pad_orientation = ao_cmd_lex_i;
377         _ao_config_edit_finish();
378 }
379 #endif
380
381 void
382 ao_config_radio_setting_show(void) __reentrant
383 {
384         printf("Radio setting: %ld\n", ao_config.radio_setting);
385 }
386
387 void
388 ao_config_radio_setting_set(void) __reentrant
389 {
390         ao_cmd_decimal();
391         if (ao_cmd_status != ao_cmd_success)
392                 return;
393         _ao_config_edit_start();
394         ao_config.radio_setting = ao_cmd_lex_u32;
395         ao_config.radio_channel = 0;
396         _ao_config_edit_finish();
397         ao_radio_recv_abort();
398 }
399
400 void
401 ao_config_radio_enable_show(void) __reentrant
402 {
403         printf("Radio enable: %d\n", ao_config.radio_enable);
404 }
405
406 void
407 ao_config_radio_enable_set(void) __reentrant
408 {
409         ao_cmd_decimal();
410         if (ao_cmd_status != ao_cmd_success)
411                 return;
412         _ao_config_edit_start();
413         ao_config.radio_enable = ao_cmd_lex_i;
414         _ao_config_edit_finish();
415 }
416         
417 struct ao_config_var {
418         __code char     *str;
419         void            (*set)(void) __reentrant;
420         void            (*show)(void) __reentrant;
421 };
422
423 static void
424 ao_config_help(void) __reentrant;
425
426 static void
427 ao_config_show(void) __reentrant;
428
429 static void
430 ao_config_write(void) __reentrant;
431
432 __code struct ao_config_var ao_config_vars[] = {
433 #if HAS_ADC
434         { "m <meters>\0Main deploy (in meters)",
435           ao_config_main_deploy_set,    ao_config_main_deploy_show, },
436         { "d <delay>\0Apogee delay (in seconds)",
437           ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
438 #endif /* HAS_ADC */
439         { "r <channel>\0Radio channel (freq = 434.550 + chan * .1)",
440           ao_config_radio_channel_set,  ao_config_radio_channel_show },
441         { "c <call>\0Callsign (8 char max)",
442           ao_config_callsign_set,       ao_config_callsign_show },
443         { "R <setting>\0Radio freq control (freq = 434.550 * setting/cal)",
444           ao_config_radio_setting_set,  ao_config_radio_setting_show },
445         { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
446           ao_config_radio_enable_set, ao_config_radio_enable_show },
447 #if HAS_ACCEL
448         { "a <+g> <-g>\0Accel calib (0 for auto)",
449           ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
450 #endif /* HAS_ACCEL */
451         { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
452           ao_config_radio_cal_set,      ao_config_radio_cal_show },
453 #if HAS_EEPROM && HAS_FLIGHT
454         { "l <size>\0Flight log size in kB",
455           ao_config_log_set,            ao_config_log_show },
456 #endif
457 #if HAS_IGNITE
458         { "i <0 dual, 1 apogee, 2 main>\0Set igniter mode",
459           ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
460 #endif
461 #if HAS_ACCEL
462         { "o <0 antenna up, 1 antenna down>\0Set pad orientation",
463           ao_config_pad_orientation_set,ao_config_pad_orientation_show },
464 #endif
465         { "s\0Show",
466           ao_config_show,               0 },
467 #if HAS_EEPROM
468         { "w\0Write to eeprom",
469           ao_config_write,              0 },
470 #endif
471         { "?\0Help",
472           ao_config_help,               0 },
473         { 0, 0, 0 }
474 };
475
476 void
477 ao_config_set(void)
478 {
479         char    c;
480         uint8_t cmd;
481         void (*__xdata func)(void) __reentrant;
482
483         ao_cmd_white();
484         c = ao_cmd_lex_c;
485         ao_cmd_lex();
486         func = 0;
487         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
488                 if (ao_config_vars[cmd].str[0] == c) {
489                         (*ao_config_vars[cmd].set)();
490                         return;
491                 }
492         ao_cmd_status = ao_cmd_syntax_error;
493 }
494
495 static void
496 ao_config_help(void) __reentrant
497 {
498         uint8_t cmd;
499         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
500                 printf("%-20s %s\n",
501                        ao_config_vars[cmd].str,
502                        ao_config_vars[cmd].str+1+strlen(ao_config_vars[cmd].str));
503 }
504
505 static void
506 ao_config_show(void) __reentrant
507 {
508         uint8_t cmd;
509         printf("Config version: %d.%d\n",
510                ao_config.major, ao_config.minor);
511         for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
512                 if (ao_config_vars[cmd].show)
513                         (*ao_config_vars[cmd].show)();
514 }
515
516 #if HAS_EEPROM
517 static void
518 ao_config_write(void) __reentrant
519 {
520         uint8_t saved = 0;
521         ao_mutex_get(&ao_config_mutex);
522         if (ao_config_dirty) {
523                 _ao_config_put();
524                 ao_config_dirty = 0;
525                 saved = 1;
526         }
527         ao_mutex_put(&ao_config_mutex);
528         if (saved)
529                 puts("Saved");
530         else
531                 puts("Nothing to save");
532 }
533 #endif
534
535 __code struct ao_cmds ao_config_cmds[] = {
536         { ao_config_set,        "c <var> <value>\0Set config variable (? for help, s to show)" },
537         { 0, NULL },
538 };
539
540 void
541 ao_config_init(void)
542 {
543         ao_cmd_register(&ao_config_cmds[0]);
544 }