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