1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-19 14:25:11 +03:00

Ghost support (#7900)

This commit is contained in:
3djc 2020-08-28 15:54:51 +02:00 committed by GitHub
parent 1cfe0edeb6
commit 205ac7a21d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 776 additions and 54 deletions

View file

@ -160,7 +160,8 @@ QString ModuleData::protocolToString(unsigned protocol)
"FrSky ACCESS R9M Lite",
"FrSky ACCESS R9M Lite Pro",
"FrSky XJT lite (D16)", "FrSky XJT lite (D8)", "FrSky XJT lite (LR12)",
"AFHDS3"
"AFHDS3",
"ImmersionRC Ghost"
};
return CHECK_IN_ARRAY(strings, protocol);
@ -221,6 +222,7 @@ int ModuleData::getMaxChannelCount()
case PULSES_XJT_LITE_X16:
case PULSES_PXX_XJT_X16:
case PULSES_CROSSFIRE:
case PULSES_GHOST:
case PULSES_SBUS:
case PULSES_PPM:
return 16;

View file

@ -59,6 +59,7 @@ enum PulsesProtocol {
PULSES_XJT_LITE_D8,
PULSES_XJT_LITE_LR12,
PULSES_AFHDS3,
PULSES_GHOST,
PULSES_PROTOCOL_LAST
};

View file

@ -96,7 +96,7 @@ class ProtocolsConversionTable: public ConversionTable
addConversion(PULSES_ACCESS_R9M, val++);
addConversion(PULSES_PXX_R9M_LITE, val++);
addConversion(PULSES_ACCESS_R9M_LITE, val++);
addConversion(PULSES_PXX_R9M_LITE_PRO, val++);
addConversion(PULSES_GHOST, val++);
addConversion(PULSES_ACCESS_R9M_LITE_PRO, val++);
addConversion(PULSES_SBUS, val++);
@ -104,6 +104,7 @@ class ProtocolsConversionTable: public ConversionTable
addConversion(PULSES_XJT_LITE_X16, val);
addConversion(PULSES_XJT_LITE_D8, val);
addConversion(PULSES_XJT_LITE_LR12, val++);
if (version >= 219) {
addConversion(PULSES_AFHDS3, val++);
}

View file

@ -760,6 +760,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
case PULSES_MULTIMODULE:
case PULSES_CROSSFIRE:
case PULSES_AFHDS3:
case PULSES_GHOST:
return true;
case PULSES_ACCESS_R9M:
return IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || board == BOARD_TARANIS_X9DP_2019 || board == BOARD_X10_EXPRESS || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess"));

View file

@ -84,6 +84,12 @@ QString SensorData::unitString() const
return tr("V");
case UNIT_MILLILITERS_PER_MINUTE:
return tr("ml/minute");
case UNIT_HERZ:
return tr("Hertz");
case UNIT_MS:
return tr("mS");
case UNIT_US:
return tr("uS");
default:
return "";
}

View file

