altos: Program default flight log max value for new boards
[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 __xdata uint8_t ao_config_loaded;
22 __xdata 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_FLIGHT_LOG_MAX        ((uint32_t) 192 * (uint32_t) 1024)
31
32 #if HAS_EEPROM
33 static void
34 _ao_config_put(void)
35 {
36         ao_storage_setup();
37         ao_storage_erase(ao_storage_config);
38         ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
39         ao_log_write_erase(0);
40         ao_storage_flush();
41 }
42
43 void
44 ao_config_put(void)
45 {
46         ao_mutex_get(&ao_config_mutex);
47         _ao_config_put();
48         ao_mutex_put(&ao_config_mutex);
49 }
50 #endif
51
52 static void
53 _ao_config_get(void)
54 {
55         if (ao_config_loaded)
56                 return;
57 #if HAS_EEPROM
58         ao_storage_setup();
59         ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
60 #endif
61         if (ao_config.major != AO_CONFIG_MAJOR) {
62                 ao_config.major = AO_CONFIG_MAJOR;
63                 ao_config.minor = AO_CONFIG_MINOR;
64                 ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
65                 ao_config.radio_channel = AO_CONFIG_DEFAULT_RADIO_CHANNEL;
66                 ao_config.accel_plus_g = 0;
67                 ao_config.accel_minus_g = 0;
68                 memset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
69                 memcpy(&ao_config.callsign, AO_CONFIG_DEFAULT_CALLSIGN,
70                        sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
71                 ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
72                 ao_config.radio_cal = ao_radio_cal;
73                 ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
74                 ao_config_dirty = 1;
75         }
76         if (ao_config.minor < AO_CONFIG_MINOR) {
77                 /* Fixups for minor version 1 */
78                 if (ao_config.minor < 1)
79                         ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
80                 /* Fixups for minor version 2 */
81                 if (ao_config.minor < 2) {
82                         ao_config.accel_plus_g = 0;
83                         ao_config.accel_minus_g = 0;
84                 }
85                 /* Fixups for minor version 3 */
86                 if (ao_config.minor < 3)
87                         ao_config.radio_cal = ao_radio_cal;
88                 /* Fixups for minor version 4 */
89                 if (ao_config.minor < 4)
90                         ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
91                 ao_config.minor = AO_CONFIG_MINOR;
92                 ao_config_dirty = 1;
93         }
94         ao_config_loaded = 1;
95 }
96
97 void
98 ao_config_get(void)
99 {
100         ao_mutex_get(&ao_config_mutex);
101         _ao_config_get();
102         ao_mutex_put(&ao_config_mutex);
103 }
104
105 void
106 ao_config_callsign_show(void)
107 {
108         printf ("Callsign: \"%s\"\n", ao_config.callsign);
109 }
110
111 void
112 ao_config_callsign_set(void) __reentrant
113 {
114         uint8_t c;
115         char callsign[AO_MAX_CALLSIGN + 1];
116
117         ao_cmd_white();
118         c = 0;
119         while (ao_cmd_lex_c != '\n') {
120                 if (c < AO_MAX_CALLSIGN)
121                         callsign[c++] = ao_cmd_lex_c;
122                 else
123                         ao_cmd_status = ao_cmd_lex_error;
124                 ao_cmd_lex();
125         }
126         if (ao_cmd_status != ao_cmd_success)
127                 return;
128         ao_mutex_get(&ao_config_mutex);
129         _ao_config_get();
130         while (c < AO_MAX_CALLSIGN + 1)
131                 callsign[c++] = '\0';
132         memcpy(&ao_config.callsign, &callsign,
133                AO_MAX_CALLSIGN + 1);
134         ao_config_dirty = 1;
135         ao_mutex_put(&ao_config_mutex);
136         ao_config_callsign_show();
137 }
138
139 void
140 ao_config_radio_channel_show(void) __reentrant
141 {
142         uint32_t        freq = 434550L + ao_config.radio_channel * 100L;
143         uint16_t        mhz = freq / 1000L;
144         uint16_t        khz = freq % 1000L;
145
146         printf("Radio channel: %d (%d.%03dMHz)\n",
147                ao_config.radio_channel, mhz, khz);
148 }
149
150 void
151 ao_config_radio_channel_set(void) __reentrant
152 {
153         ao_cmd_decimal();
154         if (ao_cmd_status != ao_cmd_success)
155                 return;
156         ao_mutex_get(&ao_config_mutex);
157         _ao_config_get();
158         ao_config.radio_channel = ao_cmd_lex_i;
159         ao_config_dirty = 1;
160         ao_mutex_put(&ao_config_mutex);
161         ao_config_radio_channel_show();
162         ao_radio_recv_abort();
163 }
164
165 #if HAS_ADC
166
167 void
168 ao_config_main_deploy_show(void) __reentrant
169 {
170         printf("Main deploy: %d meters (%d feet)\n",
171                ao_config.main_deploy,
172                (int16_t) ((int32_t) ao_config.main_deploy * 328 / 100));
173 }
174
175 void
176 ao_config_main_deploy_set(void) __reentrant
177 {
178         ao_cmd_decimal();
179         if (ao_cmd_status != ao_cmd_success)
180                 return;
181         ao_mutex_get(&ao_config_mutex);
182         _ao_config_get();
183         ao_config.main_deploy = ao_cmd_lex_i;
184         ao_config_dirty = 1;
185         ao_mutex_put(&ao_config_mutex);
186         ao_config_main_deploy_show();
187 }
188
189 void
190 ao_config_accel_calibrate_show(void) __reentrant
191 {
192         printf("Accel cal +1g: %d -1g: %d\n",
193                ao_config.accel_plus_g, ao_config.accel_minus_g);
194 }
195
196 #define ACCEL_CALIBRATE_SAMPLES 1024
197 #define ACCEL_CALIBRATE_SHIFT   10
198
199 static int16_t
200 ao_config_accel_calibrate_auto(char *orientation) __reentrant
201 {
202         uint16_t        i;
203         int32_t         accel_total;
204         uint8_t         cal_adc_ring;
205
206         printf("Orient %s and press a key...", orientation);
207         flush();
208         (void) getchar();
209         puts("\r\n"); flush();
210         puts("Calibrating..."); flush();
211         i = ACCEL_CALIBRATE_SAMPLES;
212         accel_total = 0;
213         cal_adc_ring = ao_flight_adc;
214         while (i) {
215                 ao_sleep(DATA_TO_XDATA(&ao_flight_adc));
216                 while (i && cal_adc_ring != ao_flight_adc) {
217                         accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
218                         cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
219                         i--;
220                 }
221         }
222         return accel_total >> ACCEL_CALIBRATE_SHIFT;
223 }
224
225 void
226 ao_config_accel_calibrate_set(void) __reentrant
227 {
228         int16_t up, down;
229         ao_cmd_decimal();
230         if (ao_cmd_status != ao_cmd_success)
231                 return;
232         if (ao_cmd_lex_i == 0) {
233                 up = ao_config_accel_calibrate_auto("antenna up");
234                 down = ao_config_accel_calibrate_auto("antenna down");
235         } else {
236                 up = ao_cmd_lex_i;
237                 ao_cmd_decimal();
238                 if (ao_cmd_status != ao_cmd_success)
239                         return;
240                 down = ao_cmd_lex_i;
241         }
242         if (up >= down) {
243                 printf("Invalid accel calibration: antenna up (%d) should be less than antenna down (%d)\n",
244                        up, down);
245                 return;
246         }
247         ao_mutex_get(&ao_config_mutex);
248         _ao_config_get();
249         ao_config.accel_plus_g = up;
250         ao_config.accel_minus_g = down;
251         ao_config_dirty = 1;
252         ao_mutex_put(&ao_config_mutex);
253         ao_config_accel_calibrate_show();
254 }
255
256 void
257 ao_config_apogee_delay_show(void) __reentrant
258 {
259         printf("Apogee delay: %d seconds\n",
260                ao_config.apogee_delay);
261 }
262
263 void
264 ao_config_apogee_delay_set(void) __reentrant
265 {
266         ao_cmd_decimal();
267         if (ao_cmd_status != ao_cmd_success)
268                 return;
269         ao_mutex_get(&ao_config_mutex);
270         _ao_config_get();
271         ao_config.apogee_delay = ao_cmd_lex_i;
272         ao_config_dirty = 1;
273         ao_mutex_put(&ao_config_mutex);
274         ao_config_apogee_delay_show();
275 }
276
277 #endif /* HAS_ADC */
278
279 void
280 ao_config_radio_cal_show(void) __reentrant
281 {
282         printf("Radio cal: %ld\n", ao_config.radio_cal);
283 }
284
285 void
286 ao_config_radio_cal_set(void) __reentrant
287 {
288         ao_cmd_decimal();
289         if (ao_cmd_status != ao_cmd_success)
290                 return;
291         ao_mutex_get(&ao_config_mutex);
292         _ao_config_get();
293         ao_config.radio_cal = ao_cmd_lex_u32;
294         ao_config_dirty = 1;
295         ao_mutex_put(&ao_config_mutex);
296         ao_config_radio_cal_show();
297 }
298
299 #if HAS_EEPROM
300 void
301 ao_config_log_show(void) __reentrant
302 {
303         printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
304 }
305
306 void
307 ao_config_log_set(void) __reentrant
308 {
309         uint16_t        block = (uint16_t) (ao_storage_block >> 10);
310         uint16_t        config = (uint16_t) (ao_storage_config >> 10);
311
312         ao_cmd_decimal();
313         if (ao_cmd_status != ao_cmd_success)
314                 return;
315         if (ao_log_present())
316                 printf("Storage must be empty before changing log size\n");
317         else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
318                 printf("Flight log size must be multiple of %d kB\n", block);
319         else if (ao_cmd_lex_i > config)
320                 printf("Flight log max %d kB\n", config);
321         else {
322                 ao_mutex_get(&ao_config_mutex);
323                 _ao_config_get();
324                 ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
325                 ao_config_dirty = 1;
326                 ao_mutex_put(&ao_config_mutex);
327                 ao_config_log_show();
328         }
329 }
330 #endif /* HAS_EEPROM */
331
332 struct ao_config_var {
333         char            cmd;
334         void            (*set)(void) __reentrant;
335         void            (*show)(void) __reentrant;
336         const char      *help;
337 };
338
339 static void
340 ao_config_help(void) __reentrant;
341
342 static void
343 ao_config_show(void) __reentrant;
344
345 static void
346 ao_config_write(void) __reentrant;
347
348 __code struct ao_config_var ao_config_vars[] = {
349 #if HAS_ADC
350         { 'm',  ao_config_main_deploy_set,      ao_config_main_deploy_show,
351                 "m <meters>  Set height above launch for main deploy (in meters)" },
352         { 'd',  ao_config_apogee_delay_set,     ao_config_apogee_delay_show,
353                 "d <delay>   Set apogee igniter delay (in seconds)" },
354 #endif /* HAS_ADC */
355         { 'r',  ao_config_radio_channel_set,    ao_config_radio_channel_show,
356                 "r <channel> Set radio channel (freq = 434.550 + channel * .1)" },
357         { 'c',  ao_config_callsign_set,         ao_config_callsign_show,
358                 "c <call>    Set callsign broadcast in each packet (8 char max)" },
359 #if HAS_ADC
360         { 'a',  ao_config_accel_calibrate_set,  ao_config_accel_calibrate_show,
361                 "a <+g> <-g> Set accelerometer calibration (0 for auto)" },
362 #endif /* HAS_ADC */
363         { 'f',  ao_config_radio_cal_set,        ao_config_radio_cal_show,
364                 "f <cal>     Set radio calibration value (cal = rf/(xtal/2^16))" },
365 #if HAS_EEPROM
366         { 'l',  ao_config_log_set,              ao_config_log_show,
367                 "l <size>    Set flight log size in kB" },
368 #endif
369         { 's',  ao_config_show,                 ao_config_show,
370                 "s           Show current config values" },
371 #if HAS_EEPROM
372         { 'w',  ao_config_write,                ao_config_write,
373                 "w           Write current values to eeprom" },
374 #endif
375         { '?',  ao_config_help,                 ao_config_help,
376                 "?           Show available config variables" },
377         { 0,    ao_config_help, ao_config_help,
378                 NULL },
379 };
380
381 void
382 ao_config_set(void)
383 {
384         char    c;
385         uint8_t cmd;
386         void (*__xdata func)(void) __reentrant;
387
388         ao_cmd_white();
389         c = ao_cmd_lex_c;
390         ao_cmd_lex();
391         func = 0;
392         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
393                 if (ao_config_vars[cmd].cmd == c) {
394                         func = ao_config_vars[cmd].set;
395                         break;
396                 }
397         if (func)
398                 (*func)();
399         else
400                 ao_cmd_status = ao_cmd_syntax_error;
401 }
402
403 static void
404 ao_config_help(void) __reentrant
405 {
406         uint8_t cmd;
407         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
408                 puts (ao_config_vars[cmd].help);
409 }
410
411 static void
412 ao_config_show(void) __reentrant
413 {
414         uint8_t cmd;
415         printf("Config version: %d.%d\n",
416                ao_config.major, ao_config.minor);
417         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
418                 if (ao_config_vars[cmd].show != ao_config_vars[cmd].set)
419                         (*ao_config_vars[cmd].show)();
420 }
421
422 #if HAS_EEPROM
423 static void
424 ao_config_write(void) __reentrant
425 {
426         uint8_t saved = 0;
427         ao_mutex_get(&ao_config_mutex);
428         if (ao_config_dirty) {
429                 _ao_config_put();
430                 ao_config_dirty = 0;
431                 saved = 1;
432         }
433         ao_mutex_put(&ao_config_mutex);
434         if (saved)
435                 puts("Saved");
436         else
437                 puts("Nothing to save");
438 }
439 #endif
440
441 __code struct ao_cmds ao_config_cmds[] = {
442         { 'c',  ao_config_set,  "c <var> <value>                    Set config variable (? for help, s to show)" },
443         { '\0', ao_config_set, NULL },
444 };
445
446 void
447 ao_config_init(void)
448 {
449         ao_cmd_register(&ao_config_cmds[0]);
450 }