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