Switch from GPLv2 to GPLv2+
[fw/altos] / src / kernel / ao_monitor.c
1 /*
2  * Copyright © 2009 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ao.h"
20 #include "ao_telem.h"
21 #include "ao_flight.h"
22
23 #if !HAS_MONITOR
24 #error Must define HAS_MONITOR to 1
25 #endif
26
27 #ifndef LEGACY_MONITOR
28 #error Must define LEGACY_MONITOR
29 #endif
30
31 #ifndef HAS_MONITOR_PUT
32 #define HAS_MONITOR_PUT 1
33 #endif
34
35 #ifndef AO_MONITOR_LED
36 #error Must define AO_MONITOR_LED
37 #endif
38
39 __data uint8_t ao_monitoring;
40 static __data uint8_t ao_monitor_disabled;
41 static __data uint8_t ao_internal_monitoring;
42 static __data uint8_t ao_external_monitoring;
43
44 __xdata union ao_monitor ao_monitor_ring[AO_MONITOR_RING];
45
46 __data uint8_t  ao_monitor_head;
47
48 static void
49 _ao_monitor_adjust(void)
50 {
51         if (ao_monitoring)
52                 ao_radio_recv_abort();
53         if (ao_monitor_disabled)
54                 ao_monitoring = 0;
55         else {
56                 if (ao_external_monitoring)
57                         ao_monitoring = ao_external_monitoring;
58                 else
59                         ao_monitoring = ao_internal_monitoring;
60         }
61         ao_wakeup(DATA_TO_XDATA(&ao_monitoring));
62 }
63
64 void
65 ao_monitor_get(void)
66 {
67         uint8_t size;
68
69         for (;;) {
70                 switch (ao_monitoring) {
71                 case 0:
72                         ao_sleep(DATA_TO_XDATA(&ao_monitoring));
73                         continue;
74 #if LEGACY_MONITOR
75                 case AO_MONITORING_ORIG:
76                         size = sizeof (struct ao_telemetry_orig_recv);
77                         break;
78 #endif
79                 default:
80                         if (ao_monitoring > AO_MAX_TELEMETRY)
81                                 ao_monitoring = AO_MAX_TELEMETRY;
82                         size = ao_monitoring;
83                         break;
84                 }
85                 if (!ao_radio_recv(&ao_monitor_ring[ao_monitor_head], size + 2, 0))
86                         continue;
87                 ao_monitor_head = ao_monitor_ring_next(ao_monitor_head);
88                 ao_wakeup(DATA_TO_XDATA(&ao_monitor_head));
89         }
90 }
91
92 #if AO_MONITOR_LED
93 __xdata struct ao_task ao_monitor_blink_task;
94
95 void
96 ao_monitor_blink(void)
97 {
98 #ifdef AO_MONITOR_BAD
99         uint8_t         *recv;
100 #endif
101         for (;;) {
102                 ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
103 #ifdef AO_MONITOR_BAD
104                 recv = (uint8_t *) &ao_monitor_ring[ao_monitor_ring_prev(ao_monitor_head)];
105                 if (ao_monitoring && !(recv[ao_monitoring + 1] & AO_RADIO_STATUS_CRC_OK))
106                         ao_led_for(AO_MONITOR_BAD, AO_MS_TO_TICKS(100));
107                 else
108 #endif
109                         ao_led_for(AO_MONITOR_LED, AO_MS_TO_TICKS(100));
110         }
111 }
112 #endif
113
114 #if HAS_MONITOR_PUT
115
116 static const char xdigit[16] = {
117         '0', '1', '2', '3', '4', '5', '6', '7',
118         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
119 };
120
121 #define hex(c) do { putchar(xdigit[(c) >> 4]); putchar(xdigit[(c)&0xf]); } while (0)
122
123 void
124 ao_monitor_put(void)
125 {
126 #if LEGACY_MONITOR
127         __xdata char callsign[AO_MAX_CALLSIGN+1];
128 #endif
129 #if LEGACY_MONITOR || HAS_RSSI
130         int16_t rssi;
131 #endif
132         uint8_t ao_monitor_tail;
133         uint8_t state;
134         uint8_t sum, byte;
135         __xdata union ao_monitor        *m;
136
137 #define recv_raw        ((m->raw))
138 #define recv_orig       ((m->orig))
139 #define recv_tiny       ((m->tiny))
140
141         ao_monitor_tail = ao_monitor_head;
142         for (;;) {
143                 while (!ao_external_monitoring)
144                         ao_sleep(DATA_TO_XDATA(&ao_external_monitoring));
145                 while (ao_monitor_tail == ao_monitor_head && ao_external_monitoring)
146                         ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
147                 if (!ao_external_monitoring)
148                         continue;
149                 m = &ao_monitor_ring[ao_monitor_tail];
150                 ao_monitor_tail = ao_monitor_ring_next(ao_monitor_tail);
151                 switch (ao_monitoring) {
152                 case 0:
153                         break;
154 #if LEGACY_MONITOR
155                 case AO_MONITORING_ORIG:
156                         state = recv_orig.telemetry_orig.flight_state;
157
158                         rssi = (int16_t) AO_RSSI_FROM_RADIO(recv_orig.rssi);
159                         ao_xmemcpy(callsign, recv_orig.telemetry_orig.callsign, AO_MAX_CALLSIGN);
160                         if (state > ao_flight_invalid)
161                                 state = ao_flight_invalid;
162                         if (recv_orig.status & PKT_APPEND_STATUS_1_CRC_OK) {
163
164                                 /* General header fields */
165                                 printf(AO_TELEM_VERSION " %d "
166                                        AO_TELEM_CALL " %s "
167                                        AO_TELEM_SERIAL " %d "
168                                        AO_TELEM_FLIGHT " %d "
169                                        AO_TELEM_RSSI " %d "
170                                        AO_TELEM_STATE " %s "
171                                        AO_TELEM_TICK " %d ",
172                                        AO_TELEMETRY_VERSION,
173                                        callsign,
174                                        recv_orig.telemetry_orig.serial,
175                                        recv_orig.telemetry_orig.flight,
176                                        rssi,
177                                        ao_state_names[state],
178                                        recv_orig.telemetry_orig.adc.tick);
179
180                                 /* Raw sensor values */
181                                 printf(AO_TELEM_RAW_ACCEL " %d "
182                                        AO_TELEM_RAW_BARO " %d "
183                                        AO_TELEM_RAW_THERMO " %d "
184                                        AO_TELEM_RAW_BATT " %d "
185                                        AO_TELEM_RAW_DROGUE " %d "
186                                        AO_TELEM_RAW_MAIN " %d ",
187                                        recv_orig.telemetry_orig.adc.accel,
188                                        recv_orig.telemetry_orig.adc.pres,
189                                        recv_orig.telemetry_orig.adc.temp,
190                                        recv_orig.telemetry_orig.adc.v_batt,
191                                        recv_orig.telemetry_orig.adc.sense_d,
192                                        recv_orig.telemetry_orig.adc.sense_m);
193
194                                 /* Sensor calibration values */
195                                 printf(AO_TELEM_CAL_ACCEL_GROUND " %d "
196                                        AO_TELEM_CAL_BARO_GROUND " %d "
197                                        AO_TELEM_CAL_ACCEL_PLUS " %d "
198                                        AO_TELEM_CAL_ACCEL_MINUS " %d ",
199                                        recv_orig.telemetry_orig.ground_accel,
200                                        recv_orig.telemetry_orig.ground_pres,
201                                        recv_orig.telemetry_orig.accel_plus_g,
202                                        recv_orig.telemetry_orig.accel_minus_g);
203
204                                 if (recv_orig.telemetry_orig.u.k.unused == 0x8000) {
205                                         /* Kalman state values */
206                                         printf(AO_TELEM_KALMAN_HEIGHT " %d "
207                                                AO_TELEM_KALMAN_SPEED " %d "
208                                                AO_TELEM_KALMAN_ACCEL " %d ",
209                                                recv_orig.telemetry_orig.height,
210                                                recv_orig.telemetry_orig.u.k.speed,
211                                                recv_orig.telemetry_orig.accel);
212                                 } else {
213                                         /* Ad-hoc flight values */
214                                         printf(AO_TELEM_ADHOC_ACCEL " %d "
215                                                AO_TELEM_ADHOC_SPEED " %ld "
216                                                AO_TELEM_ADHOC_BARO " %d ",
217                                                recv_orig.telemetry_orig.accel,
218                                                recv_orig.telemetry_orig.u.flight_vel,
219                                                recv_orig.telemetry_orig.height);
220                                 }
221                                 ao_gps_print(&recv_orig.telemetry_orig.gps);
222                                 ao_gps_tracking_print(&recv_orig.telemetry_orig.gps_tracking);
223                                 putchar('\n');
224 #if HAS_RSSI
225                                 ao_rssi_set(rssi);
226 #endif
227                         } else {
228                                 printf("CRC INVALID RSSI %3d\n", rssi);
229                         }
230                         break;
231 #endif /* LEGACY_MONITOR */
232                 default:
233 #if AO_PROFILE
234                 {
235                         extern uint32_t ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
236                         extern uint32_t ao_fec_decode_start, ao_fec_decode_end;
237
238                         printf ("between packet: %d\n", ao_rx_start_tick - ao_rx_last_done_tick);
239                         printf ("receive start delay: %d\n", ao_rx_packet_tick - ao_rx_start_tick);
240                         printf ("decode time: %d\n", ao_fec_decode_end - ao_fec_decode_start);
241                         printf ("rx cleanup: %d\n", ao_rx_done_tick - ao_fec_decode_end);
242                 }
243 #endif
244                         printf("TELEM ");
245                         hex((uint8_t) (ao_monitoring + 2));
246                         sum = 0x5a;
247                         for (state = 0; state < ao_monitoring + 2; state++) {
248                                 byte = recv_raw.packet[state];
249                                 sum += byte;
250                                 hex(byte);
251                         }
252                         hex(sum);
253                         putchar ('\n');
254 #if HAS_RSSI
255                         if (recv_raw.packet[ao_monitoring + 1] & AO_RADIO_STATUS_CRC_OK) {
256                                 rssi = AO_RSSI_FROM_RADIO(recv_raw.packet[ao_monitoring]);
257                                 ao_rssi_set(rssi);
258                         }
259 #endif
260                         break;
261                 }
262                 ao_usb_flush();
263         }
264 }
265
266 __xdata struct ao_task ao_monitor_put_task;
267 #endif
268
269 __xdata struct ao_task ao_monitor_get_task;
270
271 void
272 ao_monitor_set(uint8_t monitoring)
273 {
274         ao_internal_monitoring = monitoring;
275         _ao_monitor_adjust();
276 }
277
278 void
279 ao_monitor_disable(void)
280 {
281         ++ao_monitor_disabled;
282         _ao_monitor_adjust();
283 }
284
285 void
286 ao_monitor_enable(void)
287 {
288         --ao_monitor_disabled;
289         _ao_monitor_adjust();
290 }
291
292 #if HAS_MONITOR_PUT
293 static void
294 set_monitor(void)
295 {
296         ao_cmd_hex();
297         ao_external_monitoring = ao_cmd_lex_i;
298         ao_wakeup(DATA_TO_XDATA(&ao_external_monitoring));
299         ao_wakeup(DATA_TO_XDATA(&ao_monitor_head));
300         _ao_monitor_adjust();
301 }
302
303 __code struct ao_cmds ao_monitor_cmds[] = {
304         { set_monitor,  "m <0 off, 1 old, 20 std>\0Set radio monitoring" },
305         { 0,    NULL },
306 };
307 #endif
308
309 void
310 ao_monitor_init(void) __reentrant
311 {
312 #if HAS_MONITOR_PUT
313         ao_cmd_register(&ao_monitor_cmds[0]);
314         ao_add_task(&ao_monitor_put_task, ao_monitor_put, "monitor_put");
315 #endif
316         ao_add_task(&ao_monitor_get_task, ao_monitor_get, "monitor_get");
317 #if AO_MONITOR_LED
318         ao_add_task(&ao_monitor_blink_task, ao_monitor_blink, "monitor_blink");
319 #endif
320 }