LP-500 HoTT Bridge Module ported from TauLabs
[librepilot.git] / flight / modules / Telemetry / telemetry.c
blob9796e8f5999d8612e068f2c1abda3ddee77877de
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
4 * @{
5 * @addtogroup TelemetryModule Telemetry Module
6 * @brief Main telemetry module
7 * Starts three tasks (RX, TX, and priority TX) that watch event queues
8 * and handle all the telemetry of the UAVobjects
9 * @{
11 * @file telemetry.c
12 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
13 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2015.
14 * @brief Telemetry module, handles telemetry and UAVObject updates
16 * @see The GNU Public License (GPL) Version 3
18 *****************************************************************************/
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful, but
26 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * for more details.
30 * You should have received a copy of the GNU General Public License along
31 * with this program; if not, write to the Free Software Foundation, Inc.,
32 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 /* Telemetry uses four tasks. Two are created for the main telemetry
36 * stream called "TelTx" and "TelRx". Two are created to handle the OPLink
37 * radio connection, called "RadioTx" and "RadioRx", the latter being
38 * overridden by USB if connected.
40 * The following code uses a "local" prefix to refer to the telemetry channel
41 * associated with a port on the FC and a "radio" prefix to refer to the
42 * telemetry channel associated with the OPLink/USB.
44 * The "local" telemetry port to use is defined by PIOS_COM_TELEM_RF in
45 * PIOS_Board_Init().
47 * A UAVTalk connection instance, telemUavTalkCon, is associated with the
48 * "local" channel and another, radioUavTalkCon, with the "radio" channel.
49 * Associated with each instance is a transmit routine which will send data
50 * to the appropriate port.
52 * Data is passed on the telemetry channels using queues. If
53 * PIOS_TELEM_PRIORITY_QUEUE is defined then two queues are created, one normal
54 * priority and the other high priority.
56 * The "Tx" tasks read events first from the priority queue and then from
57 * the normal queue, passing each event to processObjEvent() which ultimately
58 * passes each event to the UAVTalk library which results in the appropriate
59 * transmit routine being called to send the data back to the recipient on
60 * the "local" or "radio" link.
63 #include <openpilot.h>
65 #include "telemetry.h"
67 #include "flighttelemetrystats.h"
68 #include "gcstelemetrystats.h"
69 #include "hwsettings.h"
70 #include "taskinfo.h"
72 // Private constants
73 #define MAX_QUEUE_SIZE TELEM_QUEUE_SIZE
74 // Three different stack size parameter are accepted for Telemetry(RX PIOS_TELEM_RX_STACK_SIZE)
75 // Tx(PIOS_TELEM_TX_STACK_SIZE) and Radio RX(PIOS_TELEM_RADIO_RX_STACK_SIZE)
76 #ifdef PIOS_TELEM_RX_STACK_SIZE
77 #define STACK_SIZE_RX_BYTES PIOS_TELEM_RX_STACK_SIZE
78 #define STACK_SIZE_TX_BYTES PIOS_TELEM_TX_STACK_SIZE
79 #else
80 #define STACK_SIZE_RX_BYTES PIOS_TELEM_STACK_SIZE
81 #define STACK_SIZE_TX_BYTES PIOS_TELEM_STACK_SIZE
82 #endif
84 #ifdef PIOS_TELEM_RADIO_RX_STACK_SIZE
85 #define STACK_SIZE_RADIO_RX_BYTES PIOS_TELEM_RADIO_RX_STACK_SIZE
86 #define STACK_SIZE_RADIO_TX_BYTES PIOS_TELEM_RADIO_TX_STACK_SIZE
87 #else
88 #define STACK_SIZE_RADIO_RX_BYTES STACK_SIZE_RX_BYTES
89 #define STACK_SIZE_RADIO_TX_BYTES STACK_SIZE_TX_BYTES
90 #endif
91 #define TASK_PRIORITY_RX (tskIDLE_PRIORITY + 2)
92 #define TASK_PRIORITY_TX (tskIDLE_PRIORITY + 2)
93 #define TASK_PRIORITY_RADRX (tskIDLE_PRIORITY + 2)
94 #define TASK_PRIORITY_RADTX (tskIDLE_PRIORITY + 2)
95 #define REQ_TIMEOUT_MS 250
96 #define MAX_RETRIES 2
97 #define STATS_UPDATE_PERIOD_MS 4000
98 #define CONNECTION_TIMEOUT_MS 8000
100 #ifdef PIOS_INCLUDE_RFM22B
101 #define HAS_RADIO
102 #endif
104 // Private types
105 typedef struct {
106 // Determine port on which to communicate telemetry information
107 uint32_t (*getPort)();
108 // Main telemetry queue
109 xQueueHandle queue;
111 #ifdef PIOS_TELEM_PRIORITY_QUEUE
112 // Priority telemetry queue
113 xQueueHandle priorityQueue;
114 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
116 // Transmit/receive task handles
117 xTaskHandle txTaskHandle;
118 xTaskHandle rxTaskHandle;
119 // Telemetry stream
120 UAVTalkConnection uavTalkCon;
121 } channelContext;
123 #ifdef HAS_RADIO
124 // Main telemetry channel
125 static channelContext localChannel;
126 static int32_t transmitLocalData(uint8_t *data, int32_t length);
127 static void registerLocalObject(UAVObjHandle obj);
128 static uint32_t localPort();
129 #endif /* ifdef HAS_RADIO */
131 static void updateSettings(channelContext *channel);
133 // OPLink telemetry channel
134 static channelContext radioChannel;
135 static int32_t transmitRadioData(uint8_t *data, int32_t length);
136 static void registerRadioObject(UAVObjHandle obj);
137 static uint32_t radioPort();
138 static uint32_t radio_port;
141 // Telemetry stats
142 static uint32_t txErrors;
143 static uint32_t txRetries;
144 static uint32_t timeOfLastObjectUpdate;
146 static void telemetryTxTask(void *parameters);
147 static void telemetryRxTask(void *parameters);
148 static void updateObject(
149 channelContext *channel,
150 UAVObjHandle obj,
151 int32_t eventType);
152 static void processObjEvent(
153 channelContext *channel,
154 UAVObjEvent *ev);
155 static int32_t setUpdatePeriod(
156 channelContext *channel,
157 UAVObjHandle obj,
158 int32_t updatePeriodMs);
159 static int32_t setLoggingPeriod(
160 channelContext *channel,
161 UAVObjHandle obj,
162 int32_t updatePeriodMs);
163 static void updateTelemetryStats();
164 static void gcsTelemetryStatsUpdated();
167 * Initialise the telemetry module
168 * \return -1 if initialisation failed
169 * \return 0 on success
171 int32_t TelemetryStart(void)
173 #ifdef HAS_RADIO
174 // Only start the local telemetry tasks if needed
175 if (localPort()) {
176 UAVObjIterate(&registerLocalObject);
178 // Listen to objects of interest
179 #ifdef PIOS_TELEM_PRIORITY_QUEUE
180 GCSTelemetryStatsConnectQueue(localChannel.priorityQueue);
181 #else /* PIOS_TELEM_PRIORITY_QUEUE */
182 GCSTelemetryStatsConnectQueue(localChannel.queue);
183 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
184 // Start telemetry tasks
185 xTaskCreate(telemetryTxTask,
186 "TelTx",
187 STACK_SIZE_TX_BYTES / 4,
188 &localChannel,
189 TASK_PRIORITY_TX,
190 &localChannel.txTaskHandle);
191 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYTX,
192 localChannel.txTaskHandle);
193 xTaskCreate(telemetryRxTask,
194 "TelRx",
195 STACK_SIZE_RX_BYTES / 4,
196 &localChannel,
197 TASK_PRIORITY_RX,
198 &localChannel.rxTaskHandle);
199 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYRX,
200 localChannel.rxTaskHandle);
202 #endif /* ifdef HAS_RADIO */
204 // Start the telemetry tasks associated with Radio/USB
205 UAVObjIterate(&registerRadioObject);
207 // Listen to objects of interest
208 #ifdef PIOS_TELEM_PRIORITY_QUEUE
209 GCSTelemetryStatsConnectQueue(radioChannel.priorityQueue);
210 #else /* PIOS_TELEM_PRIORITY_QUEUE */
211 GCSTelemetryStatsConnectQueue(radioChannel.queue);
212 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
214 xTaskCreate(telemetryTxTask,
215 "RadioTx",
216 STACK_SIZE_RADIO_TX_BYTES / 4,
217 &radioChannel,
218 TASK_PRIORITY_RADTX,
219 &radioChannel.txTaskHandle);
220 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_RADIOTX,
221 radioChannel.txTaskHandle);
222 xTaskCreate(telemetryRxTask,
223 "RadioRx",
224 STACK_SIZE_RADIO_RX_BYTES / 4,
225 &radioChannel,
226 TASK_PRIORITY_RADRX,
227 &radioChannel.rxTaskHandle);
228 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_RADIORX,
229 radioChannel.rxTaskHandle);
231 return 0;
234 /* Intialise a telemetry channel */
235 void TelemetryInitializeChannel(channelContext *channel)
237 // Create object queues
238 channel->queue = xQueueCreate(MAX_QUEUE_SIZE,
239 sizeof(UAVObjEvent));
241 #if defined(PIOS_TELEM_PRIORITY_QUEUE)
242 channel->priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
243 sizeof(UAVObjEvent));
244 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
246 // Create periodic event that will be used to update the telemetry stats
247 UAVObjEvent ev;
248 memset(&ev, 0, sizeof(UAVObjEvent));
250 #ifdef PIOS_TELEM_PRIORITY_QUEUE
251 EventPeriodicQueueCreate(&ev,
252 channel->priorityQueue,
253 STATS_UPDATE_PERIOD_MS);
254 #else /* PIOS_TELEM_PRIORITY_QUEUE */
255 EventPeriodicQueueCreate(&ev,
256 channel->queue,
257 STATS_UPDATE_PERIOD_MS);
258 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
262 * Initialise the telemetry module
263 * \return -1 if initialisation failed
264 * \return 0 on success
266 int32_t TelemetryInitialize(void)
268 HwSettingsInitialize();
270 #ifdef PIOS_INCLUDE_RFM22B
271 OPLinkSettingsInitialize();
272 OPLinkSettingsData data;
274 OPLinkSettingsGet(&data);
275 bool ppm_only = (data.LinkType == OPLINKSETTINGS_LINKTYPE_CONTROL);
276 if (ppm_only) {
277 radio_port = 0;
278 } else {
279 radio_port = PIOS_COM_RF;
281 #else /* PIOS_INCLUDE_RFM22B */
282 radio_port = PIOS_COM_TELEM_RF;
283 #endif /* PIOS_INCLUDE_RFM22B */
285 FlightTelemetryStatsInitialize();
286 GCSTelemetryStatsInitialize();
288 // Initialize vars
289 timeOfLastObjectUpdate = 0;
291 // Reset link stats
292 txErrors = 0;
293 txRetries = 0;
295 #ifdef HAS_RADIO
296 // Set channel port handlers
297 localChannel.getPort = localPort;
299 // Set the local telemetry baud rate
300 updateSettings(&localChannel);
302 // Only initialise local channel if telemetry port enabled
303 if (localPort()) {
304 // Initialise channel
305 TelemetryInitializeChannel(&localChannel);
306 // Initialise UAVTalk
307 localChannel.uavTalkCon = UAVTalkInitialize(&transmitLocalData);
309 #endif /* ifdef HAS_RADIO */
311 // Set channel port handlers
312 radioChannel.getPort = radioPort;
314 // Set the channel port baud rate
315 updateSettings(&radioChannel);
317 // Initialise channel
318 TelemetryInitializeChannel(&radioChannel);
319 // Initialise UAVTalk
320 radioChannel.uavTalkCon = UAVTalkInitialize(&transmitRadioData);
322 return 0;
325 MODULE_INITCALL(TelemetryInitialize, TelemetryStart);
327 #ifdef HAS_RADIO
329 * Register a new object, adds object to local list and connects the queue depending on the object's
330 * telemetry settings.
331 * \param[in] obj Object to connect
333 static void registerLocalObject(UAVObjHandle obj)
335 if (UAVObjIsMetaobject(obj)) {
336 // Only connect change notifications for meta objects. No periodic updates
337 #ifdef PIOS_TELEM_PRIORITY_QUEUE
338 UAVObjConnectQueue(obj, localChannel.priorityQueue, EV_MASK_ALL_UPDATES);
339 #else /* PIOS_TELEM_PRIORITY_QUEUE */
340 UAVObjConnectQueue(obj, localChannel.queue, EV_MASK_ALL_UPDATES);
341 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
342 } else {
343 // Setup object for periodic updates
344 updateObject(
345 &localChannel,
346 obj,
347 EV_NONE);
350 #endif /* ifdef HAS_RADIO */
352 static void registerRadioObject(UAVObjHandle obj)
354 if (UAVObjIsMetaobject(obj)) {
355 // Only connect change notifications for meta objects. No periodic updates
356 #ifdef PIOS_TELEM_PRIORITY_QUEUE
357 UAVObjConnectQueue(obj, radioChannel.priorityQueue, EV_MASK_ALL_UPDATES);
358 #else /* PIOS_TELEM_PRIORITY_QUEUE */
359 UAVObjConnectQueue(obj, radioChannel.queue, EV_MASK_ALL_UPDATES);
360 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
361 } else {
362 // Setup object for periodic updates
363 updateObject(
364 &radioChannel,
365 obj,
366 EV_NONE);
371 * Update object's queue connections and timer, depending on object's settings
372 * \param[in] telemetry channel context
373 * \param[in] obj Object to updates
375 static void updateObject(
376 channelContext *channel,
377 UAVObjHandle obj,
378 int32_t eventType)
380 UAVObjMetadata metadata;
381 UAVObjUpdateMode updateMode, loggingMode;
382 int32_t eventMask;
384 if (UAVObjIsMetaobject(obj)) {
385 // This function updates the periodic updates for the object.
386 // Meta Objects cannot have periodic updates.
387 PIOS_Assert(false);
388 return;
391 // Get metadata
392 UAVObjGetMetadata(obj, &metadata);
393 updateMode = UAVObjGetTelemetryUpdateMode(&metadata);
394 loggingMode = UAVObjGetLoggingUpdateMode(&metadata);
396 // Setup object depending on update mode
397 eventMask = 0;
398 switch (updateMode) {
399 case UPDATEMODE_PERIODIC:
400 // Set update period
401 setUpdatePeriod(channel,
402 obj,
403 metadata.telemetryUpdatePeriod);
404 // Connect queue
405 eventMask |= EV_UPDATED_PERIODIC | EV_UPDATED_MANUAL | EV_UPDATE_REQ;
406 break;
407 case UPDATEMODE_ONCHANGE:
408 // Set update period
409 setUpdatePeriod(channel, obj, 0);
410 // Connect queue
411 eventMask |= EV_UPDATED | EV_UPDATED_MANUAL | EV_UPDATE_REQ;
412 break;
413 case UPDATEMODE_THROTTLED:
414 if ((eventType == EV_UPDATED_PERIODIC) || (eventType == EV_NONE)) {
415 // If we received a periodic update, we can change back to update on change
416 eventMask |= EV_UPDATED | EV_UPDATED_MANUAL | EV_UPDATE_REQ;
417 // Set update period on initialization and metadata change
418 if (eventType == EV_NONE) {
419 setUpdatePeriod(channel,
420 obj,
421 metadata.telemetryUpdatePeriod);
423 } else {
424 // Otherwise, we just received an object update, so switch to periodic for the timeout period to prevent more updates
425 eventMask |= EV_UPDATED_PERIODIC | EV_UPDATED_MANUAL | EV_UPDATE_REQ;
427 break;
428 case UPDATEMODE_MANUAL:
429 // Set update period
430 setUpdatePeriod(channel, obj, 0);
431 // Connect queue
432 eventMask |= EV_UPDATED_MANUAL | EV_UPDATE_REQ;
433 break;
435 switch (loggingMode) {
436 case UPDATEMODE_PERIODIC:
437 // Set update period
438 setLoggingPeriod(channel, obj, metadata.loggingUpdatePeriod);
439 // Connect queue
440 eventMask |= EV_LOGGING_PERIODIC | EV_LOGGING_MANUAL;
441 break;
442 case UPDATEMODE_ONCHANGE:
443 // Set update period
444 setLoggingPeriod(channel, obj, 0);
445 // Connect queue
446 eventMask |= EV_UPDATED | EV_LOGGING_MANUAL;
447 break;
448 case UPDATEMODE_THROTTLED:
449 if ((eventType == EV_LOGGING_PERIODIC) || (eventType == EV_NONE)) {
450 // If we received a periodic update, we can change back to update on change
451 eventMask |= EV_UPDATED | EV_LOGGING_MANUAL;
452 // Set update period on initialization and metadata change
453 if (eventType == EV_NONE) {
454 setLoggingPeriod(channel,
455 obj,
456 metadata.loggingUpdatePeriod);
458 } else {
459 // Otherwise, we just received an object update, so switch to periodic for the timeout period to prevent more updates
460 eventMask |= EV_LOGGING_PERIODIC | EV_LOGGING_MANUAL;
462 break;
463 case UPDATEMODE_MANUAL:
464 // Set update period
465 setLoggingPeriod(channel, obj, 0);
466 // Connect queue
467 eventMask |= EV_LOGGING_MANUAL;
468 break;
471 // note that all setting objects have implicitly IsPriority=true
472 #ifdef PIOS_TELEM_PRIORITY_QUEUE
473 if (UAVObjIsPriority(obj)) {
474 UAVObjConnectQueue(obj, channel->priorityQueue, eventMask);
475 } else
476 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
478 UAVObjConnectQueue(obj, channel->queue, eventMask);
483 * Processes queue events
485 static void processObjEvent(
486 channelContext *channel,
487 UAVObjEvent *ev)
489 UAVObjMetadata metadata;
490 UAVObjUpdateMode updateMode;
491 int32_t retries;
492 int32_t success;
494 if (ev->obj == 0) {
495 updateTelemetryStats();
496 } else if (ev->obj == GCSTelemetryStatsHandle()) {
497 gcsTelemetryStatsUpdated();
498 } else {
499 // Get object metadata
500 UAVObjGetMetadata(ev->obj, &metadata);
501 updateMode = UAVObjGetTelemetryUpdateMode(&metadata);
503 // Act on event
504 retries = 0;
505 success = -1;
506 if ((ev->event == EV_UPDATED && (updateMode == UPDATEMODE_ONCHANGE || updateMode == UPDATEMODE_THROTTLED))
507 || ev->event == EV_UPDATED_MANUAL
508 || (ev->event == EV_UPDATED_PERIODIC && updateMode != UPDATEMODE_THROTTLED)) {
509 // Send update to GCS (with retries)
510 while (retries < MAX_RETRIES && success == -1) {
511 // call blocks until ack is received or timeout
512 success = UAVTalkSendObject(channel->uavTalkCon,
513 ev->obj,
514 ev->instId,
515 UAVObjGetTelemetryAcked(&metadata), REQ_TIMEOUT_MS);
516 if (success == -1) {
517 ++retries;
520 // Update stats
521 txRetries += retries;
522 if (success == -1) {
523 ++txErrors;
525 } else if (ev->event == EV_UPDATE_REQ) {
526 // Request object update from GCS (with retries)
527 while (retries < MAX_RETRIES && success == -1) {
528 // call blocks until update is received or timeout
529 success = UAVTalkSendObjectRequest(channel->uavTalkCon,
530 ev->obj,
531 ev->instId,
532 REQ_TIMEOUT_MS);
533 if (success == -1) {
534 ++retries;
537 // Update stats
538 txRetries += retries;
539 if (success == -1) {
540 ++txErrors;
543 // If this is a metaobject then make necessary telemetry updates
544 if (UAVObjIsMetaobject(ev->obj)) {
545 // linked object will be the actual object the metadata are for
546 updateObject(
547 channel,
548 UAVObjGetLinkedObj(ev->obj),
549 EV_NONE);
550 } else {
551 if (updateMode == UPDATEMODE_THROTTLED) {
552 // If this is UPDATEMODE_THROTTLED, the event mask changes on every event.
553 updateObject(
554 channel,
555 ev->obj,
556 ev->event);
560 // Log UAVObject if necessary
561 if (ev->obj) {
562 updateMode = UAVObjGetLoggingUpdateMode(&metadata);
563 if ((ev->event == EV_UPDATED && (updateMode == UPDATEMODE_ONCHANGE || updateMode == UPDATEMODE_THROTTLED))
564 || ev->event == EV_LOGGING_MANUAL
565 || (ev->event == EV_LOGGING_PERIODIC && updateMode != UPDATEMODE_THROTTLED)) {
566 if (ev->instId == UAVOBJ_ALL_INSTANCES) {
567 success = UAVObjGetNumInstances(ev->obj);
568 for (retries = 0; retries < success; retries++) {
569 UAVObjInstanceWriteToLog(ev->obj, retries);
571 } else {
572 UAVObjInstanceWriteToLog(ev->obj, ev->instId);
575 if (updateMode == UPDATEMODE_THROTTLED) {
576 // If this is UPDATEMODE_THROTTLED, the event mask changes on every event.
577 updateObject(
578 channel,
579 ev->obj,
580 ev->event);
586 * Telemetry transmit task, regular priority
588 static void telemetryTxTask(void *parameters)
590 channelContext *channel = (channelContext *)parameters;
591 UAVObjEvent ev;
593 /* Check for a bad context */
594 if (!channel) {
595 return;
598 // Loop forever
599 while (1) {
601 * Tries to empty the high priority queue before handling any standard priority item
604 #ifdef PIOS_TELEM_PRIORITY_QUEUE
605 // empty priority queue, non-blocking
606 while (xQueueReceive(channel->priorityQueue, &ev, 0) == pdTRUE) {
607 // Process event
608 processObjEvent(channel, &ev);
610 // check regular queue and process update - non-blocking
611 if (xQueueReceive(channel->queue, &ev, 0) == pdTRUE) {
612 // Process event
613 processObjEvent(channel, &ev);
614 // if both queues are empty, wait on priority queue for updates (1 tick) then repeat cycle
615 } else if (xQueueReceive(channel->priorityQueue, &ev, 1) == pdTRUE) {
616 // Process event
617 processObjEvent(channel, &ev);
619 #else
620 // wait on queue for updates (1 tick) then repeat cycle
621 if (xQueueReceive(channel->queue, &ev, 1) == pdTRUE) {
622 // Process event
623 processObjEvent(channel, &ev);
625 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
631 * Telemetry receive task. Processes queue events and periodic updates.
633 static void telemetryRxTask(void *parameters)
635 channelContext *channel = (channelContext *)parameters;
637 /* Check for a bad context */
638 if (!channel) {
639 return;
642 // Task loop
643 while (1) {
644 uint32_t inputPort = channel->getPort();
646 if (inputPort) {
647 // Block until data are available
648 uint8_t serial_data[16];
649 uint16_t bytes_to_process;
651 bytes_to_process = PIOS_COM_ReceiveBuffer(inputPort, serial_data, sizeof(serial_data), 500);
652 if (bytes_to_process > 0) {
653 UAVTalkProcessInputStream(channel->uavTalkCon, serial_data, bytes_to_process);
655 } else {
656 vTaskDelay(5);
661 #ifdef HAS_RADIO
663 * Determine the port to be used for communication on the telemetry channel
664 * \return com port number
666 static uint32_t localPort()
668 return PIOS_COM_TELEM_RF;
671 #endif /* ifdef HAS_RADIO */
674 * Determine the port to be used for communication on the radio channel
675 * \return com port number
677 static uint32_t radioPort()
679 uint32_t port = radio_port;
681 #ifdef PIOS_INCLUDE_USB
682 // if USB is connected, USB takes precedence for telemetry
683 if (PIOS_COM_Available(PIOS_COM_TELEM_USB)) {
684 port = PIOS_COM_TELEM_USB;
686 #endif /* PIOS_INCLUDE_USB */
688 return port;
691 #ifdef HAS_RADIO
693 * Transmit data buffer to the modem or USB port.
694 * \param[in] data Data buffer to send
695 * \param[in] length Length of buffer
696 * \return -1 on failure
697 * \return number of bytes transmitted on success
699 static int32_t transmitLocalData(uint8_t *data, int32_t length)
701 uint32_t outputPort = localChannel.getPort();
703 if (outputPort) {
704 return PIOS_COM_SendBuffer(outputPort, data, length);
707 return -1;
709 #endif /* ifdef HAS_RADIO */
712 * Transmit data buffer to the radioport.
713 * \param[in] data Data buffer to send
714 * \param[in] length Length of buffer
715 * \return -1 on failure
716 * \return number of bytes transmitted on success
718 static int32_t transmitRadioData(uint8_t *data, int32_t length)
720 uint32_t outputPort = radioChannel.getPort();
722 if (outputPort) {
723 return PIOS_COM_SendBuffer(outputPort, data, length);
726 return -1;
730 * Set update period of object (it must be already setup for periodic updates)
731 * \param[in] telemetry channel context
732 * \param[in] obj The object to update
733 * \param[in] updatePeriodMs The update period in ms, if zero then periodic updates are disabled
734 * \return 0 Success
735 * \return -1 Failure
737 static int32_t setUpdatePeriod(
738 channelContext *channel,
739 UAVObjHandle obj,
740 int32_t updatePeriodMs)
742 UAVObjEvent ev;
743 int32_t ret;
745 // Add or update object for periodic updates
746 ev.obj = obj;
747 ev.instId = UAVOBJ_ALL_INSTANCES;
748 ev.event = EV_UPDATED_PERIODIC;
749 ev.lowPriority = true;
751 #ifdef PIOS_TELEM_PRIORITY_QUEUE
752 xQueueHandle targetQueue = UAVObjIsPriority(obj) ? channel->priorityQueue :
753 channel->queue;
754 #else /* PIOS_TELEM_PRIORITY_QUEUE */
755 xQueueHandle targetQueue = channel->queue;
756 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
758 ret = EventPeriodicQueueUpdate(&ev, targetQueue, updatePeriodMs);
759 if (ret == -1) {
760 ret = EventPeriodicQueueCreate(&ev, targetQueue, updatePeriodMs);
762 return ret;
766 * Set logging update period of object (it must be already setup for periodic updates)
767 * \param[in] telemetry channel context
768 * \param[in] obj The object to update
769 * \param[in] updatePeriodMs The update period in ms, if zero then periodic updates are disabled
770 * \return 0 Success
771 * \return -1 Failure
773 static int32_t setLoggingPeriod(
774 channelContext *channel,
775 UAVObjHandle obj,
776 int32_t updatePeriodMs)
778 UAVObjEvent ev;
779 int32_t ret;
781 // Add or update object for periodic updates
782 ev.obj = obj;
783 ev.instId = UAVOBJ_ALL_INSTANCES;
784 ev.event = EV_LOGGING_PERIODIC;
785 ev.lowPriority = true;
787 #ifdef PIOS_TELEM_PRIORITY_QUEUE
788 xQueueHandle targetQueue = UAVObjIsPriority(obj) ? channel->priorityQueue :
789 channel->queue;
790 #else /* PIOS_TELEM_PRIORITY_QUEUE */
791 xQueueHandle targetQueue = channel->queue;
792 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
794 ret = EventPeriodicQueueUpdate(&ev, targetQueue, updatePeriodMs);
795 if (ret == -1) {
796 ret = EventPeriodicQueueCreate(&ev, targetQueue, updatePeriodMs);
798 return ret;
802 * Called each time the GCS telemetry stats object is updated.
803 * Trigger a flight telemetry stats update if a connection is not
804 * yet established.
806 static void gcsTelemetryStatsUpdated()
808 FlightTelemetryStatsData flightStats;
809 GCSTelemetryStatsData gcsStats;
811 FlightTelemetryStatsGet(&flightStats);
812 GCSTelemetryStatsGet(&gcsStats);
813 if (flightStats.Status != FLIGHTTELEMETRYSTATS_STATUS_CONNECTED || gcsStats.Status != GCSTELEMETRYSTATS_STATUS_CONNECTED) {
814 updateTelemetryStats();
819 * Update telemetry statistics and handle connection handshake
821 static void updateTelemetryStats()
823 UAVTalkStats utalkStats;
824 FlightTelemetryStatsData flightStats;
825 GCSTelemetryStatsData gcsStats;
826 uint8_t forceUpdate;
827 uint8_t connectionTimeout;
828 uint32_t timeNow;
830 // Get stats
831 UAVTalkGetStats(radioChannel.uavTalkCon, &utalkStats, true);
833 #ifdef HAS_RADIO
834 UAVTalkAddStats(localChannel.uavTalkCon, &utalkStats, true);
835 #endif
837 // Get object data
838 FlightTelemetryStatsGet(&flightStats);
839 GCSTelemetryStatsGet(&gcsStats);
841 // Update stats object
842 if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_CONNECTED) {
843 flightStats.TxDataRate = (float)utalkStats.txBytes / ((float)STATS_UPDATE_PERIOD_MS / 1000.0f);
844 flightStats.TxBytes += utalkStats.txBytes;
845 flightStats.TxFailures += txErrors;
846 flightStats.TxRetries += txRetries;
848 flightStats.RxDataRate = (float)utalkStats.rxBytes / ((float)STATS_UPDATE_PERIOD_MS / 1000.0f);
849 flightStats.RxBytes += utalkStats.rxBytes;
850 flightStats.RxFailures += utalkStats.rxErrors;
851 flightStats.RxSyncErrors += utalkStats.rxSyncErrors;
852 flightStats.RxCrcErrors += utalkStats.rxCrcErrors;
853 } else {
854 flightStats.TxDataRate = 0;
855 flightStats.TxBytes = 0;
856 flightStats.TxFailures = 0;
857 flightStats.TxRetries = 0;
859 flightStats.RxDataRate = 0;
860 flightStats.RxBytes = 0;
861 flightStats.RxFailures = 0;
862 flightStats.RxSyncErrors = 0;
863 flightStats.RxCrcErrors = 0;
865 txErrors = 0;
866 txRetries = 0;
868 // Check for connection timeout
869 timeNow = xTaskGetTickCount() * portTICK_RATE_MS;
870 if (utalkStats.rxObjects > 0) {
871 timeOfLastObjectUpdate = timeNow;
873 if ((timeNow - timeOfLastObjectUpdate) > CONNECTION_TIMEOUT_MS) {
874 connectionTimeout = 1;
875 } else {
876 connectionTimeout = 0;
879 // Update connection state
880 forceUpdate = 1;
881 if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_DISCONNECTED) {
882 // Wait for connection request
883 if (gcsStats.Status == GCSTELEMETRYSTATS_STATUS_HANDSHAKEREQ) {
884 flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_HANDSHAKEACK;
886 } else if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_HANDSHAKEACK) {
887 // Wait for connection
888 if (gcsStats.Status == GCSTELEMETRYSTATS_STATUS_CONNECTED) {
889 flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_CONNECTED;
890 } else if (gcsStats.Status == GCSTELEMETRYSTATS_STATUS_DISCONNECTED) {
891 flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_DISCONNECTED;
893 } else if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_CONNECTED) {
894 if (gcsStats.Status != GCSTELEMETRYSTATS_STATUS_CONNECTED || connectionTimeout) {
895 flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_DISCONNECTED;
896 } else {
897 forceUpdate = 0;
899 } else {
900 flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_DISCONNECTED;
903 // TODO: check whether is there any error condition worth raising an alarm
904 // Disconnection is actually a normal (non)working status so it is not raising alarms anymore.
905 if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_CONNECTED) {
906 AlarmsClear(SYSTEMALARMS_ALARM_TELEMETRY);
909 // Update object
910 FlightTelemetryStatsSet(&flightStats);
912 // Force telemetry update if not connected
913 if (forceUpdate) {
914 FlightTelemetryStatsUpdated();
919 * Update the telemetry settings, called on startup.
920 * FIXME: This should be in the TelemetrySettings object. But objects
921 * have too much overhead yet. Also the telemetry has no any specific
922 * settings, etc. Thus the HwSettings object which contains the
923 * telemetry port speed is used for now.
925 static void updateSettings(channelContext *channel)
927 uint32_t port = channel->getPort();
929 if (port) {
930 // Retrieve settings
931 HwSettingsTelemetrySpeedOptions speed;
932 HwSettingsTelemetrySpeedGet(&speed);
934 // Set port speed
935 switch (speed) {
936 case HWSETTINGS_TELEMETRYSPEED_2400:
937 PIOS_COM_ChangeBaud(port, 2400);
938 break;
939 case HWSETTINGS_TELEMETRYSPEED_4800:
940 PIOS_COM_ChangeBaud(port, 4800);
941 break;
942 case HWSETTINGS_TELEMETRYSPEED_9600:
943 PIOS_COM_ChangeBaud(port, 9600);
944 break;
945 case HWSETTINGS_TELEMETRYSPEED_19200:
946 PIOS_COM_ChangeBaud(port, 19200);
947 break;
948 case HWSETTINGS_TELEMETRYSPEED_38400:
949 PIOS_COM_ChangeBaud(port, 38400);
950 break;
951 case HWSETTINGS_TELEMETRYSPEED_57600:
952 PIOS_COM_ChangeBaud(port, 57600);
953 break;
954 case HWSETTINGS_TELEMETRYSPEED_115200:
955 PIOS_COM_ChangeBaud(port, 115200);
956 break;
962 * @}
963 * @}