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