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