altos: Enable pyro channel control in telepyro
[fw/altos] / src / core / ao_pyro.c
1 /*
2  * Copyright © 2012 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 #include <ao_pyro.h>
20 #include <ao_sample.h>
21 #include <ao_flight.h>
22
23 #if IS_COMPANION
24 #include <ao_companion.h>
25 #define ao_accel ao_companion_command.accel
26 #define ao_speed ao_companion_command.speed
27 #define ao_height ao_companion_command.height
28 #define ao_flight_state ao_companion_command.flight_state
29 #define ao_motor_number ao_companion_command.motor_number
30 #endif
31
32 #define ao_lowbit(x)    ((x) & (-x))
33
34 /*
35  * Given a pyro structure, figure out
36  * if the current flight state satisfies all
37  * of the requirements
38  */
39 static uint8_t
40 ao_pyro_ready(struct ao_pyro *pyro)
41 {
42         enum ao_pyro_flag flag, flags;
43
44         flags = pyro->flags;
45         while (flags != ao_pyro_none) {
46                 flag = ao_lowbit(flags);
47                 flags &= ~flag;
48                 switch (flag) {
49
50                 case ao_pyro_accel_less:
51                         if (ao_accel <= pyro->accel_less)
52                                 continue;
53                         break;
54                 case ao_pyro_accel_greater:
55                         if (ao_accel >= pyro->accel_greater)
56                                 continue;
57                         break;
58
59
60                 case ao_pyro_speed_less:
61                         if (ao_speed <= pyro->speed_less)
62                                 continue;
63                         break;
64                 case ao_pyro_speed_greater:
65                         if (ao_speed >= pyro->speed_greater)
66                                 continue;
67                         break;
68
69                 case ao_pyro_height_less:
70                         if (ao_height <= pyro->height_less)
71                                 continue;
72                         break;
73                 case ao_pyro_height_greater:
74                         if (ao_height >= pyro->height_greater)
75                                 continue;
76                         break;
77
78 #if HAS_ORIENT
79                 case ao_pyro_orient_less:
80                         if (ao_orient <= pyro->orient_less)
81                                 continue;
82                         break;
83                 case ao_pyro_orient_greater:
84                         if (ao_orient >= pyro->orient_greater)
85                                 continue;
86                         break;
87 #endif
88
89                 case ao_pyro_time_less:
90                         if ((int16_t) (ao_time() - ao_boost_tick) <= pyro->time_less)
91                                 continue;
92                         break;
93                 case ao_pyro_time_greater:
94                         if ((int16_t) (ao_time() - ao_boost_tick) >= pyro->time_greater)
95                                 continue;
96                         break;
97
98                 case ao_pyro_ascending:
99                         if (ao_speed > 0)
100                                 continue;
101                         break;
102                 case ao_pyro_descending:
103                         if (ao_speed < 0)
104                                 continue;
105                         break;
106
107                 case ao_pyro_after_motor:
108                         if (ao_motor_number == pyro->motor)
109                                 continue;
110                         break;
111
112                 case ao_pyro_delay:
113                         /* handled separately */
114                         continue;
115
116                 default:
117                         continue;
118                 }
119                 return FALSE;
120         }
121         return TRUE;
122 }
123
124 #define ao_pyro_fire_port(port, bit, pin) do {  \
125                 ao_gpio_set(port, bit, pin, 1); \
126                 ao_delay(AO_MS_TO_TICKS(50));   \
127                 ao_gpio_set(port, bit, pin, 0); \
128         } while (0)
129
130
131 static void
132 ao_pyro_fire(uint8_t p)
133 {
134         switch (p) {
135 #if AO_PYRO_NUM > 0
136         case 0: ao_pyro_fire_port(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0); break;
137 #endif
138 #if AO_PYRO_NUM > 1
139         case 1: ao_pyro_fire_port(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1); break;
140 #endif
141 #if AO_PYRO_NUM > 2
142         case 2: ao_pyro_fire_port(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2); break;
143 #endif
144 #if AO_PYRO_NUM > 3
145         case 3: ao_pyro_fire_port(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3); break;
146 #endif
147 #if AO_PYRO_NUM > 4
148         case 4: ao_pyro_fire_port(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4); break;
149 #endif
150 #if AO_PYRO_NUM > 5
151         case 5: ao_pyro_fire_port(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5); break;
152 #endif
153 #if AO_PYRO_NUM > 6
154         case 6: ao_pyro_fire_port(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6); break;
155 #endif
156 #if AO_PYRO_NUM > 7
157         case 7: ao_pyro_fire_port(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7); break;
158 #endif
159         default: break;
160         }
161         ao_delay(AO_MS_TO_TICKS(50));
162 }
163
164 uint8_t ao_pyro_wakeup;
165
166 static void
167 ao_pyro(void)
168 {
169         uint8_t         p;
170         struct ao_pyro  *pyro;
171
172         ao_config_get();
173         while (ao_flight_state < ao_flight_boost)
174                 ao_sleep(&ao_flight_state);
175
176         for (;;) {
177                 ao_alarm(AO_MS_TO_TICKS(100));
178                 ao_sleep(&ao_pyro_wakeup);
179                 ao_clear_alarm();
180                 for (p = 0; p < AO_PYRO_NUM; p++) {
181                         pyro = &ao_config.pyro[p];
182
183                         /* Ignore igniters which have already fired
184                          */
185                         if (pyro->fired)
186                                 continue;
187
188                         /* Ignore disabled igniters
189                          */
190                         if (!pyro->flags)
191                                 continue;
192
193                         /* Check pyro state to see if it shoule fire
194                          */
195                         if (!pyro->delay_done) {
196                                 if (!ao_pyro_ready(pyro))
197                                         continue;
198
199                                 /* If there's a delay set, then remember when
200                                  * it expires
201                                  */
202                                 if (pyro->flags & ao_pyro_delay)
203                                         pyro->delay_done = ao_time() + pyro->delay;
204                         }
205
206                         /* Check to see if we're just waiting for
207                          * the delay to expire
208                          */
209                         if (pyro->delay_done) {
210                                 if ((int16_t) (ao_time() - pyro->delay_done) < 0)
211                                         continue;
212                         }
213
214                         ao_pyro_fire(p);
215                 }
216         }
217 }
218
219 __xdata struct ao_task ao_pyro_task;
220
221 #define NO_VALUE        0xff
222
223 #define AO_PYRO_NAME_LEN        3
224
225 #if !DISABLE_HELP
226 #define ENABLE_HELP 1
227 #endif
228
229 #if ENABLE_HELP
230 #define HELP(s) (s)
231 #else
232 #define HELP(s)
233 #endif
234
235 const struct {
236         char                    name[AO_PYRO_NAME_LEN];
237         enum ao_pyro_flag       flag;
238         uint8_t                 offset;
239 #if ENABLE_HELP
240         char                    *help;
241 #endif
242 } ao_pyro_values[] = {
243         { "a<", ao_pyro_accel_less,     offsetof(struct ao_pyro, accel_less), HELP("accel less (m/ss * 16)") },
244         { "a>", ao_pyro_accel_greater,  offsetof(struct ao_pyro, accel_greater), HELP("accel greater (m/ss * 16)") },
245
246         { "s<", ao_pyro_speed_less,     offsetof(struct ao_pyro, speed_less), HELP("speed less (m/s * 16)") },
247         { "s>", ao_pyro_speed_greater,  offsetof(struct ao_pyro, speed_greater), HELP("speed greater (m/s * 16)") },
248
249         { "h<", ao_pyro_height_less,    offsetof(struct ao_pyro, height_less), HELP("height less (m)") },
250         { "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), HELP("height greater (m)") },
251
252 #if HAS_ORIENT
253         { "o<", ao_pyro_orient_less,    offsetof(struct ao_pyro, orient_less), HELP("orient less (deg)") },
254         { "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), HELP("orient greater (deg)")  },
255 #endif
256
257         { "t<", ao_pyro_time_less,      offsetof(struct ao_pyro, time_less), HELP("time less (s * 100)") },
258         { "t>", ao_pyro_time_greater,   offsetof(struct ao_pyro, time_greater), HELP("time greater (s * 100)")  },
259
260         { "A", ao_pyro_ascending,       NO_VALUE, HELP("ascending") },
261         { "D", ao_pyro_descending,      NO_VALUE, HELP("descending") },
262
263         { "m", ao_pyro_after_motor,     offsetof(struct ao_pyro, motor), HELP("after motor") },
264
265         { "d", ao_pyro_delay,           offsetof(struct ao_pyro, delay), HELP("delay before firing (s * 100)") },
266         { "", ao_pyro_none,             NO_VALUE, HELP(NULL) },
267 };
268
269 static void
270 ao_pyro_print_name(uint8_t v)
271 {
272         const char *s = ao_pyro_values[v].name;
273         printf ("%s%s", s, "   " + strlen(s));
274 }
275
276 #if ENABLE_HELP
277 static void
278 ao_pyro_help(void)
279 {
280         uint8_t v;
281         for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
282                 ao_pyro_print_name(v);
283                 if (ao_pyro_values[v].offset != NO_VALUE)
284                         printf ("<n> ");
285                 else
286                         printf ("    ");
287                 printf ("%s\n", ao_pyro_values[v].help);
288         }
289 }
290 #endif
291
292 void
293 ao_pyro_show(void)
294 {
295         uint8_t         p;
296         uint8_t         v;
297         struct ao_pyro  *pyro;
298
299         printf ("Pyro-count: %d\n", AO_PYRO_NUM);
300         for (p = 0; p < AO_PYRO_NUM; p++) {
301                 printf ("Pyro %2d: ", p);
302                 pyro = &ao_config.pyro[p];
303                 if (!pyro->flags) {
304                         printf ("<disabled>\n");
305                         continue;
306                 }
307                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
308                         if (!(pyro->flags & ao_pyro_values[v].flag))
309                                 continue;
310                         ao_pyro_print_name(v);
311                         if (ao_pyro_values[v].offset != NO_VALUE) {
312                                 int16_t value;
313
314                                 value = *((int16_t *) ((char *) pyro + ao_pyro_values[v].offset));
315                                 printf ("%6d ", value);
316                         } else {
317                                 printf ("       ");
318                         }
319                 }
320                 printf ("\n");
321         }
322 }
323
324 void
325 ao_pyro_set(void)
326 {
327         uint8_t p;
328         struct ao_pyro pyro_tmp;
329         char    name[AO_PYRO_NAME_LEN];
330         uint8_t c;
331         uint8_t v;
332
333         ao_cmd_white();
334
335 #if ENABLE_HELP
336         switch (ao_cmd_lex_c) {
337         case '?':
338                 ao_pyro_help();
339                 return;
340         }
341 #endif
342
343         ao_cmd_decimal();
344         if (ao_cmd_status != ao_cmd_success)
345                 return;
346         p = ao_cmd_lex_i;
347         if (p < 0 || AO_PYRO_NUM <= p) {
348                 printf ("invalid pyro channel %d\n", p);
349                 return;
350         }
351         pyro_tmp.flags = 0;
352         for (;;) {
353                 ao_cmd_white();
354                 if (ao_cmd_lex_c == '\n')
355                         break;
356
357                 for (c = 0; c < AO_PYRO_NAME_LEN - 1; c++) {
358                         if (ao_cmd_is_white())
359                                 break;
360                         name[c] = ao_cmd_lex_c;
361                         ao_cmd_lex();
362                 }
363                 name[c] = '\0';
364                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
365                         if (!strcmp (ao_pyro_values[v].name, name))
366                                 break;
367                 }
368                 if (ao_pyro_values[v].flag == ao_pyro_none) {
369                         printf ("invalid pyro field %s\n", name);
370                         ao_cmd_status = ao_cmd_syntax_error;
371                         return;
372                 }
373                 pyro_tmp.flags |= ao_pyro_values[v].flag;
374                 if (ao_pyro_values[v].offset != NO_VALUE) {
375                         ao_cmd_decimal();
376                         if (ao_cmd_status != ao_cmd_success)
377                                 return;
378                         *((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
379                 }
380         }
381         _ao_config_edit_start();
382         ao_config.pyro[p] = pyro_tmp;
383         _ao_config_edit_finish();
384 }
385
386 static void
387 ao_pyro_manual(void)
388 {
389         ao_cmd_white();
390         if (!ao_match_word("DoIt"))
391                 return;
392         ao_cmd_white();
393         ao_cmd_decimal();
394         if (ao_cmd_lex_i < 0 || AO_PYRO_NUM <= ao_cmd_lex_i)
395                 return;
396         ao_pyro_fire(ao_cmd_lex_i);
397
398 }
399
400 const struct ao_cmds ao_pyro_cmds[] = {
401         { ao_pyro_manual,       "P DoIt <n>\0Fire igniter" },
402         { 0, NULL }
403 };
404
405 void
406 ao_pyro_init(void)
407 {
408 #if AO_PYRO_NUM > 0
409         ao_enable_output(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0, 0);
410 #endif
411 #if AO_PYRO_NUM > 1
412         ao_enable_output(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1, 0);
413 #endif
414 #if AO_PYRO_NUM > 2
415         ao_enable_output(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2, 0);
416 #endif
417 #if AO_PYRO_NUM > 3
418         ao_enable_output(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3, 0);
419 #endif
420 #if AO_PYRO_NUM > 4
421         ao_enable_output(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4, 0);
422 #endif
423 #if AO_PYRO_NUM > 5
424         ao_enable_output(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5, 0);
425 #endif
426 #if AO_PYRO_NUM > 6
427         ao_enable_output(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6, 0);
428 #endif
429 #if AO_PYRO_NUM > 7
430         ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7, 0);
431 #endif
432         ao_cmd_register(&ao_pyro_cmds[0]);
433         ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
434 }