altos: Add arbitrary pyro channel support
[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 #define ao_lowbit(x)    ((x) & (-x))
24
25 /*
26  * Given a pyro structure, figure out
27  * if the current flight state satisfies all
28  * of the requirements
29  */
30 static uint8_t
31 ao_pyro_ready(struct ao_pyro *pyro)
32 {
33         enum ao_pyro_flag flag, flags;
34
35         flags = pyro->flags;
36         while (flags != ao_pyro_none) {
37                 flag = ao_lowbit(flags);
38                 flags &= ~flag;
39                 switch (flag) {
40
41                 case ao_pyro_accel_less:
42                         if (ao_accel <= pyro->accel_less)
43                                 continue;
44                         break;
45                 case ao_pyro_accel_greater:
46                         if (ao_accel >= pyro->accel_greater)
47                                 continue;
48                         break;
49
50
51                 case ao_pyro_speed_less:
52                         if (ao_speed <= pyro->speed_less)
53                                 continue;
54                         break;
55                 case ao_pyro_speed_greater:
56                         if (ao_speed >= pyro->speed_greater)
57                                 continue;
58                         break;
59
60                 case ao_pyro_height_less:
61                         if (ao_height <= pyro->height_less)
62                                 continue;
63                         break;
64                 case ao_pyro_height_greater:
65                         if (ao_height >= pyro->height_greater)
66                                 continue;
67                         break;
68
69                 case ao_pyro_orient_less:
70 //                      if (ao_orient <= pyro->orient_less)
71                                 continue;
72                         break;
73                 case ao_pyro_orient_greater:
74 //                      if (ao_orient >= pyro->orient_greater)
75                                 continue;
76                         break;
77
78                 case ao_pyro_time_less:
79                         if ((int16_t) (ao_time() - ao_boost_tick) <= pyro->time_less)
80                                 continue;
81                         break;
82                 case ao_pyro_time_greater:
83                         if ((int16_t) (ao_time() - ao_boost_tick) >= pyro->time_greater)
84                                 continue;
85                         break;
86
87                 case ao_pyro_ascending:
88                         if (ao_speed > 0)
89                                 continue;
90                         break;
91                 case ao_pyro_descending:
92                         if (ao_speed < 0)
93                                 continue;
94                         break;
95
96                 case ao_pyro_after_motor:
97                         if (ao_motor_number == pyro->motor)
98                                 continue;
99                         break;
100
101                 case ao_pyro_delay:
102                         /* handled separately */
103                         continue;
104                         
105                 }
106                 return FALSE;
107         }
108         return TRUE;
109 }
110
111 #define ao_pyro_fire_port(port, pin) do {               \
112                 ao_gpio_set(port, pin, 1);      \
113                 ao_delay(AO_MS_TO_TICKS(50));   \
114                 ao_gpio_set(port, pin, 0);      \
115         } while (0)
116         
117
118 static void
119 ao_pyro_fire(uint8_t p)
120 {
121         switch (p) {
122 #if AO_PYRO_NUM > 0
123         case 0: ao_pyro_fire_port(AO_PYRO_PORT_0, AO_PYRO_PIN_0); break;
124 #endif
125 #if AO_PYRO_NUM > 1
126         case 1: ao_pyro_fire_port(AO_PYRO_PORT_1, AO_PYRO_PIN_1); break;
127 #endif
128 #if AO_PYRO_NUM > 2
129         case 2: ao_pyro_fire_port(AO_PYRO_PORT_2, AO_PYRO_PIN_2); break;
130 #endif
131 #if AO_PYRO_NUM > 3
132         case 3: ao_pyro_fire_port(AO_PYRO_PORT_3, AO_PYRO_PIN_3); break;
133 #endif
134 #if AO_PYRO_NUM > 4
135         case 4: ao_pyro_fire_port(AO_PYRO_PORT_4, AO_PYRO_PIN_4); break;
136 #endif
137 #if AO_PYRO_NUM > 5
138         case 5: ao_pyro_fire_port(AO_PYRO_PORT_5, AO_PYRO_PIN_5); break;
139 #endif
140 #if AO_PYRO_NUM > 6
141         case 6: ao_pyro_fire_port(AO_PYRO_PORT_6, AO_PYRO_PIN_6); break;
142 #endif
143 #if AO_PYRO_NUM > 7
144         case 7: ao_pyro_fire_port(AO_PYRO_PORT_7, AO_PYRO_PIN_7); break;
145 #endif
146         default: break;
147         }
148         ao_delay(AO_MS_TO_TICKS(50));
149 }
150
151 static void
152 ao_pyro(void)
153 {
154         uint8_t         p;
155         struct ao_pyro  *pyro;
156
157         ao_config_get();
158         while (ao_flight_state < ao_flight_boost)
159                 ao_sleep(&ao_flight_state);
160
161         for (;;) {
162                 ao_delay(AO_MS_TO_TICKS(100));
163                 for (p = 0; p < AO_PYRO_NUM; p++) {
164                         pyro = &ao_config.pyro[p];
165
166                         /* Ignore igniters which have already fired
167                          */
168                         if (pyro->fired)
169                                 continue;
170
171                         /* Ignore disabled igniters
172                          */
173                         if (!pyro->flags)
174                                 continue;
175
176                         /* Check pyro state to see if it shoule fire
177                          */
178                         if (!pyro->delay_done) {
179                                 if (!ao_pyro_ready(pyro))
180                                         continue;
181
182                                 /* If there's a delay set, then remember when
183                                  * it expires
184                                  */
185                                 if (pyro->flags & ao_pyro_delay)
186                                         pyro->delay_done = ao_time() + pyro->delay;
187                         }
188
189                         /* Check to see if we're just waiting for
190                          * the delay to expire
191                          */
192                         if (pyro->delay_done) {
193                                 if ((int16_t) (ao_time() - pyro->delay_done) < 0)
194                                         continue;
195                         }
196
197                         ao_pyro_fire(p);
198                 }
199         }
200 }
201
202 __xdata struct ao_task ao_pyro_task;
203
204 #define NO_VALUE        0xff
205
206 #define AO_PYRO_NAME_LEN        3
207
208 const struct {
209         char                    name[AO_PYRO_NAME_LEN];
210         enum ao_pyro_flag       flag;
211         uint8_t                 offset;
212         char                    *help;
213 } ao_pyro_values[] = {
214         { "a<", ao_pyro_accel_less,     offsetof(struct ao_pyro, accel_less), "accel less (m/ss * 16)" },
215         { "a>", ao_pyro_accel_greater,  offsetof(struct ao_pyro, accel_greater), "accel greater (m/ss * 16)" },
216
217         { "s<", ao_pyro_speed_less,     offsetof(struct ao_pyro, speed_less), "speed less (m/s * 16)" },
218         { "s>", ao_pyro_speed_greater,  offsetof(struct ao_pyro, speed_greater), "speed greater (m/s * 16)" },
219
220         { "h<", ao_pyro_height_less,    offsetof(struct ao_pyro, height_less), "height less (m)" },
221         { "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), "height greater (m)" },
222
223         { "o<", ao_pyro_orient_less,    offsetof(struct ao_pyro, orient_less), "orient less (deg)" },
224         { "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), "orient greater (deg)"  },
225
226         { "t<", ao_pyro_time_less,      offsetof(struct ao_pyro, time_less), "time less (s * 100)" },
227         { "t>", ao_pyro_time_greater,   offsetof(struct ao_pyro, time_greater), "time greater (s * 100)"  },
228
229         { "A", ao_pyro_ascending,       NO_VALUE, "ascending" },
230         { "D", ao_pyro_descending,      NO_VALUE, "descending" },
231         
232         { "m", ao_pyro_after_motor,     offsetof(struct ao_pyro, motor), "after motor" },
233
234         { "d", ao_pyro_delay,           offsetof(struct ao_pyro, delay), "delay before firing (s * 100)" },
235         { "", ao_pyro_none,             NO_VALUE, NULL },
236 };
237
238 static void
239 ao_pyro_print_name(uint8_t v)
240 {
241         const char *s = ao_pyro_values[v].name;
242         printf ("%s%s", s, "   " + strlen(s));
243 }
244
245 static void
246 ao_pyro_help(void)
247 {
248         uint8_t v;
249         for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
250                 ao_pyro_print_name(v);
251                 if (ao_pyro_values[v].offset != NO_VALUE)
252                         printf ("<n> ");
253                 else
254                         printf ("    ");
255                 printf ("%s\n", ao_pyro_values[v].help);
256         }
257 }
258               
259 void
260 ao_pyro_show(void)
261 {
262         uint8_t         p;
263         uint8_t         v;
264         struct ao_pyro  *pyro;
265
266         for (p = 0; p < AO_PYRO_NUM; p++) {
267                 printf ("Pyro %2d: ", p);
268                 pyro = &ao_config.pyro[p];
269                 if (!pyro->flags) {
270                         printf ("<disabled>\n");
271                         continue;
272                 }
273                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
274                         if (!(pyro->flags & ao_pyro_values[v].flag))
275                                 continue;
276                         ao_pyro_print_name(v);
277                         if (ao_pyro_values[v].offset != NO_VALUE) {
278                                 int16_t value;
279
280                                 value = *((int16_t *) ((char *) pyro + ao_pyro_values[v].offset));
281                                 printf ("%6d ", value);
282                         } else {
283                                 printf ("       ");
284                         }
285                 }
286                 printf ("\n");
287         }
288 }
289
290 void
291 ao_pyro_set(void)
292 {
293         uint8_t p;
294         struct ao_pyro *pyro;
295         struct ao_pyro pyro_tmp;
296         char    name[AO_PYRO_NAME_LEN];
297         uint8_t c;
298         uint8_t v;
299
300         ao_cmd_white();
301         switch (ao_cmd_lex_c) {
302         case '?':
303                 ao_pyro_help();
304                 return;
305         }
306
307         ao_cmd_decimal();
308         if (ao_cmd_status != ao_cmd_success)
309                 return;
310         p = ao_cmd_lex_i;
311         if (p < 0 || AO_PYRO_NUM <= p) {
312                 printf ("invalid pyro channel %d\n", p);
313                 return;
314         }
315         pyro_tmp.flags = 0;
316         for (;;) {
317                 ao_cmd_white();
318                 if (ao_cmd_lex_c == '\n')
319                         break;
320                 
321                 for (c = 0; c < AO_PYRO_NAME_LEN - 1; c++) {
322                         if (ao_cmd_is_white())
323                                 break;
324                         name[c] = ao_cmd_lex_c;
325                         ao_cmd_lex();
326                 }
327                 name[c] = '\0';
328                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
329                         if (!strcmp (ao_pyro_values[v].name, name))
330                                 break;
331                 }
332                 if (ao_pyro_values[v].flag == ao_pyro_none) {
333                         printf ("invalid pyro field %s\n", name);
334                         ao_cmd_status = ao_cmd_syntax_error;
335                         return;
336                 }
337                 pyro_tmp.flags |= ao_pyro_values[v].flag;
338                 if (ao_pyro_values[v].offset != NO_VALUE) {
339                         ao_cmd_decimal();
340                         if (ao_cmd_status != ao_cmd_success)
341                                 return;
342                         *((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
343                 }
344         }
345         _ao_config_edit_start();
346         ao_config.pyro[p] = pyro_tmp;
347         _ao_config_edit_finish();
348 }
349
350 void
351 ao_pyro_init(void)
352 {
353 #if AO_PYRO_NUM > 0
354         ao_enable_output(AO_PYRO_PORT_0, AO_PYRO_PIN_0, 0);
355 #endif
356 #if AO_PYRO_NUM > 1
357         ao_enable_output(AO_PYRO_PORT_1, AO_PYRO_PIN_1, 0);
358 #endif
359 #if AO_PYRO_NUM > 2
360         ao_enable_output(AO_PYRO_PORT_2, AO_PYRO_PIN_2, 0);
361 #endif
362 #if AO_PYRO_NUM > 3
363         ao_enable_output(AO_PYRO_PORT_3, AO_PYRO_PIN_3, 0);
364 #endif
365 #if AO_PYRO_NUM > 4
366         ao_enable_output(AO_PYRO_PORT_4, AO_PYRO_PIN_4, 0);
367 #endif
368 #if AO_PYRO_NUM > 5
369         ao_enable_output(AO_PYRO_PORT_5, AO_PYRO_PIN_5, 0);
370 #endif
371 #if AO_PYRO_NUM > 6
372         ao_enable_output(AO_PYRO_PORT_6, AO_PYRO_PIN_6, 0);
373 #endif
374 #if AO_PYRO_NUM > 7
375         ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, 0);
376 #endif
377         ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
378 }