From a60baf281f612d6d26ed328dba6f800cfdba027d Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Thu, 2 Apr 2015 19:09:38 +0100
Subject: [PATCH 01/16] OP-1803 Initial commit

---
 shared/uavobjectdefinition/receiverstatus.xml | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 shared/uavobjectdefinition/receiverstatus.xml

diff --git a/shared/uavobjectdefinition/receiverstatus.xml b/shared/uavobjectdefinition/receiverstatus.xml
new file mode 100644
index 000000000..5a0415be2
--- /dev/null
+++ b/shared/uavobjectdefinition/receiverstatus.xml
@@ -0,0 +1,11 @@
+<xml>
+    <object name="ReceiverStatus" singleinstance="true" settings="false" category="System" priority="true">
+	<description>Receiver status.</description>
+	<field name="Quality" units="%" type="uint8" elements="1" defaultvalue="0"/>
+
+	<access gcs="readonly" flight="readwrite"/>
+        <telemetrygcs acked="false" updatemode="manual" period="0"/>
+        <telemetryflight acked="false" updatemode="periodic" period="1000"/>
+        <logging updatemode="manual" period="0"/>
+    </object>
+</xml>

From a0f180d393f0b5adafea64771421afbe305c65e5 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Thu, 2 Apr 2015 19:09:58 +0100
Subject: [PATCH 02/16] OP-1803 Initial commit

---
 flight/modules/Receiver/receiver.c            | 21 ++++++++
 flight/pios/common/pios_rcvr.c                | 28 +++++++++++
 flight/pios/common/pios_sbus.c                | 50 ++++++++++++++++---
 flight/pios/inc/pios_rcvr.h                   |  2 +
 .../boards/coptercontrol/firmware/Makefile    |  1 +
 .../discoveryf4bare/firmware/UAVObjects.inc   |  1 +
 .../boards/revolution/firmware/UAVObjects.inc |  1 +
 .../boards/revoproto/firmware/UAVObjects.inc  |  1 +
 .../src/plugins/uavobjects/uavobjects.pro     |  2 +
 9 files changed, 99 insertions(+), 8 deletions(-)

diff --git a/flight/modules/Receiver/receiver.c b/flight/modules/Receiver/receiver.c
index 2f42b7eed..16b05c440 100644
--- a/flight/modules/Receiver/receiver.c
+++ b/flight/modules/Receiver/receiver.c
@@ -37,6 +37,7 @@
 #include <manualcontrolsettings.h>
 #include <manualcontrolcommand.h>
 #include <receiveractivity.h>
+#include <receiverstatus.h>
 #include <flightstatus.h>
 #include <flighttelemetrystats.h>
 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
@@ -99,6 +100,7 @@ struct rcvr_activity_fsm {
     ManualControlSettingsChannelGroupsOptions group;
     uint16_t prev[RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP];
     uint8_t sample_count;
+    uint8_t quality;
 };
 static struct rcvr_activity_fsm activity_fsm;
 
@@ -148,6 +150,18 @@ int32_t ReceiverInitialize()
 }
 MODULE_INITCALL(ReceiverInitialize, ReceiverStart);
 
+static void updateRcvrStatus(uint32_t rcvr_id)
+{
+    extern uint32_t pios_rcvr_group_map[];
+
+    uint8_t quality = PIOS_RCVR_GetQuality(pios_rcvr_group_map[rcvr_id]);
+#if 1
+    ReceiverStatusQualitySet(&quality);
+#else
+    quality = quality * 100 / 255;
+#endif
+}
+
 /**
  * Module task
  */
@@ -243,6 +257,13 @@ static void receiverTask(__attribute__((unused)) void *parameters)
             }
         }
 
+	/* Update the receiver status, specifically signal quality, for the
+	 * receiver used for flight mode control.
+	 *
+	 * SCEDEBUG Fixed to use S.Bus for now
+	 */
+	updateRcvrStatus(MANUALCONTROLSETTINGS_CHANNELGROUPS_SBUS);
+
         // Check settings, if error raise alarm
         if (settings.ChannelGroups.Roll >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
             || settings.ChannelGroups.Pitch >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
diff --git a/flight/pios/common/pios_rcvr.c b/flight/pios/common/pios_rcvr.c
index b947c5142..66720ff5d 100644
--- a/flight/pios/common/pios_rcvr.c
+++ b/flight/pios/common/pios_rcvr.c
@@ -113,6 +113,34 @@ int32_t PIOS_RCVR_Read(uint32_t rcvr_id, uint8_t channel)
     return rcvr_dev->driver->read(rcvr_dev->lower_id, channel);
 }
 
+/**
+ * @brief Reads input quality from the appropriate driver
+ * @param[in] rcvr_id driver to read from
+ * @returns Unitless input value
+ *  @retval PIOS_RCVR_NODRIVER driver was not initialized
+ */
+uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id)
+{
+    if (rcvr_id == 0) {
+        return PIOS_RCVR_NODRIVER;
+    }
+
+    struct pios_rcvr_dev *rcvr_dev = (struct pios_rcvr_dev *)rcvr_id;
+
+    if (!PIOS_RCVR_validate(rcvr_dev)) {
+        /* Undefined RCVR port for this board (see pios_board.c) */
+        PIOS_Assert(0);
+    }
+
+    if (!rcvr_dev->driver->get_quality)
+    {
+    	/* If no quality is available assume max */
+	return 255;
+    }
+
+    return rcvr_dev->driver->get_quality(rcvr_dev->lower_id);
+}
+
 /**
  * @brief Get a semaphore that signals when a new sample is available.
  * @param[in] rcvr_id driver to read from
diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index 69089e9cd..5531626c8 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -32,6 +32,8 @@
 
 #ifdef PIOS_INCLUDE_SBUS
 
+#include <uavobjectmanager.h>
+#include <sbusstatus.h>
 #include "pios_sbus_priv.h"
 
 /* Forward Declarations */
@@ -42,11 +44,12 @@ static uint16_t PIOS_SBus_RxInCallback(uint32_t context,
                                        uint16_t *headroom,
                                        bool *need_yield);
 static void PIOS_SBus_Supervisor(uint32_t sbus_id);
-
+static uint8_t PIOS_SBus_Quality_Get(uint32_t rcvr_id);
 
 /* Local Variables */
 const struct pios_rcvr_driver pios_sbus_rcvr_driver = {
     .read = PIOS_SBus_Get,
+    .get_quality = PIOS_SBus_Quality_Get
 };
 
 enum pios_sbus_dev_magic {
@@ -60,8 +63,14 @@ struct pios_sbus_state {
     uint8_t  failsafe_timer;
     uint8_t  frame_found;
     uint8_t  byte_count;
+    uint8_t  quality;
 };
 
+/* With an S.Bus frame rate of 7ms (130Hz) averaging over 26 samples
+ * gives about a 200ms response.
+ */
+#define SBUS_FL_WEIGHTED_AVE 26
+
 struct pios_sbus_dev {
     enum pios_sbus_dev_magic   magic;
     const struct pios_sbus_cfg *cfg;
@@ -120,6 +129,7 @@ static void PIOS_SBus_ResetState(struct pios_sbus_state *state)
     state->receive_timer  = 0;
     state->failsafe_timer = 0;
     state->frame_found    = 0;
+    state->quality        = 0;
     PIOS_SBus_ResetChannels(state);
 }
 
@@ -251,18 +261,29 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
         state->byte_count++;
     } else {
         if (b == SBUS_EOF_BYTE || (b & SBUS_R7008SB_EOF_COUNTER_MASK) == 0) {
+	    uint8_t quality_trend;
+
             /* full frame received */
             uint8_t flags = state->received_data[SBUS_FRAME_LENGTH - 3];
             if (flags & SBUS_FLAG_FL) {
                 /* frame lost, do not update */
-            } else if (flags & SBUS_FLAG_FS) {
-                /* failsafe flag active */
-                PIOS_SBus_ResetChannels(state);
+                /* Quality trend is towards 0% */
+	        quality_trend = 0;
             } else {
-                /* data looking good */
-                PIOS_SBus_UnrollChannels(state);
-                state->failsafe_timer = 0;
-            }
+                /* Quality trend is towards 100% */
+	        quality_trend = 100;
+		if (flags & SBUS_FLAG_FS) {
+		    /* failsafe flag active */
+		    PIOS_SBus_ResetChannels(state);
+		} else {
+		    /* data looking good */
+		    PIOS_SBus_UnrollChannels(state);
+		    state->failsafe_timer = 0;
+		}
+	    }
+	    /* Present quality as a weighted average of good frames */
+	    state->quality = ((state->quality * (SBUS_FL_WEIGHTED_AVE - 1)) +
+		       quality_trend) / SBUS_FL_WEIGHTED_AVE;
         } else {
             /* discard whole frame */
         }
