Ghost support (#7900)
This commit is contained in:
Executable
+216
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const char * const ghstRfProfileValue[GHST_RF_PROFILE_COUNT] = { "Auto", "Norm", "Race", "Pure", "Long" };
|
||||
const char * const ghstVtxBandName[GHST_VTX_BAND_COUNT] = { "- - -" , "IRC", "Race", "BandE", "BandB", "BandA" };
|
||||
|
||||
struct GhostSensor
|
||||
{
|
||||
const uint16_t id;
|
||||
const char * name;
|
||||
const TelemetryUnit unit;
|
||||
const uint8_t precision;
|
||||
};
|
||||
|
||||
// telemetry sensors ID
|
||||
enum
|
||||
{
|
||||
GHOST_ID_RX_RSSI = 0x0001, // Rx-side RSSI
|
||||
GHOST_ID_RX_LQ = 0x0002, // Rx-side link quality
|
||||
GHOST_ID_RX_SNR = 0x0003, // Rx-side signal to noise
|
||||
GHOST_ID_FRAME_RATE = 0x0004, // Tx-side frame rate
|
||||
GHOST_ID_TX_POWER = 0x0005, // Tx-side power output
|
||||
GHOST_ID_RF_MODE = 0x0006, // Tx-side frame rate
|
||||
GHOST_ID_TOTAL_LATENCY = 0x0007, // Tx-side total latency
|
||||
GHOST_ID_VTX_FREQ = 0x0008, // Vtx Frequency (in MHz)
|
||||
GHOST_ID_VTX_POWER = 0x0009, // Vtx Power (in mW)
|
||||
GHOST_ID_VTX_CHAN = 0x000a, // Vtx Channel
|
||||
GHOST_ID_VTX_BAND = 0x000b, // Vtx Band
|
||||
|
||||
};
|
||||
|
||||
const GhostSensor ghostSensors[] = {
|
||||
{GHOST_ID_RX_RSSI, ZSTR_RSSI, UNIT_DB, 0},
|
||||
{GHOST_ID_RX_LQ, ZSTR_RX_QUALITY, UNIT_PERCENT, 0},
|
||||
{GHOST_ID_RX_SNR, ZSTR_RX_SNR, UNIT_DB, 0},
|
||||
|
||||
{GHOST_ID_FRAME_RATE, ZSTR_FRAME_RATE, UNIT_RAW, 0},
|
||||
{GHOST_ID_TX_POWER, ZSTR_TX_POWER, UNIT_MILLIWATTS, 0},
|
||||
{GHOST_ID_RF_MODE, ZSTR_RF_MODE, UNIT_TEXT, 0},
|
||||
{GHOST_ID_TOTAL_LATENCY, ZSTR_TOTAL_LATENCY, UNIT_RAW, 0},
|
||||
|
||||
{GHOST_ID_VTX_FREQ, ZSTR_VTX_FREQ, UNIT_RAW, 0},
|
||||
{GHOST_ID_VTX_POWER, ZSTR_VTX_PWR, UNIT_RAW, 0},
|
||||
{GHOST_ID_VTX_CHAN, ZSTR_VTX_CHAN, UNIT_RAW, 0},
|
||||
{GHOST_ID_VTX_BAND, ZSTR_VTX_BAND, UNIT_TEXT, 0},
|
||||
|
||||
{0x00, NULL, UNIT_RAW, 0},
|
||||
};
|
||||
|
||||
const GhostSensor *getGhostSensor(uint8_t id)
|
||||
{
|
||||
for (const GhostSensor * sensor = ghostSensors; sensor->id; sensor++) {
|
||||
if (id == sensor->id)
|
||||
return sensor;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void processGhostTelemetryValue(uint8_t index, int32_t value)
|
||||
{
|
||||
if (!TELEMETRY_STREAMING())
|
||||
return;
|
||||
|
||||
const GhostSensor * sensor = getGhostSensor(index);
|
||||
setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, value, sensor->unit, sensor->precision);
|
||||
}
|
||||
|
||||
void processGhostTelemetryValueString(const GhostSensor * sensor, const char * str)
|
||||
{
|
||||
int i = 0;
|
||||
if (TELEMETRY_STREAMING()) {
|
||||
do {
|
||||
setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, str[i], UNIT_TEXT, i);
|
||||
} while (str[i++] != 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkGhostTelemetryFrameCRC()
|
||||
{
|
||||
uint8_t len = telemetryRxBuffer[1];
|
||||
uint8_t crc = crc8(&telemetryRxBuffer[2], len - 1);
|
||||
return (crc == telemetryRxBuffer[len + 1]);
|
||||
}
|
||||
|
||||
uint16_t getTelemetryValue_u16(uint8_t index)
|
||||
{
|
||||
return (telemetryRxBuffer[index] << 8) | telemetryRxBuffer[index + 1];
|
||||
}
|
||||
|
||||
uint32_t getTelemetryValue_s32(uint8_t index)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
val <<= 8, val |= telemetryRxBuffer[index + i];
|
||||
return val;
|
||||
}
|
||||
|
||||
void processGhostTelemetryFrame()
|
||||
{
|
||||
if (!checkGhostTelemetryFrameCRC()) {
|
||||
TRACE("[GS] CRC error");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t id = telemetryRxBuffer[2];
|
||||
switch(id) {
|
||||
case GHST_DL_LINK_STAT:
|
||||
{
|
||||
uint8_t rssiVal = min<uint8_t>(telemetryRxBuffer[3], 100);
|
||||
uint8_t lqVal = min<uint8_t>(telemetryRxBuffer[4], 100);
|
||||
uint8_t snrVal = min<uint8_t>(telemetryRxBuffer[5], 100);
|
||||
|
||||
processGhostTelemetryValue(GHOST_ID_RX_RSSI, rssiVal);
|
||||
processGhostTelemetryValue(GHOST_ID_RX_LQ, lqVal);
|
||||
processGhostTelemetryValue(GHOST_ID_RX_SNR, snrVal);
|
||||
|
||||
// give OpenTx the LQ value, not RSSI
|
||||
if (lqVal) {
|
||||
telemetryData.rssi.set(lqVal);
|
||||
telemetryStreaming = TELEMETRY_TIMEOUT10ms;
|
||||
}
|
||||
else {
|
||||
telemetryData.rssi.reset();
|
||||
telemetryStreaming = 0;
|
||||
}
|
||||
|
||||
processGhostTelemetryValue(GHOST_ID_TX_POWER, getTelemetryValue_u16(6));
|
||||
processGhostTelemetryValue(GHOST_ID_FRAME_RATE, getTelemetryValue_u16(8));
|
||||
processGhostTelemetryValue(GHOST_ID_TOTAL_LATENCY, getTelemetryValue_u16(10));
|
||||
uint8_t rfModeEnum = min<uint8_t>(telemetryRxBuffer[12], GHST_RF_PROFILE_MAX);
|
||||
|
||||
// RF mode string, one char at a time
|
||||
const GhostSensor * sensor = getGhostSensor(GHOST_ID_RF_MODE);
|
||||
const char * rfModeString = ghstRfProfileValue[rfModeEnum];
|
||||
processGhostTelemetryValueString(sensor, rfModeString);
|
||||
break;
|
||||
}
|
||||
|
||||
case GHST_DL_VTX_STAT:
|
||||
{
|
||||
uint8_t vtxBandEnum = min<uint8_t>(telemetryRxBuffer[8], GHST_VTX_BAND_MAX);
|
||||
|
||||
const GhostSensor * sensor = getGhostSensor(GHOST_ID_VTX_BAND);
|
||||
const char * vtxBandString = ghstVtxBandName[vtxBandEnum];
|
||||
|
||||
processGhostTelemetryValue(GHOST_ID_VTX_FREQ, getTelemetryValue_u16(4));
|
||||
processGhostTelemetryValue(GHOST_ID_VTX_POWER, getTelemetryValue_u16(6));
|
||||
processGhostTelemetryValue(GHOST_ID_VTX_CHAN, min<uint8_t>(telemetryRxBuffer[9], 8));
|
||||
processGhostTelemetryValueString(sensor, vtxBandString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processGhostTelemetryData(uint8_t data)
|
||||
{
|
||||
if (telemetryRxBufferCount == 0 && data != GHST_ADDR_RADIO) {
|
||||
TRACE("[GH] address 0x%02X error", data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (telemetryRxBufferCount < TELEMETRY_RX_PACKET_SIZE) {
|
||||
telemetryRxBuffer[telemetryRxBufferCount++] = data;
|
||||
}
|
||||
else {
|
||||
TRACE("[GH] array size %d error", telemetryRxBufferCount);
|
||||
telemetryRxBufferCount = 0;
|
||||
}
|
||||
|
||||
if (telemetryRxBufferCount > 4) {
|
||||
uint8_t length = telemetryRxBuffer[1];
|
||||
if (length + 2 == telemetryRxBufferCount) {
|
||||
processGhostTelemetryFrame();
|
||||
telemetryRxBufferCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ghostSetDefault(int index, uint8_t id, uint8_t subId)
|
||||
{
|
||||
TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index];
|
||||
|
||||
telemetrySensor.id = id;
|
||||
telemetrySensor.instance = subId;
|
||||
|
||||
const GhostSensor * sensor = getGhostSensor(id);
|
||||
if (sensor) {
|
||||
TelemetryUnit unit = sensor->unit;
|
||||
uint8_t prec = min<uint8_t>(2, sensor->precision);
|
||||
telemetrySensor.init(sensor->name, unit, prec);
|
||||
}
|
||||
else
|
||||
telemetrySensor.init(id);
|
||||
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _GHOST_H_
|
||||
#define _GHOST_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "dataconstants.h"
|
||||
|
||||
// Device (destination) address
|
||||
#define GHST_ADDR_RADIO 0x80 // phase 1
|
||||
#define GHST_ADDR_MODULE 0x81
|
||||
#define GHST_ADDR_FC 0x82
|
||||
#define GHST_ADDR_GOGGLES 0x83 // phase 2
|
||||
#define GHST_ADDR_5G_TXCTRL 0x84 // phase 3
|
||||
#define GHST_ADDR_5G_TWRSCAN 0x85
|
||||
#define GHST_ADDR_5G_RLY 0x86
|
||||
|
||||
#define GHST_UL_RC_CHANS_HS4_5TO8 0x10 // High Speed 4 channel (12 bits), plus CH5-8 (8 bits)
|
||||
#define GHST_UL_RC_CHANS_HS4_9TO12 0x11 // High Speed 4 channel (12 bits), plus CH9-12 (8 bits)
|
||||
#define GHST_UL_RC_CHANS_HS4_13TO16 0x12 // High Speed 4 channel (12 bits), plus CH13-16 (8 bits)
|
||||
#define GHST_UL_RC_CHANS_SIZE 12 // 1 (type) + 10 (data) + 1 (crc)
|
||||
|
||||
#define GHST_DL_OPENTX_SYNC 0x20
|
||||
#define GHST_DL_LINK_STAT 0x21
|
||||
#define GHST_DL_VTX_STAT 0x22
|
||||
|
||||
#define GHST_RC_CTR_VAL_12BIT 0x7C0 // 0x3e0 << 1
|
||||
#define GHST_RC_CTR_VAL_8BIT 0x7C
|
||||
|
||||
#define GHST_CH_BITS_12 12
|
||||
#define GHST_CH_BITS_8 8
|
||||
|
||||
enum GhstPowerMode
|
||||
{
|
||||
GHST_PWR_16UW = 0,
|
||||
GHST_PWR_100UW = 1,
|
||||
GHST_PWR_1MW = 2,
|
||||
GHST_PWR_25MW = 3,
|
||||
GHST_PWR_100MW = 4,
|
||||
GHST_PWR_200MW = 5,
|
||||
GHST_PWR_350MW = 6,
|
||||
GHST_PWR_500MW = 7,
|
||||
GHST_PWR_600MW = 8,
|
||||
GHST_PWR_1W = 9,
|
||||
GHST_PWR_1_5W = 10,
|
||||
GHST_PWR_2W = 11,
|
||||
GHST_PWR_3W = 12,
|
||||
GHST_PWR_4W = 13,
|
||||
GHST_PWR_COUNT
|
||||
};
|
||||
|
||||
enum GhstRFProfile
|
||||
{
|
||||
GHST_RF_PROFILE_Auto = 0,
|
||||
GHST_RF_PROFILE_Normal = 1,
|
||||
GHST_RF_PROFILE_Race = 2,
|
||||
GHST_RF_PROFILE_PureRace = 3,
|
||||
GHST_RF_PROFILE_LongRange = 4,
|
||||
GHST_RF_PROFILE_MAX = GHST_RF_PROFILE_LongRange,
|
||||
GHST_RF_PROFILE_COUNT
|
||||
};
|
||||
|
||||
enum GhstVtxBand
|
||||
{
|
||||
GHST_VTX_BAND_Unknown = 0,
|
||||
GHST_VTX_BAND_Irc = 1,
|
||||
GHST_VTX_BAND_Race = 2,
|
||||
GHST_VTX_BAND_BandE = 3,
|
||||
GHST_VTX_BAND_BandB = 4,
|
||||
GHST_VTX_BAND_BandA = 5,
|
||||
GHST_VTX_BAND_MAX = GHST_VTX_BAND_BandA,
|
||||
GHST_VTX_BAND_COUNT
|
||||
};
|
||||
|
||||
void processGhostTelemetryData(uint8_t data);
|
||||
void ghostSetDefault(int index, uint8_t id, uint8_t subId);
|
||||
|
||||
#if SPORT_MAX_BAUDRATE < 400000
|
||||
// For radios which can't support telemetry at high rates, offer baud rate choices
|
||||
// (modified vs. unmodified radios)
|
||||
const uint32_t GHOST_BAUDRATES[] = {
|
||||
400000,
|
||||
115200,
|
||||
};
|
||||
const uint8_t GHOST_PERIODS[] = {
|
||||
4,
|
||||
16,
|
||||
};
|
||||
#define GHOST_BAUDRATE GHOST_BAUDRATES[g_eeGeneral.telemetryBaudrate]
|
||||
#define GHOST_PERIOD GHOST_PERIODS[g_eeGeneral.telemetryBaudrate]
|
||||
#else
|
||||
#define GHOST_BAUDRATE 400000
|
||||
#define GHOST_PERIOD 4
|
||||
#endif
|
||||
|
||||
#endif // _GHOST_H_
|
||||
@@ -45,6 +45,13 @@ void processTelemetryData(uint8_t data)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) {
|
||||
processGhostTelemetryData(data);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MULTIMODULE)
|
||||
if (telemetryProtocol == PROTOCOL_TELEMETRY_SPEKTRUM) {
|
||||
processSpektrumTelemetryData(EXTERNAL_MODULE, data, telemetryRxBuffer, telemetryRxBufferCount);
|
||||
@@ -295,6 +302,16 @@ void telemetryInit(uint8_t protocol)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
else if (protocol == PROTOCOL_TELEMETRY_GHOST) {
|
||||
telemetryPortInit(GHOST_BAUDRATE, TELEMETRY_SERIAL_DEFAULT);
|
||||
#if defined(LUA)
|
||||
outputTelemetryBuffer.reset();
|
||||
#endif
|
||||
telemetryPortSetDirectionOutput();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AUX_SERIAL) || defined(PCBSKY9X)
|
||||
else if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) {
|
||||
telemetryPortInit(0, TELEMETRY_SERIAL_DEFAULT);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "frsky.h"
|
||||
#include "crossfire.h"
|
||||
#include "ghost.h"
|
||||
#include "myeeprom.h"
|
||||
#include "io/frsky_sport.h"
|
||||
|
||||
@@ -153,6 +154,12 @@ inline uint8_t modelTelemetryProtocol()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GHOST)
|
||||
if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST) {
|
||||
return PROTOCOL_TELEMETRY_GHOST;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sportUsed && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) {
|
||||
return g_model.telemetryProtocol;
|
||||
}
|
||||
|
||||
@@ -526,6 +526,11 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui
|
||||
crossfireSetDefault(index, id, instance);
|
||||
break;
|
||||
#endif
|
||||
#if defined(GHOST)
|
||||
case PROTOCOL_TELEMETRY_GHOST:
|
||||
ghostSetDefault(index, id, instance);
|
||||
break;
|
||||
#endif
|
||||
#if defined(MULTIMODULE) || defined(AFHDS3)
|
||||
case PROTOCOL_TELEMETRY_FLYSKY_IBUS:
|
||||
flySkySetDefault(index,id, subId, instance);
|
||||
|
||||
Reference in New Issue
Block a user