altos: Finish up primitive telelaunch protocol
[fw/altos] / src / ao_launch.c
1 /*
2  * Copyright © 2011 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
20 __xdata uint16_t ao_launch_ignite;
21
22 #if 0
23 #define PRINTD(...) printf(__VA_ARGS__)
24 #else
25 #define PRINTD(...) 
26 #endif
27
28 static void
29 ao_launch_run(void)
30 {
31         for (;;) {
32                 while (!ao_launch_ignite)
33                         ao_sleep(&ao_launch_ignite);
34                 while (ao_launch_ignite) {
35                         ao_launch_ignite = 0;
36
37                         ao_ignition[ao_igniter_drogue].firing = 1;
38                         ao_ignition[ao_igniter_main].firing = 1;
39                         AO_IGNITER_DROGUE = 1;
40                         ao_delay(AO_MS_TO_TICKS(500));
41                         AO_IGNITER_DROGUE = 0;
42                         ao_ignition[ao_igniter_drogue].firing = 0;
43                         ao_ignition[ao_igniter_main].firing = 0;
44                 }
45         }
46 }
47
48 static void
49 ao_launch_status(void)
50 {
51         uint8_t i;
52         for (;;) {
53                 ao_delay(AO_SEC_TO_TICKS(1));
54                 if (ao_igniter_status(ao_igniter_drogue) == ao_igniter_ready) {
55                         if (ao_igniter_status(ao_igniter_main) == ao_igniter_ready) {
56                                 for (i = 0; i < 5; i++) {
57                                         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(50));
58                                         ao_delay(AO_MS_TO_TICKS(100));
59                                 }
60                         } else {
61                                 ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
62                         }
63                 }
64         }
65 }
66
67 static __pdata uint8_t  ao_launch_armed;
68 static __pdata uint16_t ao_launch_arm_time;
69
70 static void
71 ao_launch(void)
72 {
73         static __xdata struct ao_launch_command command;
74         static __xdata struct ao_launch_query   query;
75         int16_t time_difference;
76
77         ao_led_off(AO_LED_RED);
78         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
79         for (;;) {
80                 flush();
81                 if (ao_radio_cmac_recv(&command, sizeof (command), 0) != AO_RADIO_CMAC_OK)
82                         continue;
83                 
84                 PRINTD ("tick %d serial %d cmd %d channel %d\n",
85                         command.tick, command.serial, command.cmd, command.channel);
86
87                 switch (command.cmd) {
88                 case AO_LAUNCH_QUERY:
89                         if (command.serial != ao_serial_number) {
90                                 PRINTD ("serial number mismatch\n");
91                                 break;
92                         }
93
94                         if (command.channel == 0) {
95                                 query.valid = 1;
96                                 query.arm_status = ao_igniter_status(ao_igniter_drogue);
97                                 query.igniter_status = ao_igniter_status(ao_igniter_main);
98                         } else {
99                                 query.valid = 0;
100                         }
101                         query.tick = ao_time();
102                         query.serial = ao_serial_number;
103                         query.channel = command.channel;
104                         PRINTD ("query tick %d serial %d channel %d valid %d arm %d igniter %d\n",
105                                 query.tick, query.serial, query.channel, query.valid, query.arm_status,
106                                 query.igniter_status);
107                         ao_radio_cmac_send(&query, sizeof (query));
108                         break;
109                 case AO_LAUNCH_ARM:
110                         if (command.serial != ao_serial_number) {
111                                 PRINTD ("serial number mismatch\n");
112                                 break;
113                         }
114
115                         if (command.channel != 0)
116                                 break;
117                         time_difference = command.tick - ao_time();
118                         PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
119                         if (time_difference < 0)
120                                 time_difference = -time_difference;
121                         if (time_difference > 10) {
122                                 PRINTD ("time difference too large %d\n", time_difference);
123                                 break;
124                         }
125                         PRINTD ("armed\n");
126                         ao_launch_armed = 1;
127                         ao_launch_arm_time = ao_time();
128                         break;
129                 case AO_LAUNCH_FIRE:
130                         if (!ao_launch_armed) {
131                                 PRINTD ("not armed\n");
132                                 break;
133                         }
134                         if ((uint16_t) (ao_time() - ao_launch_arm_time) > AO_SEC_TO_TICKS(20)) {
135                                 PRINTD ("late launch arm_time %d time %d\n",
136                                         ao_launch_arm_time, ao_time());
137                                 break;
138                         }
139                         time_difference = command.tick - ao_time();
140                         if (time_difference < 0)
141                                 time_difference = -time_difference;
142                         if (time_difference > 10) {
143                                 PRINTD ("time different too large %d\n", time_difference);
144                                 break;
145                         }
146                         PRINTD ("ignite\n");
147                         ao_launch_ignite = 1;
148                         ao_wakeup(&ao_launch_ignite);
149                         break;
150                 }
151         }
152 }
153
154 void
155 ao_launch_test(void)
156 {
157         switch (ao_igniter_status(ao_igniter_drogue)) {
158         case ao_igniter_ready:
159         case ao_igniter_active:
160                 printf ("Armed: ");
161                 switch (ao_igniter_status(ao_igniter_main)) {
162                 default:
163                         printf("unknown status\n");
164                         break;
165                 case ao_igniter_ready:
166                         printf("igniter good\n");
167                         break;
168                 case ao_igniter_open:
169                         printf("igniter bad\n");
170                         break;
171                 }
172                 break;
173         default:
174                 printf("Disarmed\n");
175         }
176 }
177
178 void
179 ao_launch_manual(void)
180 {
181         ao_cmd_white();
182         if (!ao_match_word("DoIt"))
183                 return;
184         ao_cmd_white();
185         ao_launch_ignite = 1;
186         ao_wakeup(&ao_launch_ignite);
187 }
188
189 static __xdata struct ao_task ao_launch_task;
190 static __xdata struct ao_task ao_launch_ignite_task;
191 static __xdata struct ao_task ao_launch_status_task;
192
193 __code struct ao_cmds ao_launch_cmds[] = {
194         { ao_launch_test,       "t\0Test launch continuity" },
195         { ao_launch_manual,     "i <key>\0Fire igniter. <key> is doit with D&I" },
196         { 0, NULL }
197 };
198
199 void
200 ao_launch_init(void)
201 {
202         AO_IGNITER_DROGUE = 0;
203         AO_IGNITER_MAIN = 0;
204         AO_IGNITER_DIR |= AO_IGNITER_DROGUE_BIT | AO_IGNITER_MAIN_BIT;
205         ao_cmd_register(&ao_launch_cmds[0]);
206         ao_add_task(&ao_launch_task, ao_launch, "launch listener");
207         ao_add_task(&ao_launch_ignite_task, ao_launch_run, "launch igniter");
208         ao_add_task(&ao_launch_status_task, ao_launch_status, "launch status");
209 }