vtx: fix VTX_SETTINGS_POWER_COUNT and add dummy entries to saPowerNames
[inav.git] / src / main / rx / ghst.c
blobcdab2668e8c5cdd4fd0a5d23e601330c2270d9a9
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
26 #include "platform.h"
28 #ifdef USE_SERIALRX_GHST
30 #include "build/build_config.h"
31 #include "build/debug.h"
33 #include "common/crc.h"
34 #include "common/maths.h"
35 #include "common/utils.h"
37 #include "drivers/serial.h"
38 #include "drivers/serial_uart.h"
39 #include "drivers/system.h"
40 #include "drivers/time.h"
42 #include "io/serial.h"
44 #include "rx/rx.h"
45 #include "rx/ghst.h"
47 #include "telemetry/ghst.h"
49 #define GHST_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO | SERIAL_BIDIR | SERIAL_BIDIR_PP)
50 #define GHST_PORT_MODE MODE_RXTX // bidirectional on single pin
52 #define GHST_MAX_FRAME_TIME_US 500 // 14 bytes @ 420k = ~450us
53 #define GHST_TIME_BETWEEN_FRAMES_US 4500 // fastest frame rate = 222.22Hz, or 4500us
55 // define the time window after the end of the last received packet where telemetry packets may be sent
56 // NOTE: This allows the Rx to double-up on Rx packets to transmit data other than servo data, but
57 // only if sent < 1ms after the servo data packet.
58 #define GHST_RX_TO_TELEMETRY_MIN_US 1000
59 #define GHST_RX_TO_TELEMETRY_MAX_US 2000
61 // At max frame rate 222Hz we should expect to see each of 3 RC frames at least every 13.5ms
62 // Set the individual frame timeout high-enough to tolerate 2 on-wire frames being lost + some jitter
63 // As a recovery condition we would expect at least 3 packets arriving on time
64 #define GHST_RC_FRAME_TIMEOUT_MS 300 // To accommodate the LR mode (12Hz)
65 #define GHST_RC_FRAME_COUNT_THRESHOLD 4 // should correspond to ~50-60ms in the best case
67 #define GHST_PAYLOAD_OFFSET offsetof(ghstFrameDef_t, type)
69 STATIC_UNIT_TESTED volatile bool ghstFrameAvailable = false;
70 STATIC_UNIT_TESTED volatile bool ghstValidatedFrameAvailable = false;
71 STATIC_UNIT_TESTED volatile bool ghstTransmittingTelemetry = false;
73 STATIC_UNIT_TESTED ghstFrame_t ghstIncomingFrame; // incoming frame, raw, not CRC checked, destination address not checked
74 STATIC_UNIT_TESTED ghstFrame_t ghstValidatedFrame; // validated frame, CRC is ok, destination address is ok, ready for decode
76 STATIC_UNIT_TESTED uint32_t ghstChannelData[GHST_MAX_NUM_CHANNELS];
78 typedef struct ghstFailsafeTracker_s {
79 unsigned onTimePacketCounter;
80 timeMs_t lastSeenMs;
81 } ghstFailsafeTracker_t;
83 static serialPort_t *serialPort;
84 static timeUs_t ghstRxFrameStartAtUs = 0;
85 static timeUs_t ghstRxFrameEndAtUs = 0;
86 static uint8_t telemetryBuf[GHST_FRAME_SIZE_MAX];
87 static uint8_t telemetryBufLen = 0;
88 static ghstFailsafeTracker_t ghstFsTracker[GHST_UL_RC_CHANS_FRAME_COUNT];
90 /* GHST Protocol
91 * Ghost uses 420k baud single-wire, half duplex connection, connected to a FC UART 'Tx' pin
92 * Each control packet is interleaved with one or more corresponding downlink packets
94 * Uplink packet format (Control packets)
95 * <Addr><Len><Type><Payload><CRC>
97 * Addr: u8 Destination address
98 * Len u8 Length includes the packet ID, but not the CRC
99 * CRC u8
101 * Ghost packets are designed to be as short as possible, for minimum latency.
103 * Note that the GHST protocol does not handle, itself, failsafe conditions. Packets are passed from
104 * the Ghost receiver to Betaflight as and when they arrive. Betaflight itself is responsible for
105 * determining when a failsafe is necessary based on dropped packets.
109 #define GHST_FRAME_LENGTH_ADDRESS 1
110 #define GHST_FRAME_LENGTH_FRAMELENGTH 1
111 #define GHST_FRAME_LENGTH_TYPE_CRC 1
113 // called from telemetry/ghst.c
114 void ghstRxWriteTelemetryData(const void *data, int len)
116 len = MIN(len, (int)sizeof(telemetryBuf));
117 memcpy(telemetryBuf, data, len);
118 telemetryBufLen = len;
121 void ghstRxSendTelemetryData(void)
123 // if there is telemetry data to write
124 if (telemetryBufLen > 0) {
125 serialWriteBuf(serialPort, telemetryBuf, telemetryBufLen);
126 telemetryBufLen = 0; // reset telemetry buffer
130 STATIC_UNIT_TESTED uint8_t ghstFrameCRC(ghstFrame_t *pGhstFrame)
132 // CRC includes type and payload
133 uint8_t crc = crc8_dvb_s2(0, pGhstFrame->frame.type);
134 for (int i = 0; i < pGhstFrame->frame.len - GHST_FRAME_LENGTH_TYPE_CRC - 1; ++i) {
135 crc = crc8_dvb_s2(crc, pGhstFrame->frame.payload[i]);
137 return crc;
140 // Receive ISR callback, called back from serial port
141 STATIC_UNIT_TESTED void ghstDataReceive(uint16_t c, void *data)
143 UNUSED(data);
145 static uint8_t ghstFrameIdx = 0;
146 const timeUs_t currentTimeUs = microsISR();
148 if (cmpTimeUs(currentTimeUs, ghstRxFrameStartAtUs) > GHST_MAX_FRAME_TIME_US) {
149 // Character received after the max. frame time, assume that this is a new frame
150 ghstFrameIdx = 0;
153 if (ghstFrameIdx == 0) {
154 // timestamp the start of the frame, to allow us to detect frame sync issues
155 ghstRxFrameStartAtUs = currentTimeUs;
158 // assume frame is 5 bytes long until we have received the frame length
159 // full frame length includes the length of the address and framelength fields
160 const int fullFrameLength = ghstFrameIdx < 3 ? 5 : ghstIncomingFrame.frame.len + GHST_FRAME_LENGTH_ADDRESS + GHST_FRAME_LENGTH_FRAMELENGTH;
162 if (ghstFrameIdx < fullFrameLength) {
163 ghstIncomingFrame.bytes[ghstFrameIdx++] = (uint8_t)c;
164 if (ghstFrameIdx >= fullFrameLength) {
165 ghstFrameIdx = 0;
167 // NOTE: this data is not yet CRC checked, nor do we know whether we are the correct recipient, this is
168 // handled in ghstFrameStatus
169 memcpy(&ghstValidatedFrame, &ghstIncomingFrame, sizeof(ghstIncomingFrame));
170 ghstFrameAvailable = true;
172 // remember what time the incoming (Rx) packet ended, so that we can ensure a quite bus before sending telemetry
173 ghstRxFrameEndAtUs = microsISR();
178 static bool shouldSendTelemetryFrame(void)
180 const timeUs_t now = micros();
181 const timeDelta_t timeSinceRxFrameEndUs = cmpTimeUs(now, ghstRxFrameEndAtUs);
182 return telemetryBufLen > 0 && timeSinceRxFrameEndUs > GHST_RX_TO_TELEMETRY_MIN_US && timeSinceRxFrameEndUs < GHST_RX_TO_TELEMETRY_MAX_US;
185 static void ghstIdle(void)
187 if (ghstTransmittingTelemetry) {
188 ghstTransmittingTelemetry = false;
192 static void ghstUpdateFailsafe(unsigned pktIdx)
194 // pktIdx is an offset of RC channel packet,
195 // We'll track arrival time of each of the frame types we ever saw arriving from this receiver
196 if (pktIdx < GHST_UL_RC_CHANS_FRAME_COUNT) {
197 if (ghstFsTracker[pktIdx].onTimePacketCounter < GHST_RC_FRAME_COUNT_THRESHOLD) {
198 ghstFsTracker[pktIdx].onTimePacketCounter++;
201 ghstFsTracker[pktIdx].lastSeenMs = millis(); // don't need microsecond resolution here
205 static bool ghstDetectFailsafe(void)
207 const timeMs_t currentTimeMs = millis();
208 int pktIdx;
211 // Inspect all of the frame types we ever saw arriving. If any of them times out - assume signal loss
212 // We should track all frame types because we care about all channels, not only AETR. Losing AUX may
213 // prevent the pilot from switching flight mode or disarming which is unsafe and should also be treated
214 // as a failsafe condition
216 for (pktIdx = 0; pktIdx < GHST_UL_RC_CHANS_FRAME_COUNT; pktIdx++) {
218 // If a frame was not seen at least once, it's not sent and we should not detaect failsafe based on that
219 if (ghstFsTracker[pktIdx].lastSeenMs == 0) {
220 continue;
223 // Packet timeout. We didn't receive the packet containing the channel data within GHST_RC_FRAME_TIMEOUT_MS
224 // This is a consistent signal loss, reset the recovery packet counter and report signal loss condition
225 if ((currentTimeMs - ghstFsTracker[pktIdx].lastSeenMs) >= GHST_RC_FRAME_TIMEOUT_MS) {
226 ghstFsTracker[pktIdx].onTimePacketCounter = 0;
227 return true;
230 // Not having at least GHST_RC_FRAME_COUNT_THRESHOLD packets without timeouts is likely caused by intermittent signal
231 // Stick to reporting signal loss
232 if (ghstFsTracker[pktIdx].onTimePacketCounter < GHST_RC_FRAME_COUNT_THRESHOLD) {
233 return true;
237 return false;
240 uint8_t ghstFrameStatus(rxRuntimeConfig_t *rxRuntimeState)
242 UNUSED(rxRuntimeState);
244 if (serialIsIdle(serialPort)) {
245 ghstIdle();
248 uint8_t ghstFailsafeFlag = ghstDetectFailsafe() ? RX_FRAME_FAILSAFE : 0;
250 if (ghstFrameAvailable) {
251 ghstFrameAvailable = false;
253 const uint8_t crc = ghstFrameCRC(&ghstValidatedFrame);
254 const int fullFrameLength = ghstValidatedFrame.frame.len + GHST_FRAME_LENGTH_ADDRESS + GHST_FRAME_LENGTH_FRAMELENGTH;
255 if (crc == ghstValidatedFrame.bytes[fullFrameLength - 1] && ghstValidatedFrame.frame.addr == GHST_ADDR_FC) {
256 ghstValidatedFrameAvailable = true;
257 return ghstFailsafeFlag | RX_FRAME_COMPLETE | RX_FRAME_PROCESSING_REQUIRED; // request callback through ghstProcessFrame to do the decoding work
260 return ghstFailsafeFlag | RX_FRAME_DROPPED; // frame was invalid
263 if (shouldSendTelemetryFrame()) {
264 return ghstFailsafeFlag | RX_FRAME_PROCESSING_REQUIRED;
267 return ghstFailsafeFlag | RX_FRAME_PENDING;
270 static bool ghstProcessFrame(const rxRuntimeConfig_t *rxRuntimeConfig)
272 // Assume that the only way we get here is if ghstFrameStatus returned RX_FRAME_PROCESSING_REQUIRED, which indicates that the CRC
273 // is correct, and the message was actually for us.
275 // do we have a telemetry buffer to send?
276 if (shouldSendTelemetryFrame()) {
277 ghstTransmittingTelemetry = true;
278 ghstRxSendTelemetryData();
281 if (ghstValidatedFrameAvailable) {
282 int startIdx = 0;
284 if (
285 ghstValidatedFrame.frame.type >= GHST_UL_RC_CHANS_HS4_FIRST &&
286 ghstValidatedFrame.frame.type <= GHST_UL_RC_CHANS_HS4_LAST
288 const ghstPayloadPulses_t* const rcChannels = (ghstPayloadPulses_t*)&ghstValidatedFrame.frame.payload;
290 // notify GHST failsafe detection that we received a channel packet
291 ghstUpdateFailsafe(ghstValidatedFrame.frame.type - GHST_UL_RC_CHANS_HS4_FIRST);
293 // all uplink frames contain CH1..4 data (12 bit)
294 ghstChannelData[0] = rcChannels->ch1to4.ch1 >> 1;
295 ghstChannelData[1] = rcChannels->ch1to4.ch2 >> 1;
296 ghstChannelData[2] = rcChannels->ch1to4.ch3 >> 1;
297 ghstChannelData[3] = rcChannels->ch1to4.ch4 >> 1;
299 switch(ghstValidatedFrame.frame.type) {
300 case GHST_UL_RC_CHANS_HS4_RSSI: {
301 const ghstPayloadPulsesRSSI_t* const rssiFrame = (ghstPayloadPulsesRSSI_t*)&ghstValidatedFrame.frame.payload;
302 lqTrackerSet(rxRuntimeConfig->lqTracker, scaleRange(constrain(rssiFrame->lq, 0, 100), 0, 100, 0, RSSI_MAX_VALUE));
304 break;
307 case GHST_UL_RC_CHANS_HS4_5TO8: startIdx = 4; break;
308 case GHST_UL_RC_CHANS_HS4_9TO12: startIdx = 8; break;
309 case GHST_UL_RC_CHANS_HS4_13TO16: startIdx = 12; break;
312 if (startIdx > 0)
314 // remainder of uplink frame contains 4 more channels (8 bit), sent in a round-robin fashion
316 ghstChannelData[startIdx++] = rcChannels->cha << 3;
317 ghstChannelData[startIdx++] = rcChannels->chb << 3;
318 ghstChannelData[startIdx++] = rcChannels->chc << 3;
319 ghstChannelData[startIdx++] = rcChannels->chd << 3;
324 return true;
327 STATIC_UNIT_TESTED uint16_t ghstReadRawRC(const rxRuntimeConfig_t *rxRuntimeState, uint8_t chan)
329 UNUSED(rxRuntimeState);
331 // derived from original SBus scaling, with slight correction for offset (now symmetrical
332 // around OpenTx 0 value)
333 // scaling is:
334 // OpenTx RC PWM
335 // min -1024 172 988us
336 // ctr 0 992 1500us
337 // max 1024 1811 2012us
340 return (5 * (ghstChannelData[chan]+1) / 8) + 880;
343 bool ghstRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeState)
345 for (int iChan = 0; iChan < GHST_MAX_NUM_CHANNELS; ++iChan) {
346 ghstChannelData[iChan] = (16 * PWM_RANGE_MIDDLE) / 10 - 1408;
349 rxRuntimeState->channelCount = GHST_MAX_NUM_CHANNELS;
350 rxRuntimeState->rcReadRawFn = ghstReadRawRC;
351 rxRuntimeState->rcFrameStatusFn = ghstFrameStatus;
352 rxRuntimeState->rcProcessFrameFn = ghstProcessFrame;
354 const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_RX_SERIAL);
355 if (!portConfig) {
356 return false;
359 serialPort = openSerialPort(portConfig->identifier,
360 FUNCTION_RX_SERIAL,
361 ghstDataReceive,
362 NULL,
363 GHST_RX_BAUDRATE,
364 GHST_PORT_MODE,
365 GHST_PORT_OPTIONS | (rxConfig->serialrx_inverted ? SERIAL_INVERTED : 0)
368 return serialPort != NULL;
371 bool ghstRxIsActive(void)
373 return serialPort != NULL;
375 #endif