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