altos: Add new telemetry packet format for TeleMega v4
[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 static void
447 ao_telemetry(void)
448 {
449         uint16_t        time;
450         int16_t         delay;
451
452         ao_config_get();
453         if (!ao_config.radio_enable)
454                 ao_exit();
455         while (!ao_flight_number)
456                 ao_sleep(&ao_flight_number);
457
458         ao_telemetry_flight_number = ao_flight_number;
459 #if HAS_LOG
460         if (ao_log_full())
461                 ao_telemetry_flight_number = 0;
462 #endif
463         telemetry.generic.serial = ao_serial_number;
464         for (;;) {
465                 while (ao_telemetry_interval == 0)
466                         ao_sleep(&telemetry);
467                 time = ao_time();
468                 ao_telemetry_time = time;
469 #if HAS_RDF
470                 ao_rdf_time = time;
471 #endif
472 #if HAS_APRS
473                 ao_aprs_time = time;
474 #endif
475                 while (ao_telemetry_interval) {
476                         time = ao_time() + AO_SEC_TO_TICKS(100);
477 #ifndef SIMPLIFY
478                         if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
479 #endif
480                         {
481 #ifndef SIMPLIFY
482                                 if ( (int16_t) (ao_time() - ao_telemetry_time) >= 0)
483 #endif
484                                 {
485                                         ao_telemetry_time = ao_time() + ao_telemetry_interval;
486 # ifdef AO_SEND_MEGA
487                                         ao_send_mega_sensor();
488                                         ao_send_mega_data();
489 # endif
490 # ifdef AO_SEND_METRUM
491                                         ao_send_metrum_sensor();
492                                         ao_send_metrum_data();
493 # endif
494 # ifdef AO_SEND_MINI
495                                         ao_send_mini();
496 # endif
497 # ifdef AO_TELEMETRY_SENSOR
498                                         ao_send_sensor();
499 # endif
500 #if HAS_COMPANION
501                                         if (ao_companion_running)
502                                                 ao_send_companion();
503 #endif
504 #if HAS_GPS
505                                         ao_send_location();
506                                         ao_send_satellite();
507 #endif
508                                         ao_send_configuration();
509                                 }
510 #ifndef SIMPLIFY
511                                 time = ao_telemetry_time;
512 #endif
513                         }
514 #if HAS_RDF
515                         if (ao_rdf
516 #ifndef SIMPLIFY
517                             && !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF)
518 #endif
519                                 )
520                         {
521                                 if ((int16_t) (ao_time() - ao_rdf_time) >= 0) {
522 #if HAS_IGNITE_REPORT
523                                         uint8_t c;
524 #endif
525                                         ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
526 #if HAS_IGNITE_REPORT
527                                         if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
528                                                 ao_radio_continuity(c);
529                                         else
530 #endif
531                                                 ao_radio_rdf();
532                                 }
533 #ifndef SIMPLIFY
534                                 if ((int16_t) (time - ao_rdf_time) > 0)
535                                         time = ao_rdf_time;
536 #endif
537                         }
538 #endif /* HAS_RDF */
539 #if HAS_APRS
540                         if (ao_config.aprs_interval != 0) {
541                                 if ((int16_t) (ao_time() - ao_aprs_time) >= 0) {
542                                         ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval);
543                                         ao_aprs_send();
544                                 }
545                                 if ((int16_t) (time - ao_aprs_time) > 0)
546                                         time = ao_aprs_time;
547                         }
548 #endif /* HAS_APRS */
549                         delay = time - ao_time();
550                         if (delay > 0) {
551                                 ao_sleep_for(&telemetry, delay);
552                         }
553                 }
554         }
555 }
556
557 #if HAS_RADIO_RATE
558 void
559 ao_telemetry_reset_interval(void)
560 {
561         ao_telemetry_set_interval(ao_telemetry_desired_interval);
562 }
563 #endif
564
565 void
566 ao_telemetry_set_interval(uint16_t interval)
567 {
568         int8_t  cur = 0;
569
570 #if HAS_RADIO_RATE
571         /* Limit max telemetry rate based on available radio bandwidth.
572          */
573         static const uint16_t min_interval[] = {
574                 /* [AO_RADIO_RATE_38400] = */ AO_MS_TO_TICKS(100),
575                 /* [AO_RADIO_RATE_9600] = */ AO_MS_TO_TICKS(500),
576                 /* [AO_RADIO_RATE_2400] = */ AO_MS_TO_TICKS(1000)
577         };
578
579         ao_telemetry_desired_interval = interval;
580         if (interval && interval < min_interval[ao_config.radio_rate])
581                 interval = min_interval[ao_config.radio_rate];
582 #endif
583         ao_telemetry_interval = interval;
584 #if AO_SEND_MEGA
585         if (interval > 1)
586                 ao_telemetry_mega_data_max = 1;
587         else
588                 ao_telemetry_mega_data_max = 2;
589         if (ao_telemetry_mega_data_max > cur)
590                 cur++;
591         ao_telemetry_mega_data_cur = cur;
592 #endif
593 #if AO_SEND_METRUM
594         ao_telemetry_metrum_data_max = AO_SEC_TO_TICKS(1) / interval;
595         if (ao_telemetry_metrum_data_max > cur)
596                 cur++;
597         ao_telemetry_metrum_data_cur = cur;
598 #endif
599
600 #if HAS_COMPANION
601         if (!ao_companion_setup.update_period)
602                 ao_companion_setup.update_period = AO_SEC_TO_TICKS(1);
603         ao_telemetry_companion_max = ao_companion_setup.update_period / interval;
604         if (ao_telemetry_companion_max > cur)
605                 cur++;
606         ao_telemetry_companion_cur = cur;
607 #endif
608
609 #if HAS_GPS
610         ao_telemetry_gps_max = AO_SEC_TO_TICKS(1) / interval;
611         if (ao_telemetry_gps_max > cur)
612                 cur++;
613         ao_telemetry_loc_cur = cur;
614         if (ao_telemetry_gps_max > cur)
615                 cur++;
616         ao_telemetry_sat_cur = cur;
617 #endif
618
619         ao_telemetry_config_max = AO_SEC_TO_TICKS(5) / interval;
620         if (ao_telemetry_config_max > cur)
621                 cur++;
622         ao_telemetry_config_cur = cur;
623
624 #ifndef SIMPLIFY
625         ao_telemetry_time = 
626 #if HAS_RDF
627                 ao_rdf_time =
628 #endif
629 #if HAS_APRS
630                 ao_aprs_time =
631 #endif
632                 ao_time();
633 #endif
634         ao_wakeup(&telemetry);
635 }
636
637 #if HAS_RDF
638 void
639 ao_rdf_set(uint8_t rdf)
640 {
641         ao_rdf = rdf;
642         if (rdf == 0)
643                 ao_radio_rdf_abort();
644         else {
645                 ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
646         }
647 }
648 #endif
649
650 struct ao_task  ao_telemetry_task;
651
652 void
653 ao_telemetry_init()
654 {
655         ao_add_task(&ao_telemetry_task, ao_telemetry, "telemetry");
656 }