@@ -341,6 +362,19 @@ static void PIOS_SBus_Supervisor(uint32_t sbus_id)
     }
 }
 
+static uint8_t PIOS_SBus_Quality_Get(uint32_t sbus_id)
+{
+    struct pios_sbus_dev *sbus_dev = (struct pios_sbus_dev *)sbus_id;
+
+    bool valid = PIOS_SBus_Validate(sbus_dev);
+
+    PIOS_Assert(valid);
+
+    struct pios_sbus_state *state = &(sbus_dev->state);
+
+    return state->quality;
+}
+
 #endif /* PIOS_INCLUDE_SBUS */
 
 /**
diff --git a/flight/pios/inc/pios_rcvr.h b/flight/pios/inc/pios_rcvr.h
index 74fac086f..c289635d0 100644
--- a/flight/pios/inc/pios_rcvr.h
+++ b/flight/pios/inc/pios_rcvr.h
@@ -35,10 +35,12 @@ struct pios_rcvr_driver {
     void    (*init)(uint32_t id);
     int32_t (*read)(uint32_t id, uint8_t channel);
     xSemaphoreHandle (*get_semaphore)(uint32_t id, uint8_t channel);
+    uint8_t (*get_quality)(uint32_t id);
 };
 
 /* Public Functions */
 extern int32_t PIOS_RCVR_Read(uint32_t rcvr_id, uint8_t channel);
+extern uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id);
 extern xSemaphoreHandle PIOS_RCVR_GetSemaphore(uint32_t rcvr_id, uint8_t channel);
 
 /*! Define error codes for PIOS_RCVR_Get */
diff --git a/flight/targets/boards/coptercontrol/firmware/Makefile b/flight/targets/boards/coptercontrol/firmware/Makefile
index d58b57e64..8df60a8bf 100644
--- a/flight/targets/boards/coptercontrol/firmware/Makefile
+++ b/flight/targets/boards/coptercontrol/firmware/Makefile
@@ -122,6 +122,7 @@ ifndef TESTAPP
     SRC += $(OPUAVSYNTHDIR)/gpssettings.c
     SRC += $(OPUAVSYNTHDIR)/hwsettings.c
     SRC += $(OPUAVSYNTHDIR)/receiveractivity.c
+    SRC += $(OPUAVSYNTHDIR)/receiverstatus.c
     SRC += $(OPUAVSYNTHDIR)/mixerstatus.c
     SRC += $(OPUAVSYNTHDIR)/ratedesired.c
     SRC += $(OPUAVSYNTHDIR)/txpidsettings.c
diff --git a/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc b/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc
index 9f4007db8..d87f24086 100644
--- a/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc
+++ b/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc
@@ -103,6 +103,7 @@ UAVOBJSRCFILENAMES += watchdogstatus
 UAVOBJSRCFILENAMES += flightstatus
 UAVOBJSRCFILENAMES += hwsettings
 UAVOBJSRCFILENAMES += receiveractivity
+UAVOBJSRCFILENAMES += receiverstatus
 UAVOBJSRCFILENAMES += cameradesired
 UAVOBJSRCFILENAMES += camerastabsettings
 UAVOBJSRCFILENAMES += altitudeholdsettings
diff --git a/flight/targets/boards/revolution/firmware/UAVObjects.inc b/flight/targets/boards/revolution/firmware/UAVObjects.inc
index 9f4007db8..d87f24086 100644
--- a/flight/targets/boards/revolution/firmware/UAVObjects.inc
+++ b/flight/targets/boards/revolution/firmware/UAVObjects.inc
@@ -103,6 +103,7 @@ UAVOBJSRCFILENAMES += watchdogstatus
 UAVOBJSRCFILENAMES += flightstatus
 UAVOBJSRCFILENAMES += hwsettings
 UAVOBJSRCFILENAMES += receiveractivity
+UAVOBJSRCFILENAMES += receiverstatus
 UAVOBJSRCFILENAMES += cameradesired
 UAVOBJSRCFILENAMES += camerastabsettings
 UAVOBJSRCFILENAMES += altitudeholdsettings
diff --git a/flight/targets/boards/revoproto/firmware/UAVObjects.inc b/flight/targets/boards/revoproto/firmware/UAVObjects.inc
index 987266d1f..0d216aba4 100644
--- a/flight/targets/boards/revoproto/firmware/UAVObjects.inc
+++ b/flight/targets/boards/revoproto/firmware/UAVObjects.inc
@@ -103,6 +103,7 @@ UAVOBJSRCFILENAMES += watchdogstatus
 UAVOBJSRCFILENAMES += flightstatus
 UAVOBJSRCFILENAMES += hwsettings
 UAVOBJSRCFILENAMES += receiveractivity
+UAVOBJSRCFILENAMES += receiverstatus
 UAVOBJSRCFILENAMES += cameradesired
 UAVOBJSRCFILENAMES += camerastabsettings
 UAVOBJSRCFILENAMES += altitudeholdsettings
diff --git a/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro b/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro
index 94225fc81..69e032ecc 100644
--- a/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro
+++ b/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro
@@ -112,6 +112,7 @@ HEADERS += \
     $$UAVOBJECT_SYNTHETICS/hwsettings.h \
     $$UAVOBJECT_SYNTHETICS/gcsreceiver.h \
     $$UAVOBJECT_SYNTHETICS/receiveractivity.h \
+    $$UAVOBJECT_SYNTHETICS/receiverstatus.h \
     $$UAVOBJECT_SYNTHETICS/attitudesettings.h \
     $$UAVOBJECT_SYNTHETICS/txpidsettings.h \
     $$UAVOBJECT_SYNTHETICS/cameradesired.h \
@@ -219,6 +220,7 @@ SOURCES += \
     $$UAVOBJECT_SYNTHETICS/hwsettings.cpp \
     $$UAVOBJECT_SYNTHETICS/gcsreceiver.cpp \
     $$UAVOBJECT_SYNTHETICS/receiveractivity.cpp \
+    $$UAVOBJECT_SYNTHETICS/receiverstatus.cpp \
     $$UAVOBJECT_SYNTHETICS/attitudesettings.cpp \
     $$UAVOBJECT_SYNTHETICS/txpidsettings.cpp \
     $$UAVOBJECT_SYNTHETICS/cameradesired.cpp \

From 2e5ecd2dce988ed31be7b980dd41b93e1ec9facd Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Thu, 2 Apr 2015 21:41:40 +0100
Subject: [PATCH 03/16] OP-1803 S.Bus signal quality monitoring

---
 flight/modules/Receiver/receiver.c   | 61 ++++++++++++++++++----------
 flight/modules/Telemetry/telemetry.c |  4 +-
 flight/pios/common/pios_rcvr.c       |  7 ++--
 flight/pios/common/pios_sbus.c       | 34 ++++++++--------
 4 files changed, 62 insertions(+), 44 deletions(-)

