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