Files
edgetx-gx12/radio/src/telemetry/frsky_pxx2.cpp
T

537 lines
14 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* 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"
#if defined(PXX2)
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
#include "pulses/pxx2.h"
#include "pulses/pxx2_transport.h"
static_assert(PXX2_FRAME_MAXLENGTH <= INTMODULE_FIFO_SIZE, "");
static const char * const PXX2ModulesNames[] = {
"---",
"XJT",
"ISRM",
"ISRM-PRO",
"ISRM-S",
"R9M",
"R9M Lite",
"R9M Lite Pro",
"ISRM-N",
"ISRM-S-X9",
"ISRM-S-X10E",
"XJT Lite",
"ISRM-S-X10S",
"ISRM-X9LiteS"
};
const char * getPXX2ModuleName(uint8_t modelId)
{
if (modelId < DIM(PXX2ModulesNames))
return PXX2ModulesNames[modelId];
else
return PXX2ModulesNames[0];
}
static const char * const PXX2ReceiversNames[] = {
"---",
"X8R",
"RX8R",
"RX8R Pro",
"RX6R",
"RX4R",
"G-RX8",
"G-RX6",
"X6R",
"X4R",
"X4R SB",
"XSR",
"XSR M",
"RXSR",
"S6R",
"S8R",
"XM",
"XM+",
"XMR",
"R9",
"R9 SLIM",
"R9 SLIM+",
"R9 MINI",
"R9 MM",
"R9 Stab",
"R9 Mini OTA",
"R9 MM OTA",
"R9 SLIM+ OTA",
"Archer X",
"R9MX",
"R9SX",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"---",
"SR10 Plus", // 0x40
"R10 Plus",
"GR8 Plus",
"R8 Plus",
"SR8 Plus",
"GR6 Plus",
"R6 Plus",
"R6M (ESC DC)",
"Rs Plus",
"RS Mini",
"R6FB",
"GR6FB",
"SR12 Plus",
"R12 Plus",
"R6 Mini ESC",
"SR6 Mini",
"SR6 Mini ESC", // 0x50
};
const char * getPXX2ReceiverName(uint8_t modelId)
{
if (modelId < DIM(PXX2ReceiversNames))
return PXX2ReceiversNames[modelId];
else
return PXX2ReceiversNames[0];
}
static void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_GET_HARDWARE_INFO) {
return;
}
ModuleInformation * destination = moduleState[module].moduleInformation;
uint8_t index = frame[3];
uint8_t modelId = frame[4];
uint8_t length = min<uint8_t>(frame[0] - 3, sizeof(PXX2HardwareInformation));
if (index == PXX2_HW_INFO_TX_ID && modelId < DIM(PXX2ModulesNames)) {
memcpy(&destination->information, &frame[4], length);
if (destination->information.capabilities & ~((1 << MODULE_CAPABILITY_COUNT) - 1))
destination->information.capabilityNotSupported = true;
if (!globalData.upgradeModulePopup && destination->information.modelID == PXX2_MODULE_ISRM_S_X10S &&
destination->information.swVersion.major == 0 && destination->information.swVersion.minor == 1 && destination->information.swVersion.revision < 5) {
globalData.upgradeModulePopup = 1;
POPUP_WARNING(STR_MODULE_UPGRADE_ALERT);
}
}
else if (index < PXX2_MAX_RECEIVERS_PER_MODULE) {
memcpy(&destination->receivers[index].information, &frame[4], length);
destination->receivers[index].timestamp = get_tmr10ms();
if (destination->receivers[index].information.capabilities & ~((1 << RECEIVER_CAPABILITY_COUNT) - 1))
destination->information.capabilityNotSupported = true;
}
}
static void processModuleSettingsFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_MODULE_SETTINGS) {
return;
}
ModuleSettings * destination = moduleState[module].moduleSettings;
// Flag1
if (frame[4] & PXX2_TX_SETTINGS_FLAG1_EXTERNAL_ANTENNA)
destination->externalAntenna = 1;
// Power
destination->txPower = frame[5];
destination->state = PXX2_SETTINGS_OK;
destination->timeout = 0;
moduleState[module].mode = MODULE_MODE_NORMAL;
}
static void processReceiverSettingsFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_RECEIVER_SETTINGS) {
return;
}
ReceiverSettings * destination = moduleState[module].receiverSettings;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_FPORT)
destination->fport = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_FASTPWM)
destination->pwmRate = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_TELEMETRY_DISABLED)
destination->telemetryDisabled = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_TELEMETRY_25MW)
destination->telemetry25mw = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_ENABLE_PWM_CH5_CH6)
destination->enablePwmCh5Ch6 = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_FPORT2)
destination->fport2 = 1;
if (frame[4] & PXX2_RX_SETTINGS_FLAG1_SBUS24)
destination->sbus24 = 1;
uint8_t outputsCount = min<uint8_t>(PXX2_MAX_CHANNELS, frame[0] - 4);
destination->outputsCount = outputsCount;
for (uint8_t pin = 0; pin < outputsCount; pin++) {
destination->outputsMapping[pin] = frame[5 + pin];
}
destination->state = PXX2_SETTINGS_OK;
destination->timeout = 0;
moduleState[module].mode = MODULE_MODE_NORMAL;
}
static void processRegisterFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_REGISTER) {
return;
}
PXX2ModuleSetup& mod = reusableBuffer.moduleSetup.pxx2;
switch(frame[3]) {
case 0x00:
if (mod.registerStep == REGISTER_INIT) {
// RX_NAME follows, we store it for the next step
memcpy(mod.registerRxName, (const char *)&frame[4], PXX2_LEN_RX_NAME);
mod.registerLoopIndex = frame[12];
mod.registerStep = REGISTER_RX_NAME_RECEIVED;
}
break;
case 0x01:
if (mod.registerStep == REGISTER_RX_NAME_SELECTED) {
// RX_NAME + PASSWORD follow, we check they are good
if (memcmp(&frame[4], mod.registerRxName, PXX2_LEN_RX_NAME) == 0 &&
memcmp(&frame[12], g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID) == 0) {
mod.registerStep = REGISTER_OK;
moduleState[module].mode = MODULE_MODE_NORMAL;
#if !defined(COLORLCD)
POPUP_INFORMATION(STR_REG_OK);
#endif
}
}
break;
}
}
static void processBindFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_BIND) {
return;
}
BindInformation * destination = moduleState[module].bindInformation;
switch(frame[3]) {
case 0x00:
if (destination->step == BIND_INIT) {
bool found = false;
for (uint8_t i=0; i<destination->candidateReceiversCount; i++) {
if (memcmp(destination->candidateReceiversNames[i], &frame[4], PXX2_LEN_RX_NAME) == 0) {
found = true;
break;
}
}
if (!found && destination->candidateReceiversCount < PXX2_MAX_RECEIVERS_PER_MODULE) {
memcpy(destination->candidateReceiversNames[destination->candidateReceiversCount++], &frame[4], PXX2_LEN_RX_NAME);
if (moduleState[module].callback) {
moduleState[module].callback();
}
}
}
break;
case 0x01:
if (destination->step == BIND_START) {
if (memcmp(&destination->candidateReceiversNames[destination->selectedReceiverIndex], &frame[4], PXX2_LEN_RX_NAME) == 0) {
memcpy(g_model.moduleData[module].pxx2.receiverName[destination->rxUid], &frame[4], PXX2_LEN_RX_NAME);
storageDirty(EE_MODEL);
destination->step = BIND_WAIT;
destination->timeout = get_tmr10ms() + 30;
}
}
break;
case 0x02:
if (destination->step == BIND_INFO_REQUEST) {
if (memcmp(&destination->candidateReceiversNames[destination->selectedReceiverIndex], &frame[4], PXX2_LEN_RX_NAME) == 0) {
memcpy(&destination->receiverInformation, &frame[12], sizeof(PXX2HardwareInformation));
if (moduleState[module].callback) {
moduleState[module].callback();
}
}
}
break;
}
}
static void processResetFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_RESET) {
return;
}
if (reusableBuffer.moduleSetup.pxx2.resetReceiverIndex == frame[3]) {
memclear(g_model.moduleData[module].pxx2.receiverName[reusableBuffer.moduleSetup.pxx2.resetReceiverIndex], PXX2_LEN_RX_NAME);
}
moduleState[module].mode = MODULE_MODE_NORMAL;
}
static void processTelemetryFrame(uint8_t module, const uint8_t * frame)
{
uint8_t origin = frame[3] & 0x03;
sportProcessTelemetryPacketWithoutCrc(module, origin, &frame[4]);
}
#if defined(INTERNAL_MODULE_PXX2) && defined(ACCESS_DENIED) && !defined(SIMU)
extern "C" {
#include "AccessDenied/access_denied.h"
};
volatile int16_t authenticateFrames = 0;
static void processAuthenticationFrame(uint8_t module, const uint8_t * frame,
const etx_serial_driver_t* drv, void* ctx)
{
uint8_t cryptoType = frame[3];
uint8_t messageDigest[16] = {0};
if (frame[0] == 4 && PXX2_AUTH_REFUSED_FLAG == frame[4]) {
if (!globalData.upgradeModulePopup) {
globalData.upgradeModulePopup = 1;
POPUP_INFORMATION(STR_AUTH_FAILURE);
}
TRACE("[authFailed]\r\n", authenticateFrames);
return;
}
if (INTERNAL_MODULE == module &&
access_denied(cryptoType, frame + 4, messageDigest)) {
moduleState[module].mode = MODULE_MODE_AUTHENTICATION;
uint8_t* module_buffer = pulsesGetModuleBuffer(module);
Pxx2Pulses pxx2(module_buffer);
pxx2.setupAuthenticationFrame(module, cryptoType, (const uint8_t *)messageDigest);
drv->sendBuffer(ctx, module_buffer, pxx2.getSize());
// we remain in AUTHENTICATION mode to avoid a CHANNELS frame is sent at the
// end of the mixing process
authenticateFrames++;
TRACE("[authFrame %d]\r\n", authenticateFrames);
}
if (!globalData.upgradeModulePopup) {
if (globalData.authenticationCount >= 2) {
globalData.upgradeModulePopup = 1;
POPUP_WARNING(STR_MODULE_UPGRADE_ALERT);
}
else {
globalData.authenticationCount += 1;
}
}
}
#else
#define processAuthenticationFrame(module, frame, drv, ctx)
#endif
static void processSpectrumAnalyserFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_SPECTRUM_ANALYSER) {
return;
}
uint32_t frequency = *((uint32_t *)&frame[4]);
int8_t power = *((int8_t *)&frame[8]);
// center = 2440000000; // 2440MHz
// span = 40000000; // 40MHz
// left = 2440000000 - 20000000
// step = 10000
int32_t offset = frequency - (reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2);
TRACE("Fq=%u => %d, Pw=%d", frequency, offset, int32_t(power));
uint32_t x = offset / reusableBuffer.spectrumAnalyser.step;
if (x < LCD_W) {
reusableBuffer.spectrumAnalyser.bars[x] = max<int>(0, -SPECTRUM_ANALYSER_POWER_FLOOR + power); // we remove everything below -120dB
#if defined(COLORLCD)
if (reusableBuffer.spectrumAnalyser.bars[x] > reusableBuffer.spectrumAnalyser.max[x])
reusableBuffer.spectrumAnalyser.max[x] = reusableBuffer.spectrumAnalyser.bars[x];
#endif
}
}
static void processPowerMeterFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_POWER_METER) {
return;
}
reusableBuffer.powerMeter.power = *((int16_t *)&frame[8]);
if (!reusableBuffer.powerMeter.peak || reusableBuffer.powerMeter.power > reusableBuffer.powerMeter.peak) {
reusableBuffer.powerMeter.peak = reusableBuffer.powerMeter.power;
}
}
static void processOtaUpdateFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_OTA_UPDATE) {
return;
}
OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation;
if (destination->step == OTA_UPDATE_START) {
if (frame[3] == 0x00 && memcmp(destination->candidateReceiversNames[destination->selectedReceiverIndex], &frame[4], PXX2_LEN_RX_NAME) == 0) {
destination->step = OTA_UPDATE_START_ACK;
}
}
else if (destination->step == OTA_UPDATE_TRANSFER) {
uint32_t address = *((uint32_t *)&frame[4]);
if (frame[3] == 0x01 && destination->address == address) {
destination->step = OTA_UPDATE_TRANSFER_ACK;
}
}
else if (destination->step == OTA_UPDATE_EOF) {
if (frame[3] == 0x02) {
destination->step = OTA_UPDATE_EOF_ACK;
}
}
}
static void processModuleFrame(uint8_t module, const uint8_t *frame,
const etx_serial_driver_t* drv, void* ctx)
{
switch (frame[2]) {
case PXX2_TYPE_ID_HW_INFO:
processGetHardwareInfoFrame(module, frame);
break;
case PXX2_TYPE_ID_TX_SETTINGS:
processModuleSettingsFrame(module, frame);
break;
case PXX2_TYPE_ID_RX_SETTINGS:
processReceiverSettingsFrame(module, frame);
break;
case PXX2_TYPE_ID_REGISTER:
processRegisterFrame(module, frame);
break;
case PXX2_TYPE_ID_BIND:
processBindFrame(module, frame);
break;
case PXX2_TYPE_ID_TELEMETRY:
processTelemetryFrame(module, frame);
break;
case PXX2_TYPE_ID_AUTHENTICATION:
processAuthenticationFrame(module, frame, drv, ctx);
break;
case PXX2_TYPE_ID_RESET:
processResetFrame(module, frame);
break;
}
}
static void processToolsFrame(uint8_t module, const uint8_t * frame)
{
switch (frame[2]) {
case PXX2_TYPE_ID_POWER_METER:
processPowerMeterFrame(module, frame);
break;
case PXX2_TYPE_ID_SPECTRUM:
processSpectrumAnalyserFrame(module, frame);
break;
}
}
void processPXX2Frame(uint8_t module, const uint8_t * frame,
const etx_serial_driver_t* drv, void* ctx)
{
switch (frame[1]) {
case PXX2_TYPE_C_MODULE:
processModuleFrame(module, frame, drv, ctx);
break;
case PXX2_TYPE_C_POWER_METER:
processToolsFrame(module, frame);
break;
case PXX2_TYPE_C_OTA:
processOtaUpdateFrame(module, frame);
break;
default:
break;
}
}
#endif