@ -94,10 +94,10 @@ class SensorData {
UNIT_MILLILITERS,
UNIT_FLOZ,
UNIT_MILLILITERS_PER_MINUTE,
UNIT_MAX = UNIT_MILLILITERS_PER_MINUTE,
UNIT_SPARE1,
UNIT_SPARE2,
UNIT_SPARE3,
UNIT_HERZ,
UNIT_MS,
UNIT_US,
UNIT_MAX = UNIT_US,
UNIT_SPARE4,
UNIT_SPARE5,
UNIT_SPARE6,

View file

@ -424,6 +424,7 @@ void ModulePanel::update()
max_rx_num = 20;
break;
case PULSES_CROSSFIRE:
case PULSES_GHOST:
mask |= MASK_CHANNELS_RANGE;
module.channelsCount = 16;
break;

View file

@ -212,7 +212,7 @@ QString ModelPrinter::printModule(int idx)
str << printLabelValue(tr("Delay"), QString("%1us").arg(module.ppm.delay));
}
else {
if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_SBUS)) {
if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_GHOST || module.protocol == PULSES_SBUS)) {
str << printLabelValue(tr("Receiver"), QString::number(module.modelId));
}
if (module.protocol == PULSES_MULTIMODULE) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,88 @@
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # 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. #
---- # #
---- #########################################################################
local backgroundBitmap
local offsetX
local offsetY
local sensors = {}
local options = {
{ "VTX", BOOL, 0 },
}
local function getValues(wgt)
if wgt.options.VTX == 0 then
sensors[1] = string.format("%s", getValue("RFMD"))
sensors[2] = string.format("%d Hz", getValue("FRat"))
sensors[3] = string.format("%d %%", getValue("RQly"))
if (getValue("TPWR") == 0) then
sensors[4] = "Range"
else
sensors[4] = string.format("%d mW", getValue("TPWR"))
end
else
sensors[1] = string.format("%s", getValue("VBan"))
sensors[2] = string.format("%dMHz", getValue("VFrq"))
sensors[3] = string.format("CH: %d", getValue("VChn"))
sensors[4] = string.format("%d mW", getValue("VPwr"))
end
end
local function create(zone, options)
local wgt = { zone=zone, options=options}
backgroundBitmap = Bitmap.open("/WIDGETS/Ghost/img/background.png")
offsetX = (wgt.zone.w - 178) / 2
offsetY = (wgt.zone.h - 148) / 2
return wgt
end
local function update(wgt, options)
wgt.options = options
end
local function background(wgt)
end
function refresh(wgt)
-- runs onty on large enough zone
if wgt.zone.w < 180 or wgt.zone.h < 145 then
return
end
if backgroundBitmap ~= nil then
lcd.drawBitmap(backgroundBitmap, wgt.zone.x + offsetX, wgt.zone.y + offsetY)
end
if getRSSI() ~= 0 then
getValues(wgt)
-- RF Mode/Band
lcd.drawText(wgt.zone.x + offsetX + 75, wgt.zone.y + offsetY + 2, sensors[1], CENTER + DBLSIZE)
-- Frame rate / Frequency
lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 35, sensors[2], CENTER + DBLSIZE)
-- RSSI / Channel
lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 70, sensors[3], CENTER + DBLSIZE)
-- Transmit power
lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 105, sensors[4], CENTER + DBLSIZE)
end
end
return { name="Ghost", options=options, create=create, update=update, refresh=refresh, background=background }

View file

@ -54,6 +54,7 @@ option(MODULE_PROTOCOL_FLEX "Add support for non certified FLEX modules" OFF)
option(MODULE_PROTOCOL_D8 "Add support for D8 modules" ON)
option(FRSKY_RELEASE "Used to build FrSky released firmware" OFF)
option(TBS_RELEASE "Used to build TBS released firmware" OFF)
option(IMRC_RELEASE "Used to build IMRC released firmware" OFF)
option(ALLOW_TRAINER_MULTI "Allow multi trainer" OFF)
# since we reset all default CMAKE compiler flags for firmware builds, provide an alternate way for user to specify additional flags.
@ -348,6 +349,10 @@ if(TBS_RELEASE)
add_definitions(-DTBS_RELEASE)
endif()
if(IMRC_RELEASE)
add_definitions(-DIMRC_RELEASE)
endif()
if(FRSKY_RELEASE)
add_definitions(-DFRSKY_RELEASE)
set(POPUP_LEVEL 3)

View file