diff --git a/flight/modules/Receiver/receiver.c b/flight/modules/Receiver/receiver.c
index 16b05c440..bdcc376a8 100644
--- a/flight/modules/Receiver/receiver.c
+++ b/flight/modules/Receiver/receiver.c
@@ -99,13 +99,17 @@ static void applyLPF(float *value, ManualControlSettingsResponseTimeElem channel
 struct rcvr_activity_fsm {
     ManualControlSettingsChannelGroupsOptions group;
     uint16_t prev[RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP];
-    uint8_t sample_count;
-    uint8_t quality;
+    uint8_t  sample_count;
+    uint8_t  quality;
 };
 static struct rcvr_activity_fsm activity_fsm;
 
 static void resetRcvrActivity(struct rcvr_activity_fsm *fsm);
 static bool updateRcvrActivity(struct rcvr_activity_fsm *fsm);
+static void resetRcvrStatus(struct rcvr_activity_fsm *fsm);
+static bool updateRcvrStatus(
+    struct rcvr_activity_fsm *fsm,
+    ManualControlSettingsChannelGroupsOptions group);
 
 #define assumptions \
     ( \
@@ -140,6 +144,7 @@ int32_t ReceiverInitialize()
     AccessoryDesiredInitialize();
     ManualControlCommandInitialize();
     ReceiverActivityInitialize();
+    ReceiverStatusInitialize();
     ManualControlSettingsInitialize();
 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
     StabilizationSettingsInitialize();
@@ -150,18 +155,6 @@ int32_t ReceiverInitialize()
 }
 MODULE_INITCALL(ReceiverInitialize, ReceiverStart);
 
-static void updateRcvrStatus(uint32_t rcvr_id)
-{
-    extern uint32_t pios_rcvr_group_map[];
-
-    uint8_t quality = PIOS_RCVR_GetQuality(pios_rcvr_group_map[rcvr_id]);
-#if 1
-    ReceiverStatusQualitySet(&quality);
-#else
-    quality = quality * 100 / 255;
-#endif
-}
-
 /**
  * Module task
  */
@@ -187,6 +180,7 @@ static void receiverTask(__attribute__((unused)) void *parameters)
     /* Initialize the RcvrActivty FSM */
     portTickType lastActivityTime = xTaskGetTickCount();
     resetRcvrActivity(&activity_fsm);
+    resetRcvrStatus(&activity_fsm);
 
     // Main task loop
     lastSysTime = xTaskGetTickCount();
@@ -211,9 +205,13 @@ static void receiverTask(__attribute__((unused)) void *parameters)
                 /* Reset the aging timer because activity was detected */
                 lastActivityTime = lastSysTime;
             }
+            /* Read signal quality from the group used for the throttle */
+            (void)updateRcvrStatus(&activity_fsm,
+                                   settings.ChannelGroups.Throttle);
         }
         if (timeDifferenceMs(lastActivityTime, lastSysTime) > 5000) {
             resetRcvrActivity(&activity_fsm);
+            resetRcvrStatus(&activity_fsm);
             lastActivityTime = lastSysTime;
         }
 
@@ -257,13 +255,6 @@ static void receiverTask(__attribute__((unused)) void *parameters)
             }
         }
 
-	/* Update the receiver status, specifically signal quality, for the
-	 * receiver used for flight mode control.
-	 *
-	 * SCEDEBUG Fixed to use S.Bus for now
-	 */
-	updateRcvrStatus(MANUALCONTROLSETTINGS_CHANNELGROUPS_SBUS);
-
         // Check settings, if error raise alarm
         if (settings.ChannelGroups.Roll >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
             || settings.ChannelGroups.Pitch >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
@@ -529,6 +520,12 @@ static void resetRcvrActivity(struct rcvr_activity_fsm *fsm)
     fsm->sample_count = 0;
 }
 
+static void resetRcvrStatus(struct rcvr_activity_fsm *fsm)
+{
+    /* Reset the state */
+    fsm->quality = 0;
+}
+
 static void updateRcvrActivitySample(uint32_t rcvr_id, uint16_t samples[], uint8_t max_channels)
 {
     for (uint8_t channel = 1; channel <= max_channels; channel++) {
@@ -599,6 +596,7 @@ static bool updateRcvrActivity(struct rcvr_activity_fsm *fsm)
     if (fsm->group >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
         /* We're out of range, reset things */
         resetRcvrActivity(fsm);
+        resetRcvrStatus(fsm);
     }
 
     extern uint32_t pios_rcvr_group_map[];
@@ -644,6 +642,27 @@ group_completed:
     return activity_updated;
 }
 
+/* Read signal quality from the specified group */
+static bool updateRcvrStatus(
+    struct rcvr_activity_fsm *fsm,
+    ManualControlSettingsChannelGroupsOptions group)
+{
+    extern uint32_t pios_rcvr_group_map[];
+    bool activity_updated = false;
+    uint8_t quality;
+
+    quality = PIOS_RCVR_GetQuality(pios_rcvr_group_map[group]);
+
+    /* Compare with previous sample */
+    if (quality != fsm->quality) {
+        fsm->quality     = quality;
+        ReceiverStatusQualitySet(&quality);
+        activity_updated = true;
+    }
+
+    return activity_updated;
+}
+
 /**
  * Convert channel from servo pulse duration (microseconds) to scaled -1/+1 range.
  */
diff --git a/flight/modules/Telemetry/telemetry.c b/flight/modules/Telemetry/telemetry.c
index ee7e53cda..0b653f652 100644
--- a/flight/modules/Telemetry/telemetry.c
+++ b/flight/modules/Telemetry/telemetry.c
@@ -221,9 +221,9 @@ int32_t TelemetryInitialize(void)
     timeOfLastObjectUpdate     = 0;
 
     // Create object queues
-    localChannel.queue     = xQueueCreate(MAX_QUEUE_SIZE,
+    localChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
                                               sizeof(UAVObjEvent));
-    radioChannel.queue     = xQueueCreate(MAX_QUEUE_SIZE,
+    radioChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
                                               sizeof(UAVObjEvent));
 #if defined(PIOS_TELEM_PRIORITY_QUEUE)
     localChannel.priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
diff --git a/flight/pios/common/pios_rcvr.c b/flight/pios/common/pios_rcvr.c
index 66720ff5d..590e9f213 100644
--- a/flight/pios/common/pios_rcvr.c
+++ b/flight/pios/common/pios_rcvr.c
@@ -132,10 +132,9 @@ uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id)
         PIOS_Assert(0);
     }
 
-    if (!rcvr_dev->driver->get_quality)
-    {
-    	/* If no quality is available assume max */
-	return 255;
+    if (!rcvr_dev->driver->get_quality) {
+        /* If no quality is available assume max */
+        return 255;
     }
 
     return rcvr_dev->driver->get_quality(rcvr_dev->lower_id);
diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index 5531626c8..0d6a5e02e 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -48,7 +48,7 @@ static uint8_t PIOS_SBus_Quality_Get(uint32_t rcvr_id);
 
 /* Local Variables */
 const struct pios_rcvr_driver pios_sbus_rcvr_driver = {
-    .read = PIOS_SBus_Get,
+    .read        = PIOS_SBus_Get,
     .get_quality = PIOS_SBus_Quality_Get
 };
 
@@ -129,7 +129,7 @@ static void PIOS_SBus_ResetState(struct pios_sbus_state *state)
     state->receive_timer  = 0;
     state->failsafe_timer = 0;
     state->frame_found    = 0;
-    state->quality        = 0;
+    state->quality = 0;
     PIOS_SBus_ResetChannels(state);
 }
 
@@ -261,29 +261,29 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
         state->byte_count++;
     } else {
         if (b == SBUS_EOF_BYTE || (b & SBUS_R7008SB_EOF_COUNTER_MASK) == 0) {
-	    uint8_t quality_trend;
+            uint8_t quality_trend;
 
             /* full frame received */
             uint8_t flags = state->received_data[SBUS_FRAME_LENGTH - 3];
             if (flags & SBUS_FLAG_FL) {
                 /* frame lost, do not update */
                 /* Quality trend is towards 0% */
-	        quality_trend = 0;
+                quality_trend = 0;
             } else {
                 /* Quality trend is towards 100% */
-	        quality_trend = 100;
-		if (flags & SBUS_FLAG_FS) {
-		    /* failsafe flag active */
-		    PIOS_SBus_ResetChannels(state);
-		} else {
-		    /* data looking good */
-		    PIOS_SBus_UnrollChannels(state);
-		    state->failsafe_timer = 0;
-		}
-	    }
-	    /* Present quality as a weighted average of good frames */
-	    state->quality = ((state->quality * (SBUS_FL_WEIGHTED_AVE - 1)) +
-		       quality_trend) / SBUS_FL_WEIGHTED_AVE;
+                quality_trend = 100;
+                if (flags & SBUS_FLAG_FS) {
+                    /* failsafe flag active */
+                    PIOS_SBus_ResetChannels(state);
+                } else {
+                    /* data looking good */
+                    PIOS_SBus_UnrollChannels(state);
+                    state->failsafe_timer = 0;
+                }
+            }
+            /* Present quality as a weighted average of good frames */
+            state->quality = ((state->quality * (SBUS_FL_WEIGHTED_AVE - 1)) +
+                              quality_trend) / SBUS_FL_WEIGHTED_AVE;
         } else {
             /* discard whole frame */
         }

