altos/test: Adjust CRC error rate after FEC fix
[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 AO_TICK_TYPE     ao_telemetry_time;
44 #endif
45
46 #if HAS_RDF
47 static RDF_SPACE uint8_t ao_rdf = 0;
48 static RDF_SPACE AO_TICK_TYPE   ao_rdf_time;
49 #endif
50
51 #if HAS_APRS
52 static AO_TICK_TYPE 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 = (uint16_t) packet->tick;
142 #if AO_LOG_NORMALIZED
143 #if AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
144         telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983;
145 #else
146 #error unknown normalized log type
147 #endif
148
149 #if HAS_GYRO
150         telemetry.mega_norm.orient = (uint8_t) ao_sample_orient;
151 #endif
152         telemetry.mega_norm.accel = ao_data_accel(packet);
153         telemetry.mega_norm.pres = ao_data_pres(packet);
154         telemetry.mega_norm.temp = ao_data_temp(packet);
155
156 #if HAS_MPU6000
157         telemetry.mega_norm.accel_along = ao_data_along(packet);
158         telemetry.mega_norm.accel_across = ao_data_across(packet);
159         telemetry.mega_norm.accel_through = ao_data_through(packet);
160
161         telemetry.mega_norm.gyro_roll = ao_data_roll(packet);
162         telemetry.mega_norm.gyro_pitch = ao_data_pitch(packet);
163         telemetry.mega_norm.gyro_yaw = ao_data_yaw(packet);
164 #endif
165
166 #if HAS_MMC5983
167         telemetry.mega_norm.mag_along = ao_data_mag_along(packet);
168         telemetry.mega_norm.mag_across = ao_data_mag_across(packet);
169         telemetry.mega_norm.mag_through = ao_data_mag_through(packet);
170 #endif
171
172 #else
173
174 #if HAS_BMX160
175         telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR_BMX160;
176 #else
177 #if HAS_MPU6000 || HAS_MPU9250
178         telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR_MPU;
179 #else
180 #error unknown IMU
181 #endif
182 #endif
183
184 #if HAS_GYRO
185         telemetry.mega_sensor.orient = (uint8_t) ao_sample_orient;
186 #endif
187         telemetry.mega_sensor.accel = ao_data_accel(packet);
188         telemetry.mega_sensor.pres = ao_data_pres(packet);
189         telemetry.mega_sensor.temp = ao_data_temp(packet);
190
191 #if HAS_MPU6000
192         telemetry.mega_sensor.accel_x = packet->mpu6000.accel_x;
193         telemetry.mega_sensor.accel_y = packet->mpu6000.accel_y;
194         telemetry.mega_sensor.accel_z = packet->mpu6000.accel_z;
195
196         telemetry.mega_sensor.gyro_x = packet->mpu6000.gyro_x;
197         telemetry.mega_sensor.gyro_y = packet->mpu6000.gyro_y;
198         telemetry.mega_sensor.gyro_z = packet->mpu6000.gyro_z;
199 #endif
200
201 #if HAS_HMC5883
202         telemetry.mega_sensor.mag_x = packet->hmc5883.x;
203         telemetry.mega_sensor.mag_z = packet->hmc5883.z;
204         telemetry.mega_sensor.mag_y = packet->hmc5883.y;
205 #endif
206
207 #if HAS_MPU9250
208         telemetry.mega_sensor.accel_x = packet->mpu9250.accel_x;
209         telemetry.mega_sensor.accel_y = packet->mpu9250.accel_y;
210         telemetry.mega_sensor.accel_z = packet->mpu9250.accel_z;
211
212         telemetry.mega_sensor.gyro_x = packet->mpu9250.gyro_x;
213         telemetry.mega_sensor.gyro_y = packet->mpu9250.gyro_y;
214         telemetry.mega_sensor.gyro_z = packet->mpu9250.gyro_z;
215
216         telemetry.mega_sensor.mag_x = packet->mpu9250.mag_x;
217         telemetry.mega_sensor.mag_z = packet->mpu9250.mag_z;
218         telemetry.mega_sensor.mag_y = packet->mpu9250.mag_y;
219 #endif
220
221 #if HAS_BMX160
222         telemetry.mega_sensor.accel_x = packet->bmx160.acc_x;
223         telemetry.mega_sensor.accel_y = packet->bmx160.acc_y;
224         telemetry.mega_sensor.accel_z = packet->bmx160.acc_z;
225
226         telemetry.mega_sensor.gyro_x = packet->bmx160.gyr_x;
227         telemetry.mega_sensor.gyro_y = packet->bmx160.gyr_y;
228         telemetry.mega_sensor.gyro_z = packet->bmx160.gyr_z;
229
230         telemetry.mega_sensor.mag_x = packet->bmx160.mag_x;
231         telemetry.mega_sensor.mag_z = packet->bmx160.mag_z;
232         telemetry.mega_sensor.mag_y = packet->bmx160.mag_y;
233 #endif
234 #endif
235         ao_telemetry_send();
236 }
237
238 static int16_t ao_telemetry_mega_data_max;
239 static int16_t ao_telemetry_mega_data_cur;
240
241 /* Send mega data packet */
242 static void
243 ao_send_mega_data(void)
244 {
245         if (--ao_telemetry_mega_data_cur <= 0) {
246                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
247                 uint8_t i;
248
249                 telemetry.generic.tick = (uint16_t) packet->tick;
250                 telemetry.generic.type = AO_TELEMETRY_MEGA_DATA;
251
252                 telemetry.mega_data.state = ao_flight_state;
253                 telemetry.mega_data.v_batt = packet->adc.v_batt;
254                 telemetry.mega_data.v_pyro = packet->adc.v_pbatt;
255
256                 /* ADC range is 0-4095, so shift by four to save the high 8 bits */
257                 for (i = 0; i < AO_ADC_NUM_SENSE; i++)
258                         telemetry.mega_data.sense[i] = (int8_t) (packet->adc.sense[i] >> 4);
259
260                 telemetry.mega_data.ground_pres = ao_ground_pres;
261                 telemetry.mega_data.ground_accel = ao_ground_accel;
262                 telemetry.mega_data.accel_plus_g = ao_config.accel_plus_g;
263                 telemetry.mega_data.accel_minus_g = ao_config.accel_minus_g;
264
265                 telemetry.mega_data.acceleration = (int16_t) ao_accel;
266                 telemetry.mega_data.speed = (int16_t) ao_speed;
267                 telemetry.mega_data.height = (int16_t) ao_height;
268
269                 ao_telemetry_mega_data_cur = ao_telemetry_mega_data_max;
270                 ao_telemetry_send();
271         }
272 }
273 #endif /* AO_SEND_MEGA */
274
275 #ifdef AO_SEND_METRUM
276 /* Send telemetrum sensor packet */
277 static void
278 ao_send_metrum_sensor(void)
279 {
280         struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
281
282         telemetry.generic.tick = (uint16_t) packet->tick;
283         telemetry.generic.type = AO_TELEMETRY_METRUM_SENSOR;
284
285         telemetry.metrum_sensor.state = ao_flight_state;
286 #if HAS_ACCEL
287         telemetry.metrum_sensor.accel = ao_data_accel(packet);
288 #endif
289         telemetry.metrum_sensor.pres = ao_data_pres(packet);
290         telemetry.metrum_sensor.temp = ao_data_temp(packet);
291
292         telemetry.metrum_sensor.acceleration = (int16_t) ao_accel;
293         telemetry.metrum_sensor.speed = (int16_t) ao_speed;
294         telemetry.metrum_sensor.height = (int16_t) ao_height;
295
296         telemetry.metrum_sensor.v_batt = packet->adc.v_batt;
297         telemetry.metrum_sensor.sense_a = packet->adc.sense_a;
298         telemetry.metrum_sensor.sense_m = packet->adc.sense_m;
299
300         ao_telemetry_send();
301 }
302
303 static int16_t ao_telemetry_metrum_data_max;
304 static int16_t ao_telemetry_metrum_data_cur;
305
306 /* Send telemetrum data packet */
307 static void
308 ao_send_metrum_data(void)
309 {
310         if (--ao_telemetry_metrum_data_cur <= 0) {
311                 struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
312
313                 telemetry.generic.tick = (uint16_t) packet->tick;
314                 telemetry.generic.type = AO_TELEMETRY_METRUM_DATA;
315
316                 telemetry.metrum_data.ground_pres = ao_ground_pres;
317 #if HAS_ACCEL
318                 telemetry.metrum_data.ground_accel = ao_ground_accel;
319                 telemetry.metrum_data.accel_plus_g = ao_config.accel_plus_g;
320                 telemetry.metrum_data.accel_minus_g = ao_config.accel_minus_g;
321 #else
322                 telemetry.metrum_data.ground_accel = 1;
323                 telemetry.metrum_data.accel_plus_g = 0;
324                 telemetry.metrum_data.accel_minus_g = 2;
325 #endif
326
327                 ao_telemetry_metrum_data_cur = ao_telemetry_metrum_data_max;
328                 ao_telemetry_send();
329         }
330 }
331 #endif /* AO_SEND_METRUM */
332
333 #ifdef AO_SEND_MINI
334
335 static void
336 ao_send_mini(void)
337 {
338         struct ao_data *packet = (struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
339
340         telemetry.generic.tick = (uint16_t) packet->tick;
341         telemetry.generic.type = AO_SEND_MINI;
342
343         telemetry.mini.state = ao_flight_state;
344
345         telemetry.mini.v_batt = packet->adc.v_batt;
346         telemetry.mini.sense_a = packet->adc.sense_a;
347         telemetry.mini.sense_m = packet->adc.sense_m;
348
349         telemetry.mini.pres = ao_data_pres(packet);
350         telemetry.mini.temp = ao_data_temp(packet);
351
352         telemetry.mini.acceleration = (int16_t) ao_accel;
353         telemetry.mini.speed = (int16_t) ao_speed;
354         telemetry.mini.height = (int16_t) ao_height;
355
356         telemetry.mini.ground_pres = ao_ground_pres;
357
358         ao_telemetry_send();
359 }
360
361 #endif /* AO_SEND_MINI */
362
363 static int16_t ao_telemetry_config_max;
364 static int16_t ao_telemetry_config_cur;
365 static uint16_t ao_telemetry_flight_number;
366
367 #ifndef ao_telemetry_battery_convert
368 #define ao_telemetry_battery_convert(a) (a)
369 #endif
370
371 static void
372 ao_send_configuration(void)
373 {
374         if (--ao_telemetry_config_cur <= 0)
375         {
376                 telemetry.generic.type = AO_TELEMETRY_CONFIGURATION;
377                 telemetry.configuration.device = AO_idProduct_NUMBER;
378                 telemetry.configuration.flight = ao_telemetry_flight_number;
379                 telemetry.configuration.config_major = AO_CONFIG_MAJOR;
380                 telemetry.configuration.config_minor = AO_CONFIG_MINOR;
381 #if AO_idProduct_NUMBER == 0x25 && HAS_ADC
382                 /* TeleGPS gets battery voltage instead of apogee delay */
383                 telemetry.configuration.apogee_delay = (uint16_t) ao_telemetry_battery_convert(ao_data_ring[ao_data_ring_prev(ao_data_head)].adc.v_batt);
384 #else
385                 telemetry.configuration.apogee_delay = ao_config.apogee_delay;
386                 telemetry.configuration.main_deploy = ao_config.main_deploy;
387 #endif
388
389                 telemetry.configuration.flight_log_max = (uint16_t) (ao_config.flight_log_max >> 10);
390                 memcpy (telemetry.configuration.callsign,
391                         ao_config.callsign,
392                         AO_MAX_CALLSIGN);
393                 memcpy (telemetry.configuration.version,
394                         ao_version,
395                         AO_MAX_VERSION);
396                 ao_telemetry_config_cur = ao_telemetry_config_max;
397                 ao_telemetry_send();
398         }
399 }
400
401 #if HAS_GPS
402
403 static int16_t ao_telemetry_gps_max;
404 static int16_t ao_telemetry_loc_cur;
405 static int16_t ao_telemetry_sat_cur;
406
407 static inline void *
408 telemetry_bits(struct ao_telemetry_location *l)
409 {
410         return ((uint8_t *) l) + offsetof(struct ao_telemetry_location, flags);
411 }
412
413 static inline size_t
414 telemetry_size(void)
415 {
416         return sizeof(struct ao_telemetry_location) - offsetof(struct ao_telemetry_location, flags);
417 }
418
419 static void
420 ao_send_location(void)
421 {
422         if (--ao_telemetry_loc_cur <= 0)
423         {
424                 telemetry.generic.type = AO_TELEMETRY_LOCATION;
425                 ao_mutex_get(&ao_gps_mutex);
426                 memcpy(telemetry_bits(&telemetry.location),
427                        telemetry_bits(&ao_gps_data),
428                        telemetry_size());
429                 telemetry.location.tick = (uint16_t) ao_gps_tick;
430                 ao_mutex_put(&ao_gps_mutex);
431                 ao_telemetry_loc_cur = ao_telemetry_gps_max;
432                 ao_telemetry_send();
433         }
434 }
435
436 static void
437 ao_send_satellite(void)
438 {
439         if (--ao_telemetry_sat_cur <= 0)
440         {
441                 telemetry.generic.type = AO_TELEMETRY_SATELLITE;
442                 ao_mutex_get(&ao_gps_mutex);
443                 telemetry.satellite.channels = ao_gps_tracking_data.channels;
444                 memcpy(&telemetry.satellite.sats,
445                        &ao_gps_tracking_data.sats,
446                        AO_MAX_GPS_TRACKING * sizeof (struct ao_telemetry_satellite_info));
447                 ao_mutex_put(&ao_gps_mutex);
448                 ao_telemetry_sat_cur = ao_telemetry_gps_max;
449                 ao_telemetry_send();
450         }
451 }
452 #endif
453
454 #if HAS_COMPANION
455
456 static int16_t ao_telemetry_companion_max;
457 static int16_t ao_telemetry_companion_cur;
458
459 static void
460 ao_send_companion(void)
461 {
462         if (--ao_telemetry_companion_cur <= 0) {
463                 telemetry.generic.type = AO_TELEMETRY_COMPANION;
464                 telemetry.companion.board_id = (uint8_t) ao_companion_setup.board_id;
465                 telemetry.companion.update_period = ao_companion_setup.update_period;
466                 telemetry.companion.channels = ao_companion_setup.channels;
467                 ao_mutex_get(&ao_companion_mutex);
468                 memcpy(&telemetry.companion.companion_data,
469                        ao_companion_data,
470                        ao_companion_setup.channels * 2);
471                 ao_mutex_put(&ao_companion_mutex);
472                 ao_telemetry_companion_cur = ao_telemetry_companion_max;
473                 ao_telemetry_send();
474         }
475 }
476 #endif
477
478 #if HAS_APRS
479 static void
480 ao_set_aprs_time(void)
481 {
482         uint16_t interval = ao_config.aprs_interval;
483
484         if ((ao_gps_data.flags & AO_GPS_DATE_VALID) && interval != 0) {
485                 int second = (ao_gps_data.second / interval + 1) * interval + ao_config.aprs_offset;
486                 int delta;
487                 if (second >= 60) {
488                         second = ao_config.aprs_offset;
489                         delta = second + 60 - ao_gps_data.second;
490                 } else {
491                         delta = second - ao_gps_data.second;
492                 }
493                 ao_aprs_time = ao_gps_tick + AO_SEC_TO_TICKS(delta);
494         } else {
495                 ao_aprs_time += AO_SEC_TO_TICKS(ao_config.aprs_interval);
496         }
497 }
498 #endif
499
500 static void
501 ao_telemetry(void)
502 {
503         AO_TICK_TYPE    time;
504         AO_TICK_SIGNED  delay;
505
506         ao_config_get();
507         if (!ao_config.radio_enable)
508                 ao_exit();
509         while (!ao_flight_number)
510                 ao_sleep(&ao_flight_number);
511
512         ao_telemetry_flight_number = ao_flight_number;
513 #if HAS_LOG
514         if (ao_log_full())
515                 ao_telemetry_flight_number = 0;
516 #endif
517         telemetry.generic.serial = ao_serial_number;
518         for (;;) {
519                 while (ao_telemetry_interval == 0)
520                         ao_sleep(&telemetry);
521                 time = ao_time();
522                 ao_telemetry_time = time;
523 #if HAS_RDF
524                 ao_rdf_time = time;
525 #endif
526 #if HAS_APRS
527                 ao_aprs_time = time;
528                 ao_set_aprs_time();
529 #endif
530                 while (ao_telemetry_interval) {
531                         time = ao_time() + AO_SEC_TO_TICKS(100);
532 #ifndef SIMPLIFY
533                         if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
534 #endif
535                         {
536 #ifndef SIMPLIFY
537                                 if ( (int16_t) (ao_time() - ao_telemetry_time) >= 0)
538 #endif
539                                 {
540                                         ao_telemetry_time = ao_time() + ao_telemetry_interval;
541 # ifdef AO_SEND_MEGA
542                                         ao_send_mega_sensor();
543                                         ao_send_mega_data();
544 # endif
545 # ifdef AO_SEND_METRUM
546                                         ao_send_metrum_sensor();
547                                         ao_send_metrum_data();
548 # endif
549 # ifdef AO_SEND_MINI
550                                         ao_send_mini();
551 # endif
552 # ifdef AO_TELEMETRY_SENSOR
553                                         ao_send_sensor();
554 # endif
555 #if HAS_COMPANION
556                                         if (ao_companion_running)
557                                                 ao_send_companion();
558 #endif
559 #if HAS_GPS
560                                         ao_send_location();
561                                         ao_send_satellite();
562 #endif
563                                         ao_send_configuration();
564                                 }
565 #ifndef SIMPLIFY
566                                 time = ao_telemetry_time;
567 #endif
568                         }
569 #if HAS_RDF
570                         if (ao_rdf
571 #ifndef SIMPLIFY
572                             && !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF)
573 #endif
574                                 )
575                         {
576                                 if ((int16_t) (ao_time() - ao_rdf_time) >= 0) {
577 #if HAS_IGNITE_REPORT
578                                         uint8_t c;
579 #endif
580                                         ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
581 #if HAS_IGNITE_REPORT
582                                         if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
583                                                 ao_radio_continuity(c);
584                                         else
585 #endif
586                                                 ao_radio_rdf();
587                                 }
588 #ifndef SIMPLIFY
589                                 if ((int16_t) (time - ao_rdf_time) > 0)
590                                         time = ao_rdf_time;
591 #endif
592                         }
593 #endif /* HAS_RDF */
594 #if HAS_APRS
595                         if (ao_config.aprs_interval != 0) {
596                                 if ((int16_t) (ao_time() - ao_aprs_time) >= 0) {
597                                         ao_set_aprs_time();
598                                         ao_aprs_send();
599                                 }
600                                 if ((int16_t) (time - ao_aprs_time) > 0)
601                                         time = ao_aprs_time;
602                         }
603 #endif /* HAS_APRS */
604                         delay = (AO_TICK_SIGNED) (time - ao_time());
605                         if (delay > 0) {
606                                 ao_sleep_for(&telemetry, (AO_TICK_TYPE) delay);
607                         }
608                 }
609         }
610 }
611
612 #if HAS_RADIO_RATE
613 void
614 ao_telemetry_reset_interval(void)
615 {
616         ao_telemetry_set_interval(ao_telemetry_desired_interval);
617 }
618 #endif
619
620 void
621 ao_telemetry_set_interval(uint16_t interval)
622 {
623         int16_t cur = 0;
624
625 #if HAS_RADIO_RATE
626         /* Limit max telemetry rate based on available radio bandwidth.
627          */
628         static const uint16_t min_interval[] = {
629                 /* [AO_RADIO_RATE_38400] = */ AO_MS_TO_TICKS(100),
630                 /* [AO_RADIO_RATE_9600] = */ AO_MS_TO_TICKS(500),
631                 /* [AO_RADIO_RATE_2400] = */ AO_MS_TO_TICKS(1000)
632         };
633
634         ao_telemetry_desired_interval = interval;
635         if (interval && interval < min_interval[ao_config.radio_rate])
636                 interval = min_interval[ao_config.radio_rate];
637 #endif
638         ao_telemetry_interval = interval;
639         if (interval) {
640 #if AO_SEND_MEGA
641                 if (interval > 1)
642                         ao_telemetry_mega_data_max = 1;
643                 else
644                         ao_telemetry_mega_data_max = 2;
645                 if (ao_telemetry_mega_data_max > cur)
646                         cur++;
647                 ao_telemetry_mega_data_cur = cur;
648 #endif
649 #if AO_SEND_METRUM
650                 ao_telemetry_metrum_data_max = (int16_t) (AO_SEC_TO_TICKS(1) / interval);
651                 if (ao_telemetry_metrum_data_max > cur)
652                         cur++;
653                 ao_telemetry_metrum_data_cur = cur;
654 #endif
655
656 #if HAS_COMPANION
657                 if (!ao_companion_setup.update_period)
658                         ao_companion_setup.update_period = AO_SEC_TO_TICKS(1);
659                 ao_telemetry_companion_max = (int16_t) (ao_companion_setup.update_period / interval);
660                 if (ao_telemetry_companion_max > cur)
661                         cur++;
662                 ao_telemetry_companion_cur = cur;
663 #endif
664
665 #if HAS_GPS
666                 ao_telemetry_gps_max = (int16_t) (AO_SEC_TO_TICKS(1) / interval);
667                 if (ao_telemetry_gps_max > cur)
668                         cur++;
669                 ao_telemetry_loc_cur = cur;
670                 if (ao_telemetry_gps_max > cur)
671                         cur++;
672                 ao_telemetry_sat_cur = cur;
673 #endif
674
675                 ao_telemetry_config_max = (int16_t) (AO_SEC_TO_TICKS(5) / interval);
676                 if (ao_telemetry_config_max > cur)
677                         cur++;
678                 ao_telemetry_config_cur = cur;
679         }
680
681 #ifndef SIMPLIFY
682         ao_telemetry_time = 
683 #if HAS_RDF
684                 ao_rdf_time =
685 #endif
686 #if HAS_APRS
687                 ao_aprs_time =
688 #endif
689                 ao_time();
690 #endif
691         ao_wakeup(&telemetry);
692 }
693
694 #if HAS_RDF
695 void
696 ao_rdf_set(uint8_t rdf)
697 {
698         ao_rdf = rdf;
699         if (rdf == 0)
700                 ao_radio_rdf_abort();
701         else {
702                 ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
703         }
704 }
705 #endif
706
707 struct ao_task  ao_telemetry_task;
708
709 void
710 ao_telemetry_init()
711 {
712         ao_add_task(&ao_telemetry_task, ao_telemetry, "telemetry");
713 }