altos: Light pyro charges simultaneously if so configured
[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 uint8_t
174 ao_pyro_check(void)
175 {
176         struct ao_pyro  *pyro;
177         uint8_t         p, any_waiting;
178         uint16_t        fire = 0;
179         
180         any_waiting = 0;
181         for (p = 0; p < AO_PYRO_NUM; p++) {
182                 pyro = &ao_config.pyro[p];
183
184                 /* Ignore igniters which have already fired
185                  */
186                 if (pyro->fired)
187                         continue;
188
189                 /* Ignore disabled igniters
190                  */
191                 if (!pyro->flags)
192                         continue;
193
194                 any_waiting = 1;
195                 /* Check pyro state to see if it should fire
196                  */
197                 if (!pyro->delay_done) {
198                         if (!ao_pyro_ready(pyro))
199                                 continue;
200
201                         /* If there's a delay set, then remember when
202                          * it expires
203                          */
204                         if (pyro->flags & ao_pyro_delay) {
205                                 pyro->delay_done = ao_time() + pyro->delay;
206                                 if (!pyro->delay_done)
207                                         pyro->delay_done = 1;
208                         }
209                 }
210
211                 /* Check to see if we're just waiting for
212                  * the delay to expire
213                  */
214                 if (pyro->delay_done) {
215                         if ((int16_t) (ao_time() - pyro->delay_done) < 0)
216                                 continue;
217                 }
218
219                 fire |= (1 << p);
220         }
221
222         if (fire) {
223                 for (p = 0; p < AO_PYRO_NUM; p++) {
224                         if (fire & (1 << p))
225                                 ao_pyro_pin_set(p, 1);
226                 }
227                 ao_delay(AO_MS_TO_TICKS(50));
228                 for (p = 0; p < AO_PYRO_NUM; p++) {
229                         if (fire & (1 << p)) {
230                                 ao_pyro_pin_set(p, 0);
231                                 ao_config.pyro[p].fired = 1;
232                                 ao_pyro_fired |= (1 << p);
233                         }
234                 }
235                 ao_delay(AO_MS_TO_TICKS(50));
236         }
237
238         return any_waiting;
239 }
240
241 #define NO_VALUE        0xff
242
243 #define AO_PYRO_NAME_LEN        3
244
245 #if !DISABLE_HELP
246 #define ENABLE_HELP 1
247 #endif
248
249 #if ENABLE_HELP
250 #define HELP(s) (s)
251 #else
252 #define HELP(s)
253 #endif
254
255 const struct {
256         char                    name[AO_PYRO_NAME_LEN];
257         enum ao_pyro_flag       flag;
258         uint8_t                 offset;
259 #if ENABLE_HELP
260         char                    *help;
261 #endif
262 } ao_pyro_values[] = {
263         { "a<", ao_pyro_accel_less,     offsetof(struct ao_pyro, accel_less), HELP("accel less (m/ss * 16)") },
264         { "a>", ao_pyro_accel_greater,  offsetof(struct ao_pyro, accel_greater), HELP("accel greater (m/ss * 16)") },
265
266         { "s<", ao_pyro_speed_less,     offsetof(struct ao_pyro, speed_less), HELP("speed less (m/s * 16)") },
267         { "s>", ao_pyro_speed_greater,  offsetof(struct ao_pyro, speed_greater), HELP("speed greater (m/s * 16)") },
268
269         { "h<", ao_pyro_height_less,    offsetof(struct ao_pyro, height_less), HELP("height less (m)") },
270         { "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), HELP("height greater (m)") },
271
272 #if HAS_ORIENT
273         { "o<", ao_pyro_orient_less,    offsetof(struct ao_pyro, orient_less), HELP("orient less (deg)") },
274         { "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), HELP("orient greater (deg)")  },
275 #endif
276
277         { "t<", ao_pyro_time_less,      offsetof(struct ao_pyro, time_less), HELP("time less (s * 100)") },
278         { "t>", ao_pyro_time_greater,   offsetof(struct ao_pyro, time_greater), HELP("time greater (s * 100)")  },
279
280         { "f<", ao_pyro_state_less,     offsetof(struct ao_pyro, state_less), HELP("state less") },
281         { "f>=",ao_pyro_state_greater_or_equal, offsetof(struct ao_pyro, state_greater_or_equal), HELP("state greater or equal")  },
282
283         { "A", ao_pyro_ascending,       NO_VALUE, HELP("ascending") },
284         { "D", ao_pyro_descending,      NO_VALUE, HELP("descending") },
285
286         { "m", ao_pyro_after_motor,     offsetof(struct ao_pyro, motor), HELP("after motor") },
287
288         { "d", ao_pyro_delay,           offsetof(struct ao_pyro, delay), HELP("delay before firing (s * 100)") },
289         { "", ao_pyro_none,             NO_VALUE, HELP(NULL) },
290 };
291
292 #define NUM_PYRO_VALUES (sizeof ao_pyro_values / sizeof ao_pyro_values[0])
293
294 #ifndef AO_FLIGHT_TEST
295 static void
296 ao_pyro(void)
297 {
298         uint8_t         any_waiting;
299
300         ao_config_get();
301         while (ao_flight_state < ao_flight_boost)
302                 ao_sleep(&ao_flight_state);
303
304         for (;;) {
305                 ao_alarm(AO_MS_TO_TICKS(100));
306                 ao_sleep(&ao_pyro_wakeup);
307                 ao_clear_alarm();
308                 if (ao_flight_state >= ao_flight_landed)
309                         break;
310                 any_waiting = ao_pyro_check();
311                 if (!any_waiting)
312                         break;
313         }
314         ao_exit();
315 }
316
317 __xdata struct ao_task ao_pyro_task;
318
319
320 static void
321 ao_pyro_print_name(uint8_t v)
322 {
323         const char *s = ao_pyro_values[v].name;
324         printf ("%s%s", s, "   " + strlen(s));
325 }
326
327 #if ENABLE_HELP
328 static void
329 ao_pyro_help(void)
330 {
331         uint8_t v;
332         for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
333                 ao_pyro_print_name(v);
334                 if (ao_pyro_values[v].offset != NO_VALUE)
335                         printf ("<n> ");
336                 else
337                         printf ("    ");
338                 printf ("%s\n", ao_pyro_values[v].help);
339         }
340 }
341 #endif
342
343 void
344 ao_pyro_show(void)
345 {
346         uint8_t         p;
347         uint8_t         v;
348         struct ao_pyro  *pyro;
349
350         printf ("Pyro-count: %d\n", AO_PYRO_NUM);
351         for (p = 0; p < AO_PYRO_NUM; p++) {
352                 printf ("Pyro %2d: ", p);
353                 pyro = &ao_config.pyro[p];
354                 if (!pyro->flags) {
355                         printf ("<disabled>\n");
356                         continue;
357                 }
358                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
359                         if (!(pyro->flags & ao_pyro_values[v].flag))
360                                 continue;
361                         ao_pyro_print_name(v);
362                         if (ao_pyro_values[v].offset != NO_VALUE) {
363                                 int16_t value;
364
365                                 value = *((int16_t *) ((char *) pyro + ao_pyro_values[v].offset));
366                                 printf ("%6d ", value);
367                         } else {
368                                 printf ("       ");
369                         }
370                 }
371                 printf ("\n");
372         }
373 }
374
375 void
376 ao_pyro_set(void)
377 {
378         uint8_t p;
379         struct ao_pyro pyro_tmp;
380         char    name[AO_PYRO_NAME_LEN];
381         uint8_t c;
382         uint8_t v;
383
384         ao_cmd_white();
385
386 #if ENABLE_HELP
387         switch (ao_cmd_lex_c) {
388         case '?':
389                 ao_pyro_help();
390                 return;
391         }
392 #endif
393
394         ao_cmd_decimal();
395         if (ao_cmd_status != ao_cmd_success)
396                 return;
397         p = ao_cmd_lex_i;
398         if (p < 0 || AO_PYRO_NUM <= p) {
399                 printf ("invalid pyro channel %d\n", p);
400                 return;
401         }
402         pyro_tmp.flags = 0;
403         for (;;) {
404                 ao_cmd_white();
405                 if (ao_cmd_lex_c == '\n')
406                         break;
407
408                 for (c = 0; c < AO_PYRO_NAME_LEN - 1; c++) {
409                         if (ao_cmd_is_white())
410                                 break;
411                         name[c] = ao_cmd_lex_c;
412                         ao_cmd_lex();
413                 }
414                 name[c] = '\0';
415                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
416                         if (!strcmp (ao_pyro_values[v].name, name))
417                                 break;
418                 }
419                 if (ao_pyro_values[v].flag == ao_pyro_none) {
420                         printf ("invalid pyro field %s\n", name);
421                         ao_cmd_status = ao_cmd_syntax_error;
422                         return;
423                 }
424                 pyro_tmp.flags |= ao_pyro_values[v].flag;
425                 if (ao_pyro_values[v].offset != NO_VALUE) {
426                         ao_cmd_decimal();
427                         if (ao_cmd_status != ao_cmd_success)
428                                 return;
429                         *((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
430                 }
431         }
432         _ao_config_edit_start();
433         ao_config.pyro[p] = pyro_tmp;
434         _ao_config_edit_finish();
435 }
436
437 static void
438 ao_pyro_manual(void)
439 {
440         ao_cmd_white();
441         if (!ao_match_word("DoIt"))
442                 return;
443         ao_cmd_white();
444         ao_cmd_decimal();
445         if (ao_cmd_lex_i < 0 || AO_PYRO_NUM <= ao_cmd_lex_i)
446                 return;
447         ao_pyro_fire(ao_cmd_lex_i);
448
449 }
450
451 const struct ao_cmds ao_pyro_cmds[] = {
452         { ao_pyro_manual,       "P DoIt <n>\0Fire igniter" },
453         { 0, NULL }
454 };
455
456 void
457 ao_pyro_init(void)
458 {
459 #if AO_PYRO_NUM > 0
460         ao_enable_output(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0, 0);
461 #endif
462 #if AO_PYRO_NUM > 1
463         ao_enable_output(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1, 0);
464 #endif
465 #if AO_PYRO_NUM > 2
466         ao_enable_output(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2, 0);
467 #endif
468 #if AO_PYRO_NUM > 3
469         ao_enable_output(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3, 0);
470 #endif
471 #if AO_PYRO_NUM > 4
472         ao_enable_output(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4, 0);
473 #endif
474 #if AO_PYRO_NUM > 5
475         ao_enable_output(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5, 0);
476 #endif
477 #if AO_PYRO_NUM > 6
478         ao_enable_output(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6, 0);
479 #endif
480 #if AO_PYRO_NUM > 7
481         ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7, 0);
482 #endif
483         ao_cmd_register(&ao_pyro_cmds[0]);
484         ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
485 }
486 #endif