@ -158,9 +158,9 @@ const char * const unitsFilenames[] = {
"ml",
"founce",
"mlpm",
"spare1",
"spare2",
"spare3",
"hertz",
"ms",
"us",
"spare4",
"spare5",
"spare6",

View file

@ -270,7 +270,8 @@ enum TelemetryProtocol
PROTOCOL_TELEMETRY_HOTT,
PROTOCOL_TELEMETRY_MULTIMODULE,
PROTOCOL_TELEMETRY_AFHDS3,
PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_AFHDS3,
PROTOCOL_TELEMETRY_GHOST,
PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_GHOST,
PROTOCOL_TELEMETRY_LUA
};
@ -304,10 +305,10 @@ enum TelemetryUnit {
UNIT_MILLILITERS,
UNIT_FLOZ,
UNIT_MILLILITERS_PER_MINUTE,
UNIT_MAX = UNIT_MILLILITERS_PER_MINUTE,
UNIT_SPARE1,
UNIT_SPARE2,
UNIT_SPARE3,
UNIT_HERTZ,
UNIT_MS,
UNIT_US,
UNIT_MAX = UNIT_US,
UNIT_SPARE4,
UNIT_SPARE5,
UNIT_SPARE6,

View file

@ -126,7 +126,7 @@ void onHardwareAntennaSwitchConfirm(const char * result)
#define EXTERNAL_ANTENNA_ROW
#endif
#if defined(CROSSFIRE)
#if (defined(CROSSFIRE) || defined(GHOST))
#define MAX_BAUDRATE_ROW 0
#else
#define MAX_BAUDRATE_ROW HIDDEN_ROW

View file

@ -162,7 +162,7 @@ enum {
ITEM_RADIO_HARDWARE_CAPACITY_CALIB,
#endif
#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000
#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000
ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE,
#endif
@ -278,7 +278,7 @@ void onHardwareAntennaSwitchConfirm(const char * result)
#define TX_CAPACITY_MEASUREMENT_ROWS
#endif
#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000
#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000
#define MAX_BAUD_ROWS 0,
#else
#define MAX_BAUD_ROWS
@ -544,7 +544,7 @@ void menuRadioHardware(event_t event)
break;
#endif
#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000
#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000
case ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE:
lcdDrawTextAlignedLeft(y, STR_MAXBAUDRATE);
lcdDrawNumber(HW_SETTINGS_COLUMN2, y, CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate], attr|LEFT);

View file

@ -584,6 +584,14 @@ bool isModuleUsingSport(uint8_t moduleBay, uint8_t moduleType)
}
}
bool areModulesConflicting(int intModuleType, int extModuleType)
{
if (intModuleType == MODULE_TYPE_ISRM_PXX2)
return (extModuleType == MODULE_TYPE_GHOST);
return false;
}
#if defined(HARDWARE_INTERNAL_MODULE)
bool isInternalModuleAvailable(int moduleType)
{
@ -605,7 +613,7 @@ bool isInternalModuleAvailable(int moduleType)
if (moduleType == MODULE_TYPE_ISRM_PXX2) {
#if defined(PXX2) && defined(INTERNAL_MODULE_PXX2)
return true;
return !areModulesConflicting(moduleType, g_model.moduleData[EXTERNAL_MODULE].type);
#endif
}
@ -620,9 +628,6 @@ bool isInternalModuleAvailable(int moduleType)
bool isExternalModuleAvailable(int moduleType)
{
if (moduleType == MODULE_TYPE_R9M_LITE_PRO_PXX1)
return false;
#if !defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
if (isModuleTypeR9MLite(moduleType) || moduleType == MODULE_TYPE_XJT_LITE_PXX2)
return false;
@ -657,6 +662,11 @@ bool isExternalModuleAvailable(int moduleType)
return false;
#endif
#if !defined(GHOST)
if (moduleType == MODULE_TYPE_GHOST)
return false;
#endif
#if !defined(DSM2)
if (moduleType == MODULE_TYPE_DSM2)
return false;
@ -673,6 +683,9 @@ bool isExternalModuleAvailable(int moduleType)
#endif
#if defined(HARDWARE_INTERNAL_MODULE)
if (areModulesConflicting(g_model.moduleData[INTERNAL_MODULE].type, moduleType))
return false;
if (isTrainerUsingModuleBay() || (isModuleUsingSport(EXTERNAL_MODULE, moduleType) && isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)))
return false;
#endif
@ -697,6 +710,11 @@ bool isRfProtocolAvailable(int protocol)
return false;
}
#endif
#if defined(GHOST)
if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST) {
return false;
}
#endif
#if !defined(MODULE_PROTOCOL_D8)
if (protocol == MODULE_SUBTYPE_PXX1_ACCST_D8) {
return false;
@ -726,6 +744,10 @@ bool isTelemetryProtocolAvailable(int protocol)
return false;
}
if ( protocol== PROTOCOL_TELEMETRY_GHOST) {
return false;
}
#if !defined(MULTIMODULE)
if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM || protocol == PROTOCOL_TELEMETRY_FLYSKY_IBUS || protocol == PROTOCOL_TELEMETRY_MULTIMODULE) {
return false;

View file

@ -175,7 +175,7 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx)
else
return 0;
}
else if (isModuleDSM2(moduleIdx) || isModuleCrossfire(moduleIdx) || isModuleSBUS(moduleIdx)) {
else if (isModuleDSM2(moduleIdx) || isModuleCrossfire(moduleIdx) || isModuleGhost(moduleIdx) || isModuleSBUS(moduleIdx)) {
return 0;
}
else {

View file

@ -1981,6 +1981,9 @@ const luaR_value_entry opentxConstants[] = {
{"UNIT_MILLILITERS", UNIT_MILLILITERS },
{"UNIT_FLOZ", UNIT_FLOZ },
{"UNIT_MILLILITERS_PER_MINUTE", UNIT_MILLILITERS_PER_MINUTE },
{"UNIT_HERTZ", UNIT_HERTZ },
{"UNIT_MS", UNIT_MS },
{"UNIT_US", UNIT_US },
{"UNIT_HOURS", UNIT_HOURS },
{"UNIT_MINUTES", UNIT_MINUTES },
{"UNIT_SECONDS", UNIT_SECONDS },

View file

@ -29,6 +29,9 @@ static const char * const options[] = {
#if defined(CROSSFIRE)
"crossfire",
#endif
#if defined(GHOST)
"ghost",
#endif
#if !defined(MODULE_PROTOCOL_D8)
"eu",
#endif

89
radio/src/pulses/ghost.cpp Executable file
View file

@ -0,0 +1,89 @@
/*
* 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"
// Range for pulses (channels output) is [-1024:+1024]
uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses)
{
static uint8_t lastGhostFrameId = GHST_UL_RC_CHANS_HS4_5TO8;
uint8_t ghostUpper4Offset = 0;
switch (lastGhostFrameId) {
case GHST_UL_RC_CHANS_HS4_5TO8:
ghostUpper4Offset = 0;
break;
case GHST_UL_RC_CHANS_HS4_9TO12:
ghostUpper4Offset = 4;
break;
case GHST_UL_RC_CHANS_HS4_13TO16:
ghostUpper4Offset = 8;
break;
}
uint8_t * buf = frame;
*buf++ = GHST_ADDR_MODULE;
*buf++ = GHST_UL_RC_CHANS_SIZE;
uint8_t * crc_start = buf;
*buf++ = lastGhostFrameId;
// first 4 high speed, 12 bit channels (11 relevant bits with openTx)
uint32_t bits = 0;
uint8_t bitsavailable = 0;
for (int i = 0; i < 4; i++) {
uint32_t value = limit(0, GHST_RC_CTR_VAL_12BIT + (((pulses[i]) << 3) / 5), 2 * GHST_RC_CTR_VAL_12BIT);
bits |= value << bitsavailable;
bitsavailable += GHST_CH_BITS_12;
while (bitsavailable >= 8) {
*buf++ = bits;
bits >>= 8;
bitsavailable -= 8;
}
}
// second 4 lower speed, 8 bit channels
for (int i = 4; i < 8; ++i) {
*buf++ = limit(0, GHST_RC_CTR_VAL_8BIT + (((pulses[i + ghostUpper4Offset]) >> 1) / 5), 2 * GHST_RC_CTR_VAL_8BIT);
}
*buf++ = crc8(crc_start, GHST_UL_RC_CHANS_SIZE - 1);
switch (lastGhostFrameId) {
case GHST_UL_RC_CHANS_HS4_5TO8:
lastGhostFrameId = GHST_UL_RC_CHANS_HS4_9TO12;
break;
case GHST_UL_RC_CHANS_HS4_9TO12:
lastGhostFrameId = GHST_UL_RC_CHANS_HS4_13TO16;
break;
case GHST_UL_RC_CHANS_HS4_13TO16:
lastGhostFrameId = GHST_UL_RC_CHANS_HS4_5TO8;
break;
}
return buf - frame;
}
void setupPulsesGhost()
{
if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) {
uint8_t * pulses = extmodulePulsesData.ghost.pulses;
extmodulePulsesData.ghost.length = createGhostChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]);
}
}

View file

@ -29,11 +29,11 @@ enum ModuleType {
MODULE_TYPE_DSM2,
MODULE_TYPE_CROSSFIRE,
MODULE_TYPE_MULTIMODULE,
MODULE_TYPE_R9M_PXX1,
MODULE_TYPE_R9M_PXX2,
MODULE_TYPE_R9M_LITE_PXX1,
MODULE_TYPE_R9M_LITE_PXX2,
MODULE_TYPE_R9M_LITE_PRO_PXX1, // Doesn't exist
MODULE_TYPE_R9M_PXX1, // R9M
MODULE_TYPE_R9M_PXX2, // R9M ACCESS
MODULE_TYPE_R9M_LITE_PXX1, //R9MLite
MODULE_TYPE_R9M_LITE_PXX2, //R9MLP
MODULE_TYPE_GHOST, // Replaces MODULE_TYPE_R9M_LITE_PRO_PXX1 which doesn't exist
MODULE_TYPE_R9M_LITE_PRO_PXX2,
MODULE_TYPE_SBUS,
MODULE_TYPE_XJT_LITE_PXX2,

View file

@ -33,6 +33,7 @@
#endif
#define CROSSFIRE_CHANNELS_COUNT 16
#define GHOST_CHANNELS_COUNT 12
#if defined(MULTIMODULE)
// When using packed, the pointer in here end up not being aligned, which clang and gcc complain about
@ -150,6 +151,18 @@ inline bool isModuleCrossfire(uint8_t idx)
}
#endif
#if defined(GHOST)
inline bool isModuleGhost(uint8_t idx)
{
return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST;
}
#else
inline bool isModuleGhost(uint8_t idx)
{
return false;
}
#endif
#if defined(PCBSKY9X)
inline bool isExtraModule(uint8_t idx)
{
@ -178,7 +191,7 @@ inline bool isModulePPM(uint8_t moduleIdx)
inline bool isModuleTypeR9MNonAccess(uint8_t type)
{
return type == MODULE_TYPE_R9M_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX1;
return type == MODULE_TYPE_R9M_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX1;
}
inline bool isModuleR9MNonAccess(uint8_t idx)
@ -218,7 +231,7 @@ inline bool isModuleR9MLiteNonPro(uint8_t idx)
inline bool isModuleTypeR9MLitePro(uint8_t type)
{
return type == MODULE_TYPE_R9M_LITE_PRO_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX2;
return type == MODULE_TYPE_R9M_LITE_PRO_PXX2;
}
inline bool isModuleTypeR9MLite(uint8_t type)
@ -384,6 +397,8 @@ inline int8_t sentModuleChannels(uint8_t idx)
{
if (isModuleCrossfire(idx))
return CROSSFIRE_CHANNELS_COUNT;
else if (isModuleGhost(idx))
return GHOST_CHANNELS_COUNT;
else if (isModuleMultimodule(idx) && !isModuleMultimoduleDSM2(idx))
return 16;
else if (isModuleSBUS(idx))
@ -528,7 +543,7 @@ inline bool isTelemAllowedOnBind(uint8_t moduleIndex)
return true;
}
if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1 || g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_LITE_PRO_PXX1) {
if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1) {
if (isModuleR9M_LBT(EXTERNAL_MODULE))
return g_model.moduleData[EXTERNAL_MODULE].pxx.power < R9M_LBT_POWER_200_16CH_NOTELEM;
else

View file

@ -133,7 +133,6 @@ uint8_t getRequiredProtocol(uint8_t module)
#if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
case MODULE_TYPE_R9M_LITE_PXX1:
case MODULE_TYPE_R9M_LITE_PRO_PXX1:
protocol = PROTOCOL_CHANNELS_PXX1_SERIAL;
break;
@ -193,6 +192,12 @@ uint8_t getRequiredProtocol(uint8_t module)
break;
#endif
#if defined(GHOST)
case MODULE_TYPE_GHOST:
protocol = PROTOCOL_CHANNELS_GHOST;
break;
#endif
default:
protocol = PROTOCOL_CHANNELS_NONE;
break;
@ -243,6 +248,12 @@ void enablePulsesExternalModule(uint8_t protocol)
break;
#endif
#if defined(GHOST)
case PROTOCOL_CHANNELS_GHOST:
EXTERNAL_MODULE_ON();
break;
#endif
#if defined(PXX2) && defined(EXTMODULE_USART)
case PROTOCOL_CHANNELS_PXX2_HIGHSPEED:
extmoduleInvertedSerialStart(PXX2_HIGHSPEED_BAUDRATE);
@ -332,6 +343,13 @@ bool setupPulsesExternalModule(uint8_t protocol)
return true;
#endif
#if defined(GHOST)
case PROTOCOL_CHANNELS_GHOST:
setupPulsesGhost();
scheduleNextMixerCalculation(EXTERNAL_MODULE, GHOST_PERIOD);
return true;
#endif
#if defined(MULTIMODULE)
case PROTOCOL_CHANNELS_MULTIMODULE:
setupPulsesMultiExternalModule();

View file

@ -237,6 +237,12 @@ PACK(struct CrossfirePulsesData {
uint8_t length;
});
#define GHOST_FRAME_MAXLEN 16
PACK(struct GhostPulsesData {
uint8_t pulses[GHOST_FRAME_MAXLEN];
uint8_t length;
});
union InternalModulePulsesData {
#if defined(PXX1)
#if defined(INTMODULE_USART)
@ -286,6 +292,8 @@ union ExternalModulePulsesData {
PpmPulsesData<pulse_duration_t> ppm;
CrossfirePulsesData crossfire;
GhostPulsesData ghost;
} __ALIGNED(4);
/* The __ALIGNED keyword is required to align the struct inside the modulePulsesData below,
@ -310,6 +318,7 @@ bool setupPulsesInternalModule();
bool setupPulsesExternalModule();
void setupPulsesDSM2();
void setupPulsesCrossfire();
void setupPulsesGhost();
void setupPulsesMultiExternalModule();
void setupPulsesMultiInternalModule();
void setupPulsesSbus();
@ -364,7 +373,8 @@ enum ChannelsProtocols {
PROTOCOL_CHANNELS_SBUS,
PROTOCOL_CHANNELS_PXX2_LOWSPEED,
PROTOCOL_CHANNELS_PXX2_HIGHSPEED,
PROTOCOL_CHANNELS_AFHDS3
PROTOCOL_CHANNELS_AFHDS3,
PROTOCOL_CHANNELS_GHOST
};
inline void stopPulses()

View file

@ -40,6 +40,8 @@
#define DISPLAY_VERSION "-radiomaster"
#elif defined(TBS_RELEASE)
#define DISPLAY_VERSION "-tbs"
#elif defined(IMRC_RELEASE)
#define DISPLAY_VERSION "-imrc"
#else
#define DISPLAY_VERSION
#endif

View file

@ -14,6 +14,7 @@ option(PPM "PPM TX Module" ON)
option(DSM2 "DSM2 TX Module" ON)
option(SBUS "SBUS TX Module" ON)
option(CROSSFIRE "Crossfire TX Module" ON)
option(GHOST "Ghost TX Module" ON)
option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON)
option(DEBUG_INTERRUPTS "Count interrupts" OFF)
option(DEBUG_LATENCY "Debug latency" OFF)
@ -175,6 +176,18 @@ if(MULTIMODULE OR AFHDS3)
set(SRC ${SRC} telemetry/flysky_ibus.cpp )
endif()
if(GHOST)
add_definitions(-DGHOST)
set(PULSES_SRC
${PULSES_SRC}
ghost.cpp
)
set(SRC
${SRC}
telemetry/ghost.cpp
)
endif()
add_definitions(-DCPUARM)
add_definitions(-DGPS)
add_definitions(-DBOLD_FONT -DBATTGRAPH -DTHRTRACE)

View file

@ -48,6 +48,7 @@ if (PCB STREQUAL X10)
option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON)
set(BLUETOOTH ON)
add_definitions(-DHARDWARE_POWER_MANAGEMENT_UNIT)
add_definitions(-DRADIO_X10E)
elseif (PCBREV STREQUAL T16)
set(FLAVOUR t16)
set(LUA_EXPORT lua_export_t16)

View file

@ -368,6 +368,12 @@ void extmoduleSendNextFrame()
break;
#endif
#if defined(GHOST)
case PROTOCOL_CHANNELS_GHOST:
sportSendBuffer(extmodulePulsesData.ghost.pulses, extmodulePulsesData.ghost.length);
break;
#endif
default:
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE;
break;

View file

@ -321,6 +321,12 @@ void extmoduleSendNextFrame()
break;
#endif
#if defined(GHOST)
case PROTOCOL_CHANNELS_GHOST:
sportSendBuffer(extmodulePulsesData.ghost.pulses, extmodulePulsesData.ghost.length);
break;
#endif
default:
EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE;
break;

View file

@ -76,7 +76,7 @@ bool isForcePowerOffRequested()
bool isModuleSynchronous(uint8_t moduleIdx)
{
uint8_t protocol = moduleState[moduleIdx].protocol;
if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_NONE)
if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_GHOST || protocol == PROTOCOL_CHANNELS_NONE)
return true;
#if defined(INTMODULE_USART) || defined(EXTMODULE_USART)
if (protocol == PROTOCOL_CHANNELS_PXX1_SERIAL)

216
radio/src/telemetry/ghost.cpp Executable file
View file

@ -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);
}

114
radio/src/telemetry/ghost.h Executable file
View file

@ -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_

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -260,7 +260,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Metr.\0""Imper.", "Metrické""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1350,3 +1350,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -263,7 +263,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\012")
#define TR_VUNITSSYSTEM TR("Metrik""Imper.", "Metrisch\0 ""Imperial\0 ")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1357,3 +1357,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -263,7 +263,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1354,3 +1354,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -260,7 +260,7 @@
#define LEN_VUNITSSYSTEM "\010"
#define TR_VUNITSSYSTEM "Métrico\0""Imperial"
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1353,3 +1353,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -281,7 +281,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1361,3 +1361,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -283,7 +283,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\012")
#define TR_VUNITSSYSTEM TR("Métr.\0""Impér.", "Métriques\0""Impériales")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1378,3 +1378,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -284,7 +284,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\011")
#define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metriche\0""Imperiali")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1371,3 +1371,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -264,7 +264,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Mtrsch""Engels", "Metrisch\0 ""Engels\0 ")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1363,3 +1363,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -281,7 +281,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010") /*8 decimal*/
#define TR_VUNITSSYSTEM TR("Metr. ""Imper.", "Metryczn""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1370,3 +1370,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -277,7 +277,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1359,3 +1359,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -278,7 +278,7 @@
#define LEN_VUNITSSYSTEM TR("\006", "\010")
#define TR_VUNITSSYSTEM TR("Metri.""Imper.", "Metriska""Imperial")
#define LEN_VTELEMUNIT "\003"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz"
#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0"
#define STR_V (STR_VTELEMUNIT+1)
#define STR_A (STR_VTELEMUNIT+4)
@ -1370,3 +1370,9 @@
#define ZSTR_CL16 "Cl16"
#define ZSTR_CL17 "Cl17"
#define ZSTR_CL18 "Cl18"
#define ZSTR_FRAME_RATE "FRat"
#define ZSTR_TOTAL_LATENCY "TLat"
#define ZSTR_VTX_FREQ "VFrq"
#define ZSTR_VTX_PWR "VPwr"
#define ZSTR_VTX_CHAN "VChn"
#define ZSTR_VTX_BAND "VBan"

View file

@ -87,7 +87,7 @@
#endif
#define LEN_EXTERNAL_MODULE_PROTOCOLS "\014"
#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""R9MLP\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3"
#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3\0 "
#define LEN_INTERNAL_MODULE_PROTOCOLS LEN_EXTERNAL_MODULE_PROTOCOLS
#define TR_INTERNAL_MODULE_PROTOCOLS TR_EXTERNAL_MODULE_PROTOCOLS

View file

@ -38,6 +38,9 @@ for i, (s, f) in enumerate([("volt", "volt0"), ("volts", "volt1"),
("milliliter", "ml0"), ("milliliters", "ml1"),
("fluid ounce", "founce0"), ("fluid ounces", "founce1"),
("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"),
("hertz", "hertz0"), ("hertz", "hertz1"),
("milisecond", "ms0"), ("miliseconds", "ms1"),
("microsecond", "us0"), ("microseconds", "us1"),
("hour", "hour0"), ("hours", "hour1"),
("minute", "minute0"), ("minutes", "minute1"),
("second", "second0"), ("seconds", "second1"),