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