altos: Add bit-bang i2c driver
[fw/altos] / src / kernel / ao_telemetry.c
1 /*
2  * Copyright © 2011 Keth 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_log.h"
21 #include "ao_product.h"
22
23 static uint16_t ao_telemetry_interval;
24
25 #if HAS_RADIO_RATE
26 static uint16_t ao_telemetry_desired_interval;
27 #endif
28
29 /* TeleMetrum v1.0 just doesn't have enough space to
30  * manage the more complicated telemetry scheduling, so
31  * it loses the ability to disable telem/rdf separately
32  */
33
34 #if defined(TELEMETRUM_V_1_0)
35 #define SIMPLIFY
36 #endif
37
38 #ifdef SIMPLIFY
39 #define ao_telemetry_time time
40 #define RDF_SPACE       
41 #else
42 #define RDF_SPACE       
43 static uint16_t ao_telemetry_time;
44 #endif
45
46 #if HAS_RDF
47 static RDF_SPACE uint8_t ao_rdf = 0;
48 static RDF_SPACE uint16_t ao_rdf_time;
49 #endif
50
51 #if HAS_APRS
52 static uint16_t ao_aprs_time;
53
54 #include <ao_aprs.h>
55 #endif
56
57 #if defined(TELEMEGA)
58 #define AO_SEND_MEGA    1
59 #endif
60
61 #if defined (TELEMETRUM_V_2_0) || defined (TELEMETRUM_V_3_0)
62 #define AO_SEND_METRUM  1
63 #endif
64
65 #if defined(TELEMETRUM_V_0_1) || defined(TELEMETRUM_V_0_2) || defined(TELEMETRUM_V_1_0) || defined(TELEMETRUM_V_1_1) || defined(TELEBALLOON_V_1_1) || defined(TELEMETRUM_V_1_2)
66 #define AO_TELEMETRY_SENSOR     AO_TELEMETRY_SENSOR_TELEMETRUM
67 #endif
68
69 #if defined(TELEMINI_V_1_0)
70 #define AO_TELEMETRY_SENSOR     AO_TELEMETRY_SENSOR_TELEMINI
71 #endif
72
73 #if defined(TELENANO_V_0_1)
74 #define AO_TELEMETRY_SENSOR     AO_TELEMETRY_SENSOR_TELENANO
75 #endif
76
77 static union ao_telemetry_all   telemetry;
78
79 static void
80 ao_telemetry_send(void)
81 {
82         ao_radio_send(&telemetry, sizeof (telemetry));
83         ao_delay(1);
84 }
85
86 #if defined AO_TELEMETRY_SENSOR
87 /* Send sensor packet */
88 static void
89 ao_send_sensor(void)
90 {
91                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
92
93         telemetry.generic.tick = packet->tick;
94         telemetry.generic.type = AO_TELEMETRY_SENSOR;
95
96         telemetry.sensor.state = ao_flight_state;
97 #if HAS_ACCEL
98         telemetry.sensor.accel = packet->adc.accel;
99 #else
100         telemetry.sensor.accel = 0;
101 #endif
102         telemetry.sensor.pres = ao_data_pres(packet);
103         telemetry.sensor.temp = packet->adc.temp;
104         telemetry.sensor.v_batt = packet->adc.v_batt;
105 #if HAS_IGNITE
106         telemetry.sensor.sense_d = packet->adc.sense_d;
107         telemetry.sensor.sense_m = packet->adc.sense_m;
108 #else
109         telemetry.sensor.sense_d = 0;
110         telemetry.sensor.sense_m = 0;
111 #endif
112
113         telemetry.sensor.acceleration = ao_accel;
114         telemetry.sensor.speed = ao_speed;
115         telemetry.sensor.height = ao_height;
116
117         telemetry.sensor.ground_pres = ao_ground_pres;
118 #if HAS_ACCEL
119         telemetry.sensor.ground_accel = ao_ground_accel;
120         telemetry.sensor.accel_plus_g = ao_config.accel_plus_g;
121         telemetry.sensor.accel_minus_g = ao_config.accel_minus_g;
122 #else
123         telemetry.sensor.ground_accel = 0;
124         telemetry.sensor.accel_plus_g = 0;
125         telemetry.sensor.accel_minus_g = 0;
126 #endif
127
128         ao_telemetry_send();
129 }
130 #endif
131
132
133 #ifdef AO_SEND_MEGA
134
135 /* Send mega sensor packet */
136 static void
137 ao_send_mega_sensor(void)
138 {
139                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
140
141         telemetry.generic.tick = packet->tick;
142 #if HAS_BMX160
143         telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR_BMX160;
144 #else
145 #if HAS_MPU6000 || HAS_MPU9250
146         telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR_MPU;
147 #else
148 #error unknown IMU
149 #endif
150 #endif
151
152 #if HAS_GYRO
153         telemetry.mega_sensor.orient = ao_sample_orient;
154 #endif
155         telemetry.mega_sensor.accel = ao_data_accel(packet);
156         telemetry.mega_sensor.pres = ao_data_pres(packet);
157         telemetry.mega_sensor.temp = ao_data_temp(packet);
158
159 #if HAS_MPU6000
160         telemetry.mega_sensor.accel_x = packet->mpu6000.accel_x;
161         telemetry.mega_sensor.accel_y = packet->mpu6000.accel_y;
162         telemetry.mega_sensor.accel_z = packet->mpu6000.accel_z;
163
164         telemetry.mega_sensor.gyro_x = packet->mpu6000.gyro_x;
165         telemetry.mega_sensor.gyro_y = packet->mpu6000.gyro_y;
166         telemetry.mega_sensor.gyro_z = packet->mpu6000.gyro_z;
167 #endif
168
169 #if HAS_HMC5883
170         telemetry.mega_sensor.mag_x = packet->hmc5883.x;
171         telemetry.mega_sensor.mag_z = packet->hmc5883.z;
172         telemetry.mega_sensor.mag_y = packet->hmc5883.y;
173 #endif
174
175 #if HAS_MPU9250
176         telemetry.mega_sensor.accel_x = packet->mpu9250.accel_x;
177         telemetry.mega_sensor.accel_y = packet->mpu9250.accel_y;
178         telemetry.mega_sensor.accel_z = packet->mpu9250.accel_z;
179
180         telemetry.mega_sensor.gyro_x = packet->mpu9250.gyro_x;
181         telemetry.mega_sensor.gyro_y = packet->mpu9250.gyro_y;
182         telemetry.mega_sensor.gyro_z = packet->mpu9250.gyro_z;
183
184         telemetry.mega_sensor.mag_x = packet->mpu9250.mag_x;
185         telemetry.mega_sensor.mag_z = packet->mpu9250.mag_z;
186         telemetry.mega_sensor.mag_y = packet->mpu9250.mag_y;
187 #endif
188
189 #if HAS_BMX160
190         telemetry.mega_sensor.accel_x = packet->bmx160.acc_x;
191         telemetry.mega_sensor.accel_y = packet->bmx160.acc_y;
192         telemetry.mega_sensor.accel_z = packet->bmx160.acc_z;
193
194         telemetry.mega_sensor.gyro_x = packet->bmx160.gyr_x;
195         telemetry.mega_sensor.gyro_y = packet->bmx160.gyr_y;
196         telemetry.mega_sensor.gyro_z = packet->bmx160.gyr_z;
197
198         telemetry.mega_sensor.mag_x = packet->bmx160.mag_x;
199         telemetry.mega_sensor.mag_z = packet->bmx160.mag_z;
200         telemetry.mega_sensor.mag_y = packet->bmx160.mag_y;
201 #endif
202
203         ao_telemetry_send();
204 }
205
206 static int8_t ao_telemetry_mega_data_max;
207 static int8_t ao_telemetry_mega_data_cur;
208
209 /* Send mega data packet */
210 static void
211 ao_send_mega_data(void)
212 {
213         if (--ao_telemetry_mega_data_cur <= 0) {
214                         struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
215                 uint8_t i;
216
217                 telemetry.generic.tick = packet->tick;
218                 telemetry.generic.type = AO_TELEMETRY_MEGA_DATA;
219
220                 telemetry.mega_data.state = ao_flight_state;
221                 telemetry.mega_data.v_batt = packet->adc.v_batt;
222                 telemetry.mega_data.v_pyro = packet->adc.v_pbatt;
223
224                 /* ADC range is 0-4095, so shift by four to save the high 8 bits */
225                 for (i = 0; i < AO_ADC_NUM_SENSE; i++)
226                         telemetry.mega_data.sense[i] = packet->adc.sense[i] >> 4;
227
228                 telemetry.mega_data.ground_pres = ao_ground_pres;
229                 telemetry.mega_data.ground_accel = ao_ground_accel;
230                 telemetry.mega_data.accel_plus_g = ao_config.accel_plus_g;
231                 telemetry.mega_data.accel_minus_g = ao_config.accel_minus_g;
232
233                 telemetry.mega_data.acceleration = ao_accel;
234                 telemetry.mega_data.speed = ao_speed;
235                 telemetry.mega_data.height = ao_height;
236
237                 ao_telemetry_mega_data_cur = ao_telemetry_mega_data_max;
238                 ao_telemetry_send();
239         }
240 }
241 #endif /* AO_SEND_MEGA */
242
243 #ifdef AO_SEND_METRUM
244 /* Send telemetrum sensor packet */
245 static void
246 ao_send_metrum_sensor(void)
247 {
248                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
249
250         telemetry.generic.tick = packet->tick;
251         telemetry.generic.type = AO_TELEMETRY_METRUM_SENSOR;
252
253         telemetry.metrum_sensor.state = ao_flight_state;
254 #if HAS_ACCEL
255         telemetry.metrum_sensor.accel = ao_data_accel(packet);
256 #endif
257         telemetry.metrum_sensor.pres = ao_data_pres(packet);
258         telemetry.metrum_sensor.temp = ao_data_temp(packet);
259
260         telemetry.metrum_sensor.acceleration = ao_accel;
261         telemetry.metrum_sensor.speed = ao_speed;
262         telemetry.metrum_sensor.height = ao_height;
263
264         telemetry.metrum_sensor.v_batt = packet->adc.v_batt;
265         telemetry.metrum_sensor.sense_a = packet->adc.sense_a;
266         telemetry.metrum_sensor.sense_m = packet->adc.sense_m;
267
268         ao_telemetry_send();
269 }
270
271 static int8_t ao_telemetry_metrum_data_max;
272 static int8_t ao_telemetry_metrum_data_cur;
273
274 /* Send telemetrum data packet */
275 static void
276 ao_send_metrum_data(void)
277 {
278         if (--ao_telemetry_metrum_data_cur <= 0) {
279                         struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
280
281                 telemetry.generic.tick = packet->tick;
282                 telemetry.generic.type = AO_TELEMETRY_METRUM_DATA;
283
284                 telemetry.metrum_data.ground_pres = ao_ground_pres;
285 #if HAS_ACCEL
286                 telemetry.metrum_data.ground_accel = ao_ground_accel;
287                 telemetry.metrum_data.accel_plus_g = ao_config.accel_plus_g;
288                 telemetry.metrum_data.accel_minus_g = ao_config.accel_minus_g;
289 #else
290                 telemetry.metrum_data.ground_accel = 1;
291                 telemetry.metrum_data.accel_plus_g = 0;
292                 telemetry.metrum_data.accel_minus_g = 2;
293 #endif
294
295                 ao_telemetry_metrum_data_cur = ao_telemetry_metrum_data_max;
296                 ao_telemetry_send();
297         }
298 }
299 #endif /* AO_SEND_METRUM */
300
301 #ifdef AO_SEND_MINI
302
303 static void
304 ao_send_mini(void)
305 {
306                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
307
308         telemetry.generic.tick = packet->tick;
309         telemetry.generic.type = AO_SEND_MINI;
310
311         telemetry.mini.state = ao_flight_state;
312
313         telemetry.mini.v_batt = packet->adc.v_batt;
314         telemetry.mini.sense_a = packet->adc.sense_a;
315         telemetry.mini.sense_m = packet->adc.sense_m;
316
317         telemetry.mini.pres = ao_data_pres(packet);
318         telemetry.mini.temp = ao_data_temp(packet);
319
320         telemetry.mini.acceleration = ao_accel;
321         telemetry.mini.speed = ao_speed;
322         telemetry.mini.height = ao_height;
323
324         telemetry.mini.ground_pres = ao_ground_pres;
325
326         ao_telemetry_send();
327 }
328
329 #endif /* AO_SEND_MINI */
330
331 static int8_t ao_telemetry_config_max;
332 static int8_t ao_telemetry_config_cur;
333 static uint16_t ao_telemetry_flight_number;
334
335 #ifndef ao_telemetry_battery_convert
336 #define ao_telemetry_battery_convert(a) (a)
337 #endif
338
339 static void
340 ao_send_configuration(void)
341 {
342         if (--ao_telemetry_config_cur <= 0)
343         {
344                 telemetry.generic.type = AO_TELEMETRY_CONFIGURATION;
345                 telemetry.configuration.device = AO_idProduct_NUMBER;
346                 telemetry.configuration.flight = ao_telemetry_flight_number;
347                 telemetry.configuration.config_major = AO_CONFIG_MAJOR;
348                 telemetry.configuration.config_minor = AO_CONFIG_MINOR;
349 #if AO_idProduct_NUMBER == 0x25 && HAS_ADC
350                 /* TeleGPS gets battery voltage instead of apogee delay */
351                 telemetry.configuration.apogee_delay = ao_telemetry_battery_convert(ao_data_ring[ao_data_ring_prev(ao_data_head)].adc.v_batt);
352 #else
353                 telemetry.configuration.apogee_delay = ao_config.apogee_delay;
354                 telemetry.configuration.main_deploy = ao_config.main_deploy;
355 #endif
356
357                 telemetry.configuration.flight_log_max = ao_config.flight_log_max >> 10;
358                 memcpy (telemetry.configuration.callsign,
359                         ao_config.callsign,
360                         AO_MAX_CALLSIGN);
361                 memcpy (telemetry.configuration.version,
362                         ao_version,
363                         AO_MAX_VERSION);
364                 ao_telemetry_config_cur = ao_telemetry_config_max;
365                 ao_telemetry_send();
366         }
367 }
368
369 #if HAS_GPS
370
371 static int8_t ao_telemetry_gps_max;
372 static int8_t ao_telemetry_loc_cur;
373 static int8_t ao_telemetry_sat_cur;
374
375 static inline void *
376 telemetry_bits(struct ao_telemetry_location *l)
377 {
378         return ((uint8_t *) l) + offsetof(struct ao_telemetry_location, flags);
379 }
380
381 static inline int
382 telemetry_size(void)
383 {
384         return sizeof(struct ao_telemetry_location) - offsetof(struct ao_telemetry_location, flags);
385 }
386
387 static void
388 ao_send_location(void)
389 {
390         if (--ao_telemetry_loc_cur <= 0)
391         {
392                 telemetry.generic.type = AO_TELEMETRY_LOCATION;
393                 ao_mutex_get(&ao_gps_mutex);
394                 memcpy(telemetry_bits(&telemetry.location),
395                        telemetry_bits(&ao_gps_data),
396                        telemetry_size());
397                 telemetry.location.tick = ao_gps_tick;
398                 ao_mutex_put(&ao_gps_mutex);
399                 ao_telemetry_loc_cur = ao_telemetry_gps_max;
400                 ao_telemetry_send();
401         }
402 }
403
404 static void
405 ao_send_satellite(void)
406 {
407         if (--ao_telemetry_sat_cur <= 0)
408         {
409                 telemetry.generic.type = AO_TELEMETRY_SATELLITE;
410                 ao_mutex_get(&ao_gps_mutex);
411                 telemetry.satellite.channels = ao_gps_tracking_data.channels;
412                 memcpy(&telemetry.satellite.sats,
413                        &ao_gps_tracking_data.sats,
414                        AO_MAX_GPS_TRACKING * sizeof (struct ao_telemetry_satellite_info));
415                 ao_mutex_put(&ao_gps_mutex);
416                 ao_telemetry_sat_cur = ao_telemetry_gps_max;
417                 ao_telemetry_send();
418         }
419 }
420 #endif
421
422 #if HAS_COMPANION
423
424 static int8_t ao_telemetry_companion_max;
425 static int8_t ao_telemetry_companion_cur;
426
427 static void
428 ao_send_companion(void)
429 {
430         if (--ao_telemetry_companion_cur <= 0) {
431                 telemetry.generic.type = AO_TELEMETRY_COMPANION;
432                 telemetry.companion.board_id = ao_companion_setup.board_id;
433                 telemetry.companion.update_period = ao_companion_setup.update_period;
434                 telemetry.companion.channels = ao_companion_setup.channels;
435                 ao_mutex_get(&ao_companion_mutex);
436                 memcpy(&telemetry.companion.companion_data,
437                        ao_companion_data,
438                        ao_companion_setup.channels * 2);
439                 ao_mutex_put(&ao_companion_mutex);
440                 ao_telemetry_companion_cur = ao_telemetry_companion_max;
441                 ao_telemetry_send();
442         }
443 }
444 #endif
445
446 #if HAS_APRS
447 static void
448 ao_set_aprs_time(void)
449 {
450         uint16_t interval = ao_config.aprs_interval;
451
452         if ((ao_gps_data.flags & AO_GPS_DATE_VALID) && interval != 0) {
453                 int second = (ao_gps_data.second / interval + 1) * interval + ao_config.aprs_offset;
454                 int delta;
455                 if (second >= 60) {
456                         second = ao_config.aprs_offset;
457                         delta = second + 60 - ao_gps_data.second;
458                 } else {
459                         delta = second - ao_gps_data.second;
460                 }
461                 ao_aprs_time = ao_gps_tick + AO_SEC_TO_TICKS(delta);
462         } else {
463                 ao_aprs_time += AO_SEC_TO_TICKS(ao_config.aprs_interval);
464         }
465 }
466 #endif
467
468 static void
469 ao_telemetry(void)
470 {
471         uint16_t        time;
472         int16_t         delay;
473
474         ao_config_get();
475         if (!ao_config.radio_enable)
476                 ao_exit();
477         while (!ao_flight_number)
478                 ao_sleep(&ao_flight_number);
479
480         ao_telemetry_flight_number = ao_flight_number;
481 #if HAS_LOG
482         if (ao_log_full())
483                 ao_telemetry_flight_number = 0;
484 #endif
485         telemetry.generic.serial = ao_serial_number;
486         for (;;) {
487                 while (ao_telemetry_interval == 0)
488                         ao_sleep(&telemetry);
489                 time = ao_time();
490                 ao_telemetry_time = time;
491 #if HAS_RDF
492                 ao_rdf_time = time;
493 #endif
494 #if HAS_APRS
495                 ao_aprs_time = time;
496                 ao_set_aprs_time();
497 #endif
498                 while (ao_telemetry_interval) {
499                         time = ao_time() + AO_SEC_TO_TICKS(100);
500 #ifndef SIMPLIFY
501                         if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
502 #endif
503                         {
504 #ifndef SIMPLIFY
505                                 if ( (int16_t) (ao_time() - ao_telemetry_time) >= 0)
506 #endif
507                                 {
508                                         ao_telemetry_time = ao_time() + ao_telemetry_interval;
509 # ifdef AO_SEND_MEGA
510                                         ao_send_mega_sensor();
511                                         ao_send_mega_data();
512 # endif
513 # ifdef AO_SEND_METRUM
514                                         ao_send_metrum_sensor();
515                                         ao_send_metrum_data();
516 # endif
517 # ifdef AO_SEND_MINI
518                                         ao_send_mini();
519 # endif
520 # ifdef AO_TELEMETRY_SENSOR
521                                         ao_send_sensor();
522 # endif
523 #if HAS_COMPANION
524                                         if (ao_companion_running)
525                                                 ao_send_companion();
526 #endif
527 #if HAS_GPS
528                                         ao_send_location();
529                                         ao_send_satellite();
530 #endif
531                                         ao_send_configuration();
532                                 }
533 #ifndef SIMPLIFY
534                                 time = ao_telemetry_time;
535 #endif
536                         }
537 #if HAS_RDF
538                         if (ao_rdf
539 #ifndef SIMPLIFY
540                             && !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF)
541 #endif
542                                 )
543                         {
544                                 if ((int16_t) (ao_time() - ao_rdf_time) >= 0) {
545 #if HAS_IGNITE_REPORT
546                                         uint8_t c;
547 #endif
548                                         ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
549 #if HAS_IGNITE_REPORT
550                                         if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
551                                                 ao_radio_continuity(c);
552                                         else
553 #endif
554                                                 ao_radio_rdf();
555                                 }
556 #ifndef SIMPLIFY
557                                 if ((int16_t) (time - ao_rdf_time) > 0)
558                                         time = ao_rdf_time;
559 #endif
560                         }
561 #endif /* HAS_RDF */
562 #if HAS_APRS
563                         if (ao_config.aprs_interval != 0) {
564                                 if ((int16_t) (ao_time() - ao_aprs_time) >= 0) {
565                                         ao_set_aprs_time();
566                                         ao_aprs_send();
567                                 }
568                                 if ((int16_t) (time - ao_aprs_time) > 0)
569                                         time = ao_aprs_time;
570                         }
571 #endif /* HAS_APRS */
572                         delay = time - ao_time();
573                         if (delay > 0) {
574                                 ao_sleep_for(&telemetry, delay);
575                         }
576                 }
577         }
578 }
579
580 #if HAS_RADIO_RATE
581 void
582 ao_telemetry_reset_interval(void)
583 {
584         ao_telemetry_set_interval(ao_telemetry_desired_interval);
585 }
586 #endif
587
588 void
589 ao_telemetry_set_interval(uint16_t interval)
590 {
591         int8_t  cur = 0;
592
593 #if HAS_RADIO_RATE
594         /* Limit max telemetry rate based on available radio bandwidth.
595          */
596         static const uint16_t min_interval[] = {
597                 /* [AO_RADIO_RATE_38400] = */ AO_MS_TO_TICKS(100),
598                 /* [AO_RADIO_RATE_9600] = */ AO_MS_TO_TICKS(500),
599                 /* [AO_RADIO_RATE_2400] = */ AO_MS_TO_TICKS(1000)
600         };
601
602         ao_telemetry_desired_interval = interval;
603         if (interval && interval < min_interval[ao_config.radio_rate])
604                 interval = min_interval[ao_config.radio_rate];
605 #endif
606         ao_telemetry_interval = interval;
607 #if AO_SEND_MEGA
608         if (interval > 1)
609                 ao_telemetry_mega_data_max = 1;
610         else
611                 ao_telemetry_mega_data_max = 2;
612         if (ao_telemetry_mega_data_max > cur)
613                 cur++;
614         ao_telemetry_mega_data_cur = cur;
615 #endif
616 #if AO_SEND_METRUM
617         ao_telemetry_metrum_data_max = AO_SEC_TO_TICKS(1) / interval;
618         if (ao_telemetry_metrum_data_max > cur)
619                 cur++;
620         ao_telemetry_metrum_data_cur = cur;
621 #endif
622
623 #if HAS_COMPANION
624         if (!ao_companion_setup.update_period)
625                 ao_companion_setup.update_period = AO_SEC_TO_TICKS(1);
626         ao_telemetry_companion_max = ao_companion_setup.update_period / interval;
627         if (ao_telemetry_companion_max > cur)
628                 cur++;
629         ao_telemetry_companion_cur = cur;
630 #endif
631
632 #if HAS_GPS
633         ao_telemetry_gps_max = AO_SEC_TO_TICKS(1) / interval;
634         if (ao_telemetry_gps_max > cur)
635                 cur++;
636         ao_telemetry_loc_cur = cur;
637         if (ao_telemetry_gps_max > cur)
638                 cur++;
639         ao_telemetry_sat_cur = cur;
640 #endif
641
642         ao_telemetry_config_max = AO_SEC_TO_TICKS(5) / interval;
643         if (ao_telemetry_config_max > cur)
644                 cur++;
645         ao_telemetry_config_cur = cur;
646
647 #ifndef SIMPLIFY
648         ao_telemetry_time = 
649 #if HAS_RDF
650                 ao_rdf_time =
651 #endif
652 #if HAS_APRS
653                 ao_aprs_time =
654 #endif
655                 ao_time();
656 #endif
657         ao_wakeup(&telemetry);
658 }
659
660 #if HAS_RDF
661 void
662 ao_rdf_set(uint8_t rdf)
663 {
664         ao_rdf = rdf;
665         if (rdf == 0)
666                 ao_radio_rdf_abort();
667         else {
668                 ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
669         }
670 }
671 #endif
672
673 struct ao_task  ao_telemetry_task;
674
675 void
676 ao_telemetry_init()
677 {
678         ao_add_task(&ao_telemetry_task, ao_telemetry, "telemetry");
679 }