From 8276aa3a66bf06a1b6b3a24c982b6146cc49d68f Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 3 Apr 2015 09:47:07 +0100
Subject: [PATCH 04/16] OP-1803 Monitor signal quality when armed too

---
 flight/modules/Receiver/receiver.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/flight/modules/Receiver/receiver.c b/flight/modules/Receiver/receiver.c
index bdcc376a8..da26c23be 100644
--- a/flight/modules/Receiver/receiver.c
+++ b/flight/modules/Receiver/receiver.c
@@ -255,6 +255,10 @@ static void receiverTask(__attribute__((unused)) void *parameters)
             }
         }
 
+        /* Read signal quality from the group used for the throttle */
+        (void)updateRcvrStatus(&activity_fsm,
+                               settings.ChannelGroups.Throttle);
+
         // Check settings, if error raise alarm
         if (settings.ChannelGroups.Roll >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
             || settings.ChannelGroups.Pitch >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE

From f9f0e1657c5b4a0124c7ddacad036da19e5686a4 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 3 Apr 2015 15:29:24 +0100
Subject: [PATCH 05/16] OP-1803 Add good frame count option for debug

---
 flight/pios/common/pios_sbus.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index 0d6a5e02e..778d91743 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -32,6 +32,9 @@
 
 #ifdef PIOS_INCLUDE_SBUS
 
+// Define to report number of frames since last dropped instead of weighted ave
+#undef SBUS_GOOD_FRAME_COUNT
+
 #include <uavobjectmanager.h>
 #include <sbusstatus.h>
 #include "pios_sbus_priv.h"
@@ -64,6 +67,9 @@ struct pios_sbus_state {
     uint8_t  frame_found;
     uint8_t  byte_count;
     uint8_t  quality;
+#ifdef SBUS_GOOD_FRAME_COUNT
+    uint8_t  frame_count;
+#endif /* SBUS_GOOD_FRAME_COUNT */
 };
 
 /* With an S.Bus frame rate of 7ms (130Hz) averaging over 26 samples
@@ -130,6 +136,9 @@ static void PIOS_SBus_ResetState(struct pios_sbus_state *state)
     state->failsafe_timer = 0;
     state->frame_found    = 0;
     state->quality = 0;
+#ifdef SBUS_GOOD_FRAME_COUNT
+    state->frame_count = 0;
+#endif /* SBUS_GOOD_FRAME_COUNT */
     PIOS_SBus_ResetChannels(state);
 }
 
@@ -261,17 +270,29 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
         state->byte_count++;
     } else {
         if (b == SBUS_EOF_BYTE || (b & SBUS_R7008SB_EOF_COUNTER_MASK) == 0) {
-            uint8_t quality_trend;
+#ifndef SBUS_GOOD_FRAME_COUNT
+	    /* Quality trend is towards 0% by default*/
+            uint8_t quality_trend = 0;
+#endif /* SBUS_GOOD_FRAME_COUNT */
 
             /* full frame received */
             uint8_t flags = state->received_data[SBUS_FRAME_LENGTH - 3];
             if (flags & SBUS_FLAG_FL) {
                 /* frame lost, do not update */
-                /* Quality trend is towards 0% */
-                quality_trend = 0;
+#ifdef SBUS_GOOD_FRAME_COUNT
+		state->quality = state->frame_count;
+		state->frame_count = 0;
+#endif /* SBUS_GOOD_FRAME_COUNT */
             } else {
+#ifdef SBUS_GOOD_FRAME_COUNT
+		if (++state->frame_count == 255)
+		{
+		    state->quality = state->frame_count--;
+		}
+#else /* SBUS_GOOD_FRAME_COUNT */
                 /* Quality trend is towards 100% */
                 quality_trend = 100;
+#endif /* SBUS_GOOD_FRAME_COUNT */
                 if (flags & SBUS_FLAG_FS) {
                     /* failsafe flag active */
                     PIOS_SBus_ResetChannels(state);
@@ -281,9 +302,11 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
                     state->failsafe_timer = 0;
                 }
             }
+#ifndef SBUS_GOOD_FRAME_COUNT
             /* Present quality as a weighted average of good frames */
             state->quality = ((state->quality * (SBUS_FL_WEIGHTED_AVE - 1)) +
                               quality_trend) / SBUS_FL_WEIGHTED_AVE;
+#endif /* SBUS_GOOD_FRAME_COUNT */
         } else {
             /* discard whole frame */
         }

From 32b3f0d55de18ad7aa3932cecce2ed10c7f37edd Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 3 Apr 2015 15:45:00 +0100
Subject: [PATCH 06/16] OP-1803 Remove old header reference

---
 flight/pios/common/pios_sbus.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index 778d91743..9cd2313e8 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -36,7 +36,6 @@
 #undef SBUS_GOOD_FRAME_COUNT
 
 #include <uavobjectmanager.h>
-#include <sbusstatus.h>
 #include "pios_sbus_priv.h"
 
 /* Forward Declarations */

From b5a3ad3708cecd693059ea5425332da8f3a122a9 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 3 Apr 2015 18:01:04 +0100
Subject: [PATCH 07/16] OP-1803 Use float to avoid rounding error on signal
 quality

---
 flight/pios/common/pios_sbus.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index 9cd2313e8..b7d04c7d7 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -65,7 +65,7 @@ struct pios_sbus_state {
     uint8_t  failsafe_timer;
     uint8_t  frame_found;
     uint8_t  byte_count;
-    uint8_t  quality;
+    float    quality;
 #ifdef SBUS_GOOD_FRAME_COUNT
     uint8_t  frame_count;
 #endif /* SBUS_GOOD_FRAME_COUNT */
@@ -134,7 +134,7 @@ static void PIOS_SBus_ResetState(struct pios_sbus_state *state)
     state->receive_timer  = 0;
     state->failsafe_timer = 0;
     state->frame_found    = 0;
-    state->quality = 0;
+    state->quality = 0.0f;
 #ifdef SBUS_GOOD_FRAME_COUNT
     state->frame_count = 0;
 #endif /* SBUS_GOOD_FRAME_COUNT */
@@ -394,7 +394,7 @@ static uint8_t PIOS_SBus_Quality_Get(uint32_t sbus_id)
 
     struct pios_sbus_state *state = &(sbus_dev->state);
 
-    return state->quality;
+    return (uint8_t)(state->quality + 0.5f);
 }
 
 #endif /* PIOS_INCLUDE_SBUS */

From b7122008c7fa9b89e7cab3ace19c2d90e97b85e6 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 3 Apr 2015 18:30:36 +0100
Subject: [PATCH 08/16] OP-1803 Uncrustify

---
 flight/pios/common/pios_rcvr.c |  2 +-
 flight/pios/common/pios_sbus.c | 15 +++++++--------
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/flight/pios/common/pios_rcvr.c b/flight/pios/common/pios_rcvr.c
index 590e9f213..89a827885 100644
--- a/flight/pios/common/pios_rcvr.c
+++ b/flight/pios/common/pios_rcvr.c
@@ -116,7 +116,7 @@ int32_t PIOS_RCVR_Read(uint32_t rcvr_id, uint8_t channel)
 /**
  * @brief Reads input quality from the appropriate driver
  * @param[in] rcvr_id driver to read from
- * @returns Unitless input value
+ * @returns received signal quality expressed as a %
  *  @retval PIOS_RCVR_NODRIVER driver was not initialized
  */
 uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id)
diff --git a/flight/pios/common/pios_sbus.c b/flight/pios/common/pios_sbus.c
index b7d04c7d7..e354a5c5f 100644
--- a/flight/pios/common/pios_sbus.c
+++ b/flight/pios/common/pios_sbus.c
@@ -136,7 +136,7 @@ static void PIOS_SBus_ResetState(struct pios_sbus_state *state)
     state->frame_found    = 0;
     state->quality = 0.0f;
 #ifdef SBUS_GOOD_FRAME_COUNT
-    state->frame_count = 0;
+    state->frame_count    = 0;
 #endif /* SBUS_GOOD_FRAME_COUNT */
     PIOS_SBus_ResetChannels(state);
 }
