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