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