Correct radio frequency shown in config display.
[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      "KD7SGQ"
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         memcpy(&ao_config.callsign, &callsign,
91                AO_MAX_CALLSIGN + 1);
92         ao_config_dirty = 1;
93         ao_mutex_put(&ao_config_mutex);
94         ao_config_callsign_show();
95 }
96
97 void
98 ao_config_radio_channel_show(void) __reentrant
99 {
100         uint32_t        freq = 434550L + ao_config.radio_channel * 100L;
101         uint16_t        mhz = freq / 1000L;
102         uint16_t        khz = freq % 1000L;
103
104         printf("Radio channel: %d (%d.%03dMHz)\n",
105                ao_config.radio_channel, mhz, khz);
106 }
107
108 void
109 ao_config_radio_channel_set(void) __reentrant
110 {
111         ao_cmd_decimal();
112         if (ao_cmd_status != ao_cmd_success)
113                 return;
114         ao_mutex_get(&ao_config_mutex);
115         _ao_config_get();
116         ao_config.radio_channel = ao_cmd_lex_i;
117         ao_config_dirty = 1;
118         ao_mutex_put(&ao_config_mutex);
119         ao_config_radio_channel_show();
120 }
121
122 void
123 ao_config_main_deploy_show(void) __reentrant
124 {
125         printf("Main deploy set to %d meters (%d feet)\n",
126                ao_config.main_deploy,
127                (int16_t) ((int32_t) ao_config.main_deploy * 328 / 100));
128 }
129
130 void
131 ao_config_main_deploy_set(void) __reentrant
132 {
133         ao_cmd_decimal();
134         if (ao_cmd_status != ao_cmd_success)
135                 return;
136         ao_mutex_get(&ao_config_mutex);
137         _ao_config_get();
138         ao_config.main_deploy = ao_cmd_lex_i;
139         ao_config_dirty = 1;
140         ao_mutex_put(&ao_config_mutex);
141         ao_config_main_deploy_show();
142 }
143
144 void
145 ao_config_accel_zero_g_show(void) __reentrant
146 {
147         printf("Accel zero g point set to %d\n",
148                ao_config.accel_zero_g);
149 }
150
151 #define ZERO_G_SAMPLES  1000
152
153 static int16_t
154 ao_config_accel_zero_g_auto(void) __reentrant
155 {
156         uint16_t        i;
157         int32_t         accel_total;
158         uint8_t         cal_adc_ring;
159
160         puts("Calibrating accelerometer..."); flush();
161         i = ZERO_G_SAMPLES;
162         accel_total = 0;
163         cal_adc_ring = ao_adc_head;
164         while (i) {
165                 ao_sleep(&ao_adc_ring);
166                 while (i && cal_adc_ring != ao_adc_head) {
167                         accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
168                         cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
169                         i--;
170                 }
171         }
172         return (int16_t) (accel_total / ZERO_G_SAMPLES);
173 }
174 void
175 ao_config_accel_zero_g_set(void) __reentrant
176 {
177         ao_cmd_decimal();
178         if (ao_cmd_status != ao_cmd_success)
179                 return;
180         if (ao_cmd_lex_i == 0)
181                 ao_cmd_lex_i = ao_config_accel_zero_g_auto();
182         ao_mutex_get(&ao_config_mutex);
183         _ao_config_get();
184         ao_config.accel_zero_g = ao_cmd_lex_i;
185         ao_config_dirty = 1;
186         ao_mutex_put(&ao_config_mutex);
187         ao_config_accel_zero_g_show();
188 }
189
190 struct ao_config_var {
191         uint8_t         cmd;
192         void            (*set)(void) __reentrant;
193         void            (*show)(void) __reentrant;
194         const char      *help;
195 };
196
197 void
198 ao_config_help(void) __reentrant;
199
200 void
201 ao_config_show(void) __reentrant;
202
203 void
204 ao_config_write(void) __reentrant;
205
206 __code struct ao_config_var ao_config_vars[] = {
207         { 'm',  ao_config_main_deploy_set,      ao_config_main_deploy_show,
208                 "m <meters>  Set height above launch for main deploy (in meters)" },
209         { 'a',  ao_config_accel_zero_g_set,     ao_config_accel_zero_g_show,
210                 "a <value>   Set accelerometer zero g point (0 for auto)" },
211         { 'r',  ao_config_radio_channel_set,    ao_config_radio_channel_show,
212                 "r <channel> Set radio channel (freq = 434.550 + channel * .1)" },
213         { 'c',  ao_config_callsign_set,         ao_config_callsign_show,
214                 "c <call>    Set callsign broadcast in each packet (8 char max)" },
215         { 's',  ao_config_show,                 ao_config_show,
216                 "s           Show current config values" },
217         { 'w',  ao_config_write,                ao_config_write,
218                 "w           Write current values to eeprom" },
219         { '?',  ao_config_help,                 ao_config_help,
220                 "?           Show available config variables" },
221         { 0,    ao_config_main_deploy_set,      ao_config_main_deploy_show,
222                 NULL },
223 };
224
225 void
226 ao_config_set(void)
227 {
228         uint8_t c;
229         uint8_t cmd;
230         void (*__xdata func)(void) __reentrant;
231
232         ao_cmd_white();
233         c = ao_cmd_lex_c;
234         ao_cmd_lex();
235         func = 0;
236         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
237                 if (ao_config_vars[cmd].cmd == c) {
238                         func = ao_config_vars[cmd].set;
239                         break;
240                 }
241         if (func)
242                 (*func)();
243         else
244                 ao_cmd_status = ao_cmd_syntax_error;
245 }
246
247 void
248 ao_config_help(void) __reentrant
249 {
250         uint8_t cmd;
251         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
252                 puts (ao_config_vars[cmd].help);
253 }
254
255 void
256 ao_config_show(void) __reentrant
257 {
258         uint8_t cmd;
259         for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
260                 if (ao_config_vars[cmd].show != ao_config_vars[cmd].set)
261                         (*ao_config_vars[cmd].show)();
262 }
263
264 void
265 ao_config_write(void) __reentrant
266 {
267         ao_mutex_get(&ao_config_mutex);
268         if (ao_config_dirty) {
269                 _ao_config_put();
270                 ao_config_dirty = 0;
271                 printf("Saved\n");
272         }
273         ao_mutex_put(&ao_config_mutex);
274 }
275
276 __code struct ao_cmds ao_config_cmds[] = {
277         { 'c',  ao_config_set,  "c <var> <value>                    Set config variable (? for help, s to show)" },
278         { '\0', ao_config_set, NULL },
279 };
280
281 void
282 ao_config_init(void)
283 {
284         ao_cmd_register(&ao_config_cmds[0]);
285 }