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