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