@@ -270,7 +270,7 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
     } else {
         if (b == SBUS_EOF_BYTE || (b & SBUS_R7008SB_EOF_COUNTER_MASK) == 0) {
 #ifndef SBUS_GOOD_FRAME_COUNT
-	    /* Quality trend is towards 0% by default*/
+            /* Quality trend is towards 0% by default*/
             uint8_t quality_trend = 0;
 #endif /* SBUS_GOOD_FRAME_COUNT */
 
@@ -279,15 +279,14 @@ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b)
             if (flags & SBUS_FLAG_FL) {
                 /* frame lost, do not update */
 #ifdef SBUS_GOOD_FRAME_COUNT
-		state->quality = state->frame_count;
-		state->frame_count = 0;
+                state->quality     = state->frame_count;
+                state->frame_count = 0;
 #endif /* SBUS_GOOD_FRAME_COUNT */
             } else {
 #ifdef SBUS_GOOD_FRAME_COUNT
-		if (++state->frame_count == 255)
-		{
-		    state->quality = state->frame_count--;
-		}
+                if (++state->frame_count == 255) {
+                    state->quality = state->frame_count--;
+                }
 #else /* SBUS_GOOD_FRAME_COUNT */
                 /* Quality trend is towards 100% */
                 quality_trend = 100;

From b93d9e178e9c9ef038ef44c03a463ba786801eeb Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Sat, 4 Apr 2015 14:50:08 +0100
Subject: [PATCH 09/16] OP-1803 Max signal quality is 100%

---
 flight/pios/common/pios_rcvr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flight/pios/common/pios_rcvr.c b/flight/pios/common/pios_rcvr.c
index 89a827885..4ad7046ce 100644
--- a/flight/pios/common/pios_rcvr.c
+++ b/flight/pios/common/pios_rcvr.c
@@ -134,7 +134,7 @@ uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id)
 
     if (!rcvr_dev->driver->get_quality) {
         /* If no quality is available assume max */
-        return 255;
+        return 100;
     }
 
     return rcvr_dev->driver->get_quality(rcvr_dev->lower_id);

From 73aeb5d65336cc81051ddbfcd8debe3b17fc98dc Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Sat, 4 Apr 2015 22:02:54 +0100
Subject: [PATCH 10/16] OP-1803 Report link quality for OPLink

---
 flight/pios/common/pios_oplinkrcvr.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/flight/pios/common/pios_oplinkrcvr.c b/flight/pios/common/pios_oplinkrcvr.c
index c4579a0b5..5c967c105 100644
--- a/flight/pios/common/pios_oplinkrcvr.c
+++ b/flight/pios/common/pios_oplinkrcvr.c
@@ -34,6 +34,7 @@
 
 #include <uavobjectmanager.h>
 #include <oplinkreceiver.h>
+#include <oplinkstatus.h>
 #include <pios_oplinkrcvr_priv.h>
 
 static OPLinkReceiverData oplinkreceiverdata;
@@ -41,9 +42,11 @@ static OPLinkReceiverData oplinkreceiverdata;
 /* Provide a RCVR driver */
 static int32_t PIOS_OPLinkRCVR_Get(uint32_t rcvr_id, uint8_t channel);
 static void PIOS_oplinkrcvr_Supervisor(uint32_t ppm_id);
+static uint8_t PIOS_OPLinkRCVR_Quality_Get(uint32_t oplinkrcvr_id);
 
 const struct pios_rcvr_driver pios_oplinkrcvr_rcvr_driver = {
-    .read = PIOS_OPLinkRCVR_Get,
+    .read        = PIOS_OPLinkRCVR_Get,
+    .get_quality = PIOS_OPLinkRCVR_Quality_Get
 };
 
 /* Local Variables */
@@ -186,6 +189,16 @@ static void PIOS_oplinkrcvr_Supervisor(uint32_t oplinkrcvr_id)
     oplinkrcvr_dev->Fresh = false;
 }
 
+static uint8_t PIOS_OPLinkRCVR_Quality_Get(__attribute__((unused)) uint32_t oplinkrcvr_id)
+{
+    uint8_t oplink_quality;
+
+    OPLinkStatusLinkQualityGet(&oplink_quality);
+
+    /* link_status is in the range 0-128, so scale to a % */
+    return oplink_quality * 100 / 128;
+}
+
 #endif /* PIOS_INCLUDE_OPLINKRCVR */
 
 /**

From 7c7b19ff2d0591ba416c61e301846d9bb87a95fb Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Sat, 11 Apr 2015 10:10:04 +0100
Subject: [PATCH 11/16] OP-1289 Merge OP-1803 Only create local telemetry
 queues/tasks if needed

---
 flight/modules/Telemetry/telemetry.c | 134 +++++++++++++++------------
 1 file changed, 76 insertions(+), 58 deletions(-)

diff --git a/flight/modules/Telemetry/telemetry.c b/flight/modules/Telemetry/telemetry.c
index 58532ce78..9d3d1d5e6 100644
--- a/flight/modules/Telemetry/telemetry.c
+++ b/flight/modules/Telemetry/telemetry.c
@@ -157,36 +157,45 @@ static void updateSettings(channelContext *channel);
  */
 int32_t TelemetryStart(void)
 {
-    UAVObjIterate(&registerLocalObject);
+    // Only start the local telemetry tasks if needed
+    if (localPort()) {
+        UAVObjIterate(&registerLocalObject);
+
+        // Listen to objects of interest
+#ifdef PIOS_TELEM_PRIORITY_QUEUE
+        GCSTelemetryStatsConnectQueue(localChannel.priorityQueue);
+#else /* PIOS_TELEM_PRIORITY_QUEUE */
+        GCSTelemetryStatsConnectQueue(localChannel.queue);
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+        // Start telemetry tasks
+        xTaskCreate(telemetryTxTask,
+                    "TelTx",
+                    STACK_SIZE_TX_BYTES / 4,
+                    &localChannel,
+                    TASK_PRIORITY_TX,
+                    &localChannel.txTaskHandle);
+        PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYTX,
+                                       localChannel.txTaskHandle);
+        xTaskCreate(telemetryRxTask,
+                    "TelRx",
+                    STACK_SIZE_RX_BYTES / 4,
+                    &localChannel,
+                    TASK_PRIORITY_RX,
+                    &localChannel.rxTaskHandle);
+        PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYRX,
+                                       localChannel.rxTaskHandle);
+    }
+
+    // Start the telemetry tasks associated with Radio/USB
     UAVObjIterate(&registerRadioObject);
 
     // Listen to objects of interest
 #ifdef PIOS_TELEM_PRIORITY_QUEUE
-    GCSTelemetryStatsConnectQueue(localChannel.priorityQueue);
     GCSTelemetryStatsConnectQueue(radioChannel.priorityQueue);
 #else /* PIOS_TELEM_PRIORITY_QUEUE */
-    GCSTelemetryStatsConnectQueue(localChannel.queue);
     GCSTelemetryStatsConnectQueue(radioChannel.queue);
 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
 
-    // Start telemetry tasks
-    xTaskCreate(telemetryTxTask,
-                "TelTx",
-                STACK_SIZE_TX_BYTES / 4,
-                &localChannel,
-                TASK_PRIORITY_TX,
-                &localChannel.txTaskHandle);
-    PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYTX,
-                                   localChannel.txTaskHandle);
-    xTaskCreate(telemetryRxTask,
-                "TelRx",
-                STACK_SIZE_RX_BYTES / 4,
-                &localChannel,
-                TASK_PRIORITY_RX,
-                &localChannel.rxTaskHandle);
-    PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYRX,
-                                   localChannel.rxTaskHandle);
-
     xTaskCreate(telemetryTxTask,
                 "RadioTx",
                 STACK_SIZE_RADIO_TX_BYTES / 4,
@@ -207,6 +216,35 @@ int32_t TelemetryStart(void)
     return 0;
 }
 
