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