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