+/* Intialise a telemetry channel */
+void TelemetryInitializeChannel(channelContext *channel)
+{
+    // Create object queues
+    channel->queue = xQueueCreate(MAX_QUEUE_SIZE,
+                                  sizeof(UAVObjEvent));
+#if defined(PIOS_TELEM_PRIORITY_QUEUE)
+    channel->priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
+                                          sizeof(UAVObjEvent));
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+
+    // Initialise UAVTalk
+    channel->uavTalkCon = UAVTalkInitialize(&transmitLocalData);
+
+    // Create periodic event that will be used to update the telemetry stats
+    UAVObjEvent ev;
+    memset(&ev, 0, sizeof(UAVObjEvent));
+
+#ifdef PIOS_TELEM_PRIORITY_QUEUE
+    EventPeriodicQueueCreate(&ev,
+                             channel->priorityQueue,
+                             STATS_UPDATE_PERIOD_MS);
+#else /* PIOS_TELEM_PRIORITY_QUEUE */
+    EventPeriodicQueueCreate(&ev,
+                             channel->queue,
+                             STATS_UPDATE_PERIOD_MS);
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+}
+
 /**
  * Initialise the telemetry module
  * \return -1 if initialisation failed
@@ -214,58 +252,38 @@ int32_t TelemetryStart(void)
  */
 int32_t TelemetryInitialize(void)
 {
+    HwSettingsInitialize();
+
     FlightTelemetryStatsInitialize();
     GCSTelemetryStatsInitialize();
 
     // Initialize vars
-    timeOfLastObjectUpdate     = 0;
+    timeOfLastObjectUpdate = 0;
 
-    // Create object queues
-    localChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-    radioChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-#if defined(PIOS_TELEM_PRIORITY_QUEUE)
-    localChannel.priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-    radioChannel.priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+    // Reset link stats
+    txErrors  = 0;
+    txRetries = 0;
 
     // Set channel port handlers
     localChannel.getPort = localPort;
     radioChannel.getPort = radioPort;
 
-    HwSettingsInitialize();
+    // Set the local telemetry baud rate
     updateSettings(&localChannel);
 
+    // Only initialise local channel if telemetry port enabled
+    if (localPort()) {
+        // Initialise channel
+        TelemetryInitializeChannel(&localChannel);
+        // Initialise UAVTalk
+        localChannel.uavTalkCon = UAVTalkInitialize(&transmitLocalData);
+    }
+
+    // Initialise channel
+    TelemetryInitializeChannel(&radioChannel);
     // Initialise UAVTalk
-    localChannel.uavTalkCon = UAVTalkInitialize(&transmitLocalData);
     radioChannel.uavTalkCon = UAVTalkInitialize(&transmitRadioData);
 
-    // Create periodic event that will be used to update the telemetry stats
-    // FIXME STATS_UPDATE_PERIOD_MS is 4000ms while FlighTelemetryStats update period is 5000ms...
-    txErrors  = 0;
-    txRetries = 0;
-    UAVObjEvent ev;
-    memset(&ev, 0, sizeof(UAVObjEvent));
-
-#ifdef PIOS_TELEM_PRIORITY_QUEUE
-    EventPeriodicQueueCreate(&ev,
-                             localChannel.priorityQueue,
-                             STATS_UPDATE_PERIOD_MS);
-    EventPeriodicQueueCreate(&ev,
-                             radioChannel.priorityQueue,
-                             STATS_UPDATE_PERIOD_MS);
-#else /* PIOS_TELEM_PRIORITY_QUEUE */
-    EventPeriodicQueueCreate(&ev,
-                             localChannel.queue,
-                             STATS_UPDATE_PERIOD_MS);
-    EventPeriodicQueueCreate(&ev,
-                             radioChannel.queue,
-                             STATS_UPDATE_PERIOD_MS);
-#endif /* PIOS_TELEM_PRIORITY_QUEUE */
-
     return 0;
 }
 

From d2a69b2d449f0e0935e837d2ea31018c92a83ca8 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Sat, 11 Apr 2015 10:10:04 +0100
Subject: [PATCH 12/16] OP-1803 Merge OP-1289 Only create local telemetry
 queues/tasks if needed

---
 flight/modules/Telemetry/telemetry.c | 134 +++++++++++++++------------
 1 file changed, 76 insertions(+), 58 deletions(-)

diff --git a/flight/modules/Telemetry/telemetry.c b/flight/modules/Telemetry/telemetry.c
index 58532ce78..9d3d1d5e6 100644
--- a/flight/modules/Telemetry/telemetry.c
+++ b/flight/modules/Telemetry/telemetry.c
@@ -157,36 +157,45 @@ static void updateSettings(channelContext *channel);
  */
 int32_t TelemetryStart(void)
 {
-    UAVObjIterate(&registerLocalObject);
+    // Only start the local telemetry tasks if needed
+    if (localPort()) {
+        UAVObjIterate(&registerLocalObject);
+
+        // Listen to objects of interest
+#ifdef PIOS_TELEM_PRIORITY_QUEUE
+        GCSTelemetryStatsConnectQueue(localChannel.priorityQueue);
+#else /* PIOS_TELEM_PRIORITY_QUEUE */
+        GCSTelemetryStatsConnectQueue(localChannel.queue);
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+        // Start telemetry tasks
+        xTaskCreate(telemetryTxTask,
+                    "TelTx",
+                    STACK_SIZE_TX_BYTES / 4,
+                    &localChannel,
+                    TASK_PRIORITY_TX,
+                    &localChannel.txTaskHandle);
+        PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYTX,
+                                       localChannel.txTaskHandle);
+        xTaskCreate(telemetryRxTask,
+                    "TelRx",
+                    STACK_SIZE_RX_BYTES / 4,
+                    &localChannel,
+                    TASK_PRIORITY_RX,
+                    &localChannel.rxTaskHandle);
+        PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYRX,
+                                       localChannel.rxTaskHandle);
+    }
+
+    // Start the telemetry tasks associated with Radio/USB
     UAVObjIterate(&registerRadioObject);
 
     // Listen to objects of interest
 #ifdef PIOS_TELEM_PRIORITY_QUEUE
-    GCSTelemetryStatsConnectQueue(localChannel.priorityQueue);
     GCSTelemetryStatsConnectQueue(radioChannel.priorityQueue);
 #else /* PIOS_TELEM_PRIORITY_QUEUE */
-    GCSTelemetryStatsConnectQueue(localChannel.queue);
     GCSTelemetryStatsConnectQueue(radioChannel.queue);
 #endif /* PIOS_TELEM_PRIORITY_QUEUE */
 
-    // Start telemetry tasks
-    xTaskCreate(telemetryTxTask,
-                "TelTx",
-                STACK_SIZE_TX_BYTES / 4,
-                &localChannel,
-                TASK_PRIORITY_TX,
-                &localChannel.txTaskHandle);
-    PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYTX,
-                                   localChannel.txTaskHandle);
-    xTaskCreate(telemetryRxTask,
-                "TelRx",
-                STACK_SIZE_RX_BYTES / 4,
-                &localChannel,
-                TASK_PRIORITY_RX,
-                &localChannel.rxTaskHandle);
-    PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_TELEMETRYRX,
-                                   localChannel.rxTaskHandle);
-
     xTaskCreate(telemetryTxTask,
                 "RadioTx",
                 STACK_SIZE_RADIO_TX_BYTES / 4,
@@ -207,6 +216,35 @@ int32_t TelemetryStart(void)
     return 0;
 }
 
+/* Intialise a telemetry channel */
+void TelemetryInitializeChannel(channelContext *channel)
+{
+    // Create object queues
+    channel->queue = xQueueCreate(MAX_QUEUE_SIZE,
+                                  sizeof(UAVObjEvent));
+#if defined(PIOS_TELEM_PRIORITY_QUEUE)
+    channel->priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
+                                          sizeof(UAVObjEvent));
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+
+    // Initialise UAVTalk
+    channel->uavTalkCon = UAVTalkInitialize(&transmitLocalData);
+
+    // Create periodic event that will be used to update the telemetry stats
+    UAVObjEvent ev;
+    memset(&ev, 0, sizeof(UAVObjEvent));
+
+#ifdef PIOS_TELEM_PRIORITY_QUEUE
+    EventPeriodicQueueCreate(&ev,
+                             channel->priorityQueue,
+                             STATS_UPDATE_PERIOD_MS);
+#else /* PIOS_TELEM_PRIORITY_QUEUE */
+    EventPeriodicQueueCreate(&ev,
+                             channel->queue,
+                             STATS_UPDATE_PERIOD_MS);
+#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+}
+
 /**
  * Initialise the telemetry module
  * \return -1 if initialisation failed
@@ -214,58 +252,38 @@ int32_t TelemetryStart(void)
  */
 int32_t TelemetryInitialize(void)
 {
+    HwSettingsInitialize();
+
     FlightTelemetryStatsInitialize();
     GCSTelemetryStatsInitialize();
 
     // Initialize vars
-    timeOfLastObjectUpdate     = 0;
+    timeOfLastObjectUpdate = 0;
 
-    // Create object queues
-    localChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-    radioChannel.queue         = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-#if defined(PIOS_TELEM_PRIORITY_QUEUE)
-    localChannel.priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-    radioChannel.priorityQueue = xQueueCreate(MAX_QUEUE_SIZE,
-                                              sizeof(UAVObjEvent));
-#endif /* PIOS_TELEM_PRIORITY_QUEUE */
+    // Reset link stats
+    txErrors  = 0;
+    txRetries = 0;
 
     // Set channel port handlers
     localChannel.getPort = localPort;
     radioChannel.getPort = radioPort;
 
-    HwSettingsInitialize();
+    // Set the local telemetry baud rate
     updateSettings(&localChannel);
 
+    // Only initialise local channel if telemetry port enabled
+    if (localPort()) {
+        // Initialise channel
+        TelemetryInitializeChannel(&localChannel);
+        // Initialise UAVTalk
+        localChannel.uavTalkCon = UAVTalkInitialize(&transmitLocalData);
+    }
+
+    // Initialise channel
+    TelemetryInitializeChannel(&radioChannel);
     // Initialise UAVTalk
-    localChannel.uavTalkCon = UAVTalkInitialize(&transmitLocalData);
     radioChannel.uavTalkCon = UAVTalkInitialize(&transmitRadioData);
 
-    // Create periodic event that will be used to update the telemetry stats
-    // FIXME STATS_UPDATE_PERIOD_MS is 4000ms while FlighTelemetryStats update period is 5000ms...
-    txErrors  = 0;
-    txRetries = 0;
-    UAVObjEvent ev;
-    memset(&ev, 0, sizeof(UAVObjEvent));
-
-#ifdef PIOS_TELEM_PRIORITY_QUEUE
-    EventPeriodicQueueCreate(&ev,
-                             localChannel.priorityQueue,
-                             STATS_UPDATE_PERIOD_MS);
-    EventPeriodicQueueCreate(&ev,
-                             radioChannel.priorityQueue,
-                             STATS_UPDATE_PERIOD_MS);
-#else /* PIOS_TELEM_PRIORITY_QUEUE */
-    EventPeriodicQueueCreate(&ev,
-                             localChannel.queue,
-                             STATS_UPDATE_PERIOD_MS);
-    EventPeriodicQueueCreate(&ev,
-                             radioChannel.queue,
-                             STATS_UPDATE_PERIOD_MS);
-#endif /* PIOS_TELEM_PRIORITY_QUEUE */
-
     return 0;
 }
 

From b87a1c1fa6a73633b7eb6d1773e473bc6370f514 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Sun, 19 Apr 2015 18:41:04 +0100
Subject: [PATCH 13/16] OP-1803 Enable DSM link quality so testers have
 something to work with

---
 flight/pios/stm32f10x/pios_dsm.c | 56 +++++++++++++++++++++++---------
 flight/pios/stm32f4xx/pios_dsm.c | 56 +++++++++++++++++++++++---------
 2 files changed, 81 insertions(+), 31 deletions(-)

diff --git a/flight/pios/stm32f10x/pios_dsm.c b/flight/pios/stm32f10x/pios_dsm.c
index 0a3450a9f..a058ee9c2 100644
--- a/flight/pios/stm32f10x/pios_dsm.c
+++ b/flight/pios/stm32f10x/pios_dsm.c
@@ -34,9 +34,12 @@
 
 #include "pios_dsm_priv.h"
 
+// *** UNTESTED CODE ***
+#define DSM_LINK_QUALITY
 
 /* Forward Declarations */
 static int32_t PIOS_DSM_Get(uint32_t rcvr_id, uint8_t channel);
+static uint8_t PIOS_DSM_Quality_Get(uint32_t rcvr_id);
 static uint16_t PIOS_DSM_RxInCallback(uint32_t context,
                                       uint8_t *buf,
                                       uint16_t buf_len,
@@ -46,7 +49,8 @@ static void PIOS_DSM_Supervisor(uint32_t dsm_id);
 
 /* Local Variables */
 const struct pios_rcvr_driver pios_dsm_rcvr_driver = {
-    .read = PIOS_DSM_Get,
+    .read        = PIOS_DSM_Get,
+    .get_quality = PIOS_DSM_Quality_Get
 };
 
 enum pios_dsm_dev_magic {
@@ -60,12 +64,15 @@ struct pios_dsm_state {
     uint8_t  failsafe_timer;
     uint8_t  frame_found;
     uint8_t  byte_count;
-#ifdef DSM_LOST_FRAME_COUNTER
     uint8_t  frames_lost_last;
-    uint16_t frames_lost;
-#endif
+    float    quality;
 };
 
+/* With an DSM frame rate of 11ms (90Hz) averaging over 18 samples
+ * gives about a 200ms response.
+ */
+#define DSM_FL_WEIGHTED_AVE 18
+
 struct pios_dsm_dev {
     enum pios_dsm_dev_magic   magic;
     const struct pios_dsm_cfg *cfg;
@@ -164,10 +171,8 @@ static void PIOS_DSM_ResetState(struct pios_dsm_dev *dsm_dev)
     state->receive_timer    = 0;
     state->failsafe_timer   = 0;
     state->frame_found      = 0;
-#ifdef DSM_LOST_FRAME_COUNTER
+    state->quality = 0.0f;
     state->frames_lost_last = 0;
-    state->frames_lost      = 0;
-#endif
     PIOS_DSM_ResetChannels(dsm_dev);
 }
 
@@ -183,13 +188,24 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
     static uint8_t resolution    = 11;
     uint32_t channel_log = 0;
 
-#ifdef DSM_LOST_FRAME_COUNTER
+    // *** UNTESTED CODE ***
+#ifdef DSM_LINK_QUALITY
     /* increment the lost frame counter */
     uint8_t frames_lost = state->received_data[0];
-    state->frames_lost += (frames_lost - state->frames_lost_last);
-    state->frames_lost_last = frames_lost;
-#endif
 
+    /* We only get a lost frame count when the next good frame comes in */
+    /* Present quality as a weighted average of good frames */
+    /* First consider the bad frames */
+    for (int i = 0; i < frames_lost - state->frames_lost_last; i++) {
+        state->quality = (state->quality * (DSM_FL_WEIGHTED_AVE - 1)) /
+                         DSM_FL_WEIGHTED_AVE;
+    }
+    /* And now the good frame */
+    state->quality = ((state->quality * (DSM_FL_WEIGHTED_AVE - 1)) +
+                      100) / DSM_FL_WEIGHTED_AVE;
+
+    state->frames_lost_last = frames_lost;
+#endif /* DSM_LINK_QUALITY */
 
     /* unroll channels */
     uint8_t *s    = &(state->received_data[2]);
@@ -238,11 +254,6 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
         }
     }
 
-#ifdef DSM_LOST_FRAME_COUNTER
-    /* put lost frames counter into the last channel for debugging */
-    state->channel_data[PIOS_DSM_NUM_INPUTS - 1] = state->frames_lost;
-#endif
-
     /* all channels processed */
     return 0;
 
@@ -406,6 +417,19 @@ static void PIOS_DSM_Supervisor(uint32_t dsm_id)
     }
 }
 
+static uint8_t PIOS_DSM_Quality_Get(uint32_t dsm_id)
+{
+    struct pios_dsm_dev *dsm_dev = (struct pios_dsm_dev *)dsm_id;
+
+    bool valid = PIOS_DSM_Validate(dsm_dev);
+
+    PIOS_Assert(valid);
+
+    struct pios_dsm_state *state = &(dsm_dev->state);
+
+    return (uint8_t)(state->quality + 0.5f);
+}
+
 #endif /* PIOS_INCLUDE_DSM */
 
 /**
diff --git a/flight/pios/stm32f4xx/pios_dsm.c b/flight/pios/stm32f4xx/pios_dsm.c
index 055f9505c..764bc231b 100644
--- a/flight/pios/stm32f4xx/pios_dsm.c
+++ b/flight/pios/stm32f4xx/pios_dsm.c
@@ -34,12 +34,16 @@
 
 #include "pios_dsm_priv.h"
 
+// *** UNTESTED CODE ***
+#define DSM_LINK_QUALITY
+
 #ifndef PIOS_INCLUDE_RTC
 #error PIOS_INCLUDE_RTC must be used to use DSM
 #endif
 
 /* Forward Declarations */
 static int32_t PIOS_DSM_Get(uint32_t rcvr_id, uint8_t channel);
+static uint8_t PIOS_DSM_Quality_Get(uint32_t rcvr_id);
 static uint16_t PIOS_DSM_RxInCallback(uint32_t context,
                                       uint8_t *buf,
                                       uint16_t buf_len,
@@ -49,7 +53,8 @@ static void PIOS_DSM_Supervisor(uint32_t dsm_id);
 
 /* Local Variables */
 const struct pios_rcvr_driver pios_dsm_rcvr_driver = {
-    .read = PIOS_DSM_Get,
+    .read        = PIOS_DSM_Get,
+    .get_quality = PIOS_DSM_Quality_Get
 };
 
 enum pios_dsm_dev_magic {
@@ -63,12 +68,15 @@ struct pios_dsm_state {
     uint8_t  failsafe_timer;
     uint8_t  frame_found;
     uint8_t  byte_count;
-#ifdef DSM_LOST_FRAME_COUNTER
     uint8_t  frames_lost_last;
-    uint16_t frames_lost;
-#endif
+    float    quality;
 };
 
+/* With an DSM frame rate of 11ms (90Hz) averaging over 18 samples
+ * gives about a 200ms response.
+ */
+#define DSM_FL_WEIGHTED_AVE 18
+
 struct pios_dsm_dev {
     enum pios_dsm_dev_magic   magic;
     const struct pios_dsm_cfg *cfg;
@@ -167,10 +175,8 @@ static void PIOS_DSM_ResetState(struct pios_dsm_dev *dsm_dev)
     state->receive_timer    = 0;
     state->failsafe_timer   = 0;
     state->frame_found      = 0;
-#ifdef DSM_LOST_FRAME_COUNTER
+    state->quality = 0.0f;
     state->frames_lost_last = 0;
-    state->frames_lost      = 0;
-#endif
     PIOS_DSM_ResetChannels(dsm_dev);
 }
 
@@ -186,12 +192,24 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
     static uint8_t resolution    = 11;
     uint32_t channel_log = 0;
 
-#ifdef DSM_LOST_FRAME_COUNTER
+    // *** UNTESTED CODE ***
+#ifdef DSM_LINK_QUALITY
     /* increment the lost frame counter */
     uint8_t frames_lost = state->received_data[0];
-    state->frames_lost += (frames_lost - state->frames_lost_last);
+
+    /* We only get a lost frame count when the next good frame comes in */
+    /* Present quality as a weighted average of good frames */
+    /* First consider the bad frames */
+    for (int i = 0; i < frames_lost - state->frames_lost_last; i++) {
+        state->quality = (state->quality * (DSM_FL_WEIGHTED_AVE - 1)) /
+                         DSM_FL_WEIGHTED_AVE;
+    }
+    /* And now the good frame */
+    state->quality = ((state->quality * (DSM_FL_WEIGHTED_AVE - 1)) +
+                      100) / DSM_FL_WEIGHTED_AVE;
+
     state->frames_lost_last = frames_lost;
-#endif
+#endif /* DSM_LINK_QUALITY */
 
     /* unroll channels */
     uint8_t *s    = &(state->received_data[2]);
@@ -240,11 +258,6 @@ static int PIOS_DSM_UnrollChannels(struct pios_dsm_dev *dsm_dev)
         }
     }
 
-#ifdef DSM_LOST_FRAME_COUNTER
-    /* put lost frames counter into the last channel for debugging */
-    state->channel_data[PIOS_DSM_NUM_INPUTS - 1] = state->frames_lost;
-#endif
-
     /* all channels processed */
     return 0;
 
@@ -408,6 +421,19 @@ static void PIOS_DSM_Supervisor(uint32_t dsm_id)
     }
 }
 
+static uint8_t PIOS_DSM_Quality_Get(uint32_t dsm_id)
+{
+    struct pios_dsm_dev *dsm_dev = (struct pios_dsm_dev *)dsm_id;
+
+    bool valid = PIOS_DSM_Validate(dsm_dev);
+
+    PIOS_Assert(valid);
+
+    struct pios_dsm_state *state = &(dsm_dev->state);
+
+    return (uint8_t)(state->quality + 0.5f);
+}
+
 #endif /* PIOS_INCLUDE_DSM */
 
 /**

From d28831008f4b49ad65c88bd872eae33c618b3e7e Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Mon, 20 Apr 2015 21:40:42 +0100
Subject: [PATCH 14/16] OP-1803 Return link quality of zero rather than throw
 an exception if no receiver if the receiver port is undefined

---
 flight/pios/common/pios_rcvr.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flight/pios/common/pios_rcvr.c b/flight/pios/common/pios_rcvr.c
index 4ad7046ce..afae44b2a 100644
--- a/flight/pios/common/pios_rcvr.c
+++ b/flight/pios/common/pios_rcvr.c
@@ -129,7 +129,8 @@ uint8_t PIOS_RCVR_GetQuality(uint32_t rcvr_id)
 
     if (!PIOS_RCVR_validate(rcvr_dev)) {
         /* Undefined RCVR port for this board (see pios_board.c) */
-        PIOS_Assert(0);
+        /* As no receiver is available assume min */
+        return 0;
     }
 
     if (!rcvr_dev->driver->get_quality) {

From 8689b2d0caab34124617b8bf82596530be9b95ba Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 8 May 2015 07:38:12 +0100
Subject: [PATCH 15/16] OP-1803 Don't report error codes as %

---
 flight/modules/Receiver/receiver.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/flight/modules/Receiver/receiver.c b/flight/modules/Receiver/receiver.c
index da26c23be..16f1c95d5 100644
--- a/flight/modules/Receiver/receiver.c
+++ b/flight/modules/Receiver/receiver.c
@@ -653,14 +653,19 @@ static bool updateRcvrStatus(
 {
     extern uint32_t pios_rcvr_group_map[];
     bool activity_updated = false;
-    uint8_t quality;
+    int8_t quality;
 
     quality = PIOS_RCVR_GetQuality(pios_rcvr_group_map[group]);
 
+    /* If no driver is detected or any other error then return */
+    if (quality < 0) {
+        return activity_updated;
+    }
+
     /* Compare with previous sample */
     if (quality != fsm->quality) {
         fsm->quality     = quality;
-        ReceiverStatusQualitySet(&quality);
+        ReceiverStatusQualitySet(&fsm->quality);
         activity_updated = true;
     }
 

From 023f157ba89748472b854f685336a1366ae2f1a9 Mon Sep 17 00:00:00 2001
From: Steve Evans <Steve@SCEvans.com>
Date: Fri, 8 May 2015 07:42:32 +0100
Subject: [PATCH 16/16] OP-1803 Disable untested DSM link quality code

---
 flight/pios/stm32f10x/pios_dsm.c | 2 +-
 flight/pios/stm32f4xx/pios_dsm.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/flight/pios/stm32f10x/pios_dsm.c b/flight/pios/stm32f10x/pios_dsm.c
index a058ee9c2..44fa484be 100644
--- a/flight/pios/stm32f10x/pios_dsm.c
+++ b/flight/pios/stm32f10x/pios_dsm.c
@@ -35,7 +35,7 @@
 #include "pios_dsm_priv.h"
 
 // *** UNTESTED CODE ***
-#define DSM_LINK_QUALITY
+#undef DSM_LINK_QUALITY
 
 /* Forward Declarations */
 static int32_t PIOS_DSM_Get(uint32_t rcvr_id, uint8_t channel);
diff --git a/flight/pios/stm32f4xx/pios_dsm.c b/flight/pios/stm32f4xx/pios_dsm.c
index 764bc231b..bdd8244e1 100644
--- a/flight/pios/stm32f4xx/pios_dsm.c
+++ b/flight/pios/stm32f4xx/pios_dsm.c
@@ -35,7 +35,7 @@
 #include "pios_dsm_priv.h"
 
 // *** UNTESTED CODE ***
-#define DSM_LINK_QUALITY
+#undef DSM_LINK_QUALITY
 
 #ifndef PIOS_INCLUDE_RTC
 #error PIOS_INCLUDE_RTC must be used to use DSM