From 850e3580ed02b057afc6fccc0a780112770f9230 Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Fri, 30 Apr 2021 16:51:41 +0200 Subject: [PATCH] New radio driver for MD-3x0 platform, still requiring a bit of debugging --- openrtx/include/calibration/calibUtils.h | 9 + openrtx/include/interfaces/radio.h | 28 +- openrtx/include/rtx/rtx.h | 16 +- openrtx/src/rtx/OpMode_FM.cpp | 8 +- openrtx/src/rtx/rtx.cpp | 15 +- platform/drivers/ADC/ADC1_MDx.h | 8 + platform/drivers/baseband/SKY72310.h | 8 + platform/drivers/baseband/radio_MD3x0.c | 305 ------------------- platform/drivers/baseband/radio_MD3x0.cpp | 349 ++++++++++++++++++++++ 9 files changed, 411 insertions(+), 335 deletions(-) delete mode 100644 platform/drivers/baseband/radio_MD3x0.c create mode 100644 platform/drivers/baseband/radio_MD3x0.cpp diff --git a/openrtx/include/calibration/calibUtils.h b/openrtx/include/calibration/calibUtils.h index c3ac5f0d..dda68ac7 100644 --- a/openrtx/include/calibration/calibUtils.h +++ b/openrtx/include/calibration/calibUtils.h @@ -23,6 +23,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** * This function allows to obtain the value of a given calibration parameter for * frequencies outside the calibration points. It works by searching the two @@ -42,4 +46,9 @@ uint8_t interpCalParameter(const freq_t freq, const freq_t *calPoints, const uint8_t *param, const uint8_t elems); + +#ifdef __cplusplus +} +#endif + #endif /* CALIB_UTILS_H */ diff --git a/openrtx/include/interfaces/radio.h b/openrtx/include/interfaces/radio.h index e730e7f2..4e129995 100644 --- a/openrtx/include/interfaces/radio.h +++ b/openrtx/include/interfaces/radio.h @@ -39,33 +39,18 @@ extern "C" { /** * Initialise low-level radio transceiver. */ -void radio_init(); +void radio_init(const rtxStatus_t *rtxState); /** * Shut down low-level radio transceiver. */ void radio_terminate(); -/** - * - */ -void radio_setBandwidth(const enum bandwidth bw); - /** * */ void radio_setOpmode(const enum opmode mode); -/** - * - */ -void radio_setVcoFrequency(const freq_t frequency, const bool isTransmitting); - -/** - * - */ -void radio_setCSS(const tone_t rxCss, const tone_t txCss); - /** * */ @@ -79,7 +64,7 @@ void radio_enableRx(); /** * */ -void radio_enableTx(const float txPower, const bool enableCss); +void radio_enableTx(); /** * @@ -89,12 +74,17 @@ void radio_disableRtx(); /** * */ -void radio_updateCalibrationParams(const rtxStatus_t *rtxCfg); +void radio_updateConfiguration(); /** * */ -float radio_getRssi(const freq_t rxFreq); +float radio_getRssi(); + +/** + * + */ +enum opstatus radio_getStatus(); #ifdef __cplusplus } diff --git a/openrtx/include/rtx/rtx.h b/openrtx/include/rtx/rtx.h index 395feba1..0447073a 100644 --- a/openrtx/include/rtx/rtx.h +++ b/openrtx/include/rtx/rtx.h @@ -26,13 +26,19 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { - uint8_t opMode : 2, /**< Operating mode (FM, DMR, ...) */ - bandwidth : 2, /**< Channel bandwidth */ + uint8_t opMode; /**< Operating mode (FM, DMR, ...) */ + + uint8_t bandwidth : 2, /**< Channel bandwidth */ txDisable : 1, /**< Disable TX operation */ scan : 1, /**< Scan enabled */ - opStatus : 2; /**< Operating status (OFF, ...) */ + opStatus : 2, /**< Operating status (OFF, ...) */ + _padding : 2; /**< Padding to 8 bits */ freq_t rxFrequency; /**< RX frequency, in Hz */ freq_t txFrequency; /**< TX frequency, in Hz */ @@ -119,4 +125,8 @@ void rtx_taskFunc(); */ float rtx_getRssi(); +#ifdef __cplusplus +} +#endif + #endif /* RTX_H */ diff --git a/openrtx/src/rtx/OpMode_FM.cpp b/openrtx/src/rtx/OpMode_FM.cpp index 22c0b1a8..d7a9d9ac 100644 --- a/openrtx/src/rtx/OpMode_FM.cpp +++ b/openrtx/src/rtx/OpMode_FM.cpp @@ -89,6 +89,8 @@ void OpMode_FM::disable() void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg) { + (void) newCfg; + // RX logic if(status->opStatus == RX) { @@ -119,7 +121,6 @@ void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg) { radio_disableRtx(); - radio_setVcoFrequency(status->rxFrequency, false); radio_enableRx(); status->opStatus = RX; enterRx = false; @@ -133,8 +134,7 @@ void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg) radio_disableRtx(); audio_enableMic(); - radio_setVcoFrequency(status->txFrequency, true); - radio_enableTx(status->txPower, status->txToneEn); + radio_enableTx(); status->opStatus = TX; } @@ -148,7 +148,7 @@ void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg) enterRx = true; } - /* Led control logic */ + // Led control logic switch(status->opStatus) { case RX: diff --git a/openrtx/src/rtx/rtx.cpp b/openrtx/src/rtx/rtx.cpp index baca4fb7..e1551972 100644 --- a/openrtx/src/rtx/rtx.cpp +++ b/openrtx/src/rtx/rtx.cpp @@ -61,12 +61,13 @@ void rtx_init(pthread_mutex_t *m) /* * Initialise low-level platform-specific driver */ - radio_init(); + radio_init(&rtxStatus); + radio_updateConfiguration(); /* * Initial value for RSSI filter */ - rssi = radio_getRssi(rtxStatus.rxFrequency); + rssi = radio_getRssi(); reinitFilter = false; } @@ -127,6 +128,9 @@ void rtx_taskFunc() */ if(currMode->getID() != rtxStatus.opMode) { + // Forward opMode change also to radio driver + radio_setOpmode(static_cast< enum opmode >(rtxStatus.opMode)); + currMode->disable(); rtxStatus.opStatus = OFF; @@ -139,6 +143,9 @@ void rtx_taskFunc() currMode->enable(); } + + // Tell radio driver that there was a change in its configuration. + radio_updateConfiguration(); } /* @@ -163,11 +170,11 @@ void rtx_taskFunc() { if(!reinitFilter) { - rssi = 0.74*radio_getRssi(rtxStatus.rxFrequency) + 0.26*rssi; + rssi = 0.74*radio_getRssi() + 0.26*rssi; } else { - rssi = radio_getRssi(rtxStatus.rxFrequency); + rssi = radio_getRssi(); reinitFilter = false; } } diff --git a/platform/drivers/ADC/ADC1_MDx.h b/platform/drivers/ADC/ADC1_MDx.h index 728354b6..44eae7e7 100644 --- a/platform/drivers/ADC/ADC1_MDx.h +++ b/platform/drivers/ADC/ADC1_MDx.h @@ -20,6 +20,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Driver for ADC1, used on all the MDx devices to continuously sample battery * voltage and other values. @@ -81,4 +85,8 @@ void adc1_terminate(); */ float adc1_getMeasurement(uint8_t ch); +#ifdef __cplusplus +} +#endif + #endif /* ADC1_H */ diff --git a/platform/drivers/baseband/SKY72310.h b/platform/drivers/baseband/SKY72310.h index 5dbb6cca..885e4c6e 100644 --- a/platform/drivers/baseband/SKY72310.h +++ b/platform/drivers/baseband/SKY72310.h @@ -21,6 +21,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Driver for SKY73210 PLL IC. * @@ -62,4 +66,8 @@ bool SKY73210_isPllLocked(); */ bool SKY73210_spiInUse(); +#ifdef __cplusplus +} +#endif + #endif /* SKY73210_H */ diff --git a/platform/drivers/baseband/radio_MD3x0.c b/platform/drivers/baseband/radio_MD3x0.c deleted file mode 100644 index 0127fb37..00000000 --- a/platform/drivers/baseband/radio_MD3x0.c +++ /dev/null @@ -1,305 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * 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. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "HR_C5000.h" -#include "SKY72310.h" - -static const freq_t IF_FREQ = 49950000; /* Intermediate frequency: 49.95MHz */ - -const md3x0Calib_t *calData; /* Pointer to calibration data */ - -uint8_t vtune_rx = 0; /* Tuning voltage for RX input filter */ -uint8_t txpwr_lo = 0; /* APC voltage for TX output power control, low power */ -uint8_t txpwr_hi = 0; /* APC voltage for TX output power control, high power */ - -enum opmode currOpMode; /* Current operating mode, needed for TX control */ - -/* - * Parameters for RSSI voltage (mV) to input power (dBm) conversion. - * Gain is constant, while offset values are aligned to calibration frequency - * test points. - * Thanks to Wojciech SP5WWP for the measurements! - */ -float rssi_gain = 22.0f; -float rssi_offset[] = {3277.618f, 3654.755f, 3808.191f, - 3811.318f, 3804.936f, 3806.591f, - 3723.882f, 3621.373f, 3559.782f}; - -void radio_init() -{ - /* - * Load calibration data - */ - calData = ((const md3x0Calib_t *) platform_getCalibrationData()); - - /* - * Configure RTX GPIOs - */ - gpio_setMode(PLL_PWR, OUTPUT); - gpio_setMode(VCOVCC_SW, OUTPUT); - gpio_setMode(DMR_SW, OUTPUT); - gpio_setMode(WN_SW, OUTPUT); - gpio_setMode(FM_SW, OUTPUT); - gpio_setMode(RF_APC_SW, OUTPUT); - gpio_setMode(TX_STG_EN, OUTPUT); - gpio_setMode(RX_STG_EN, OUTPUT); - - gpio_setMode(FM_MUTE, OUTPUT); - gpio_clearPin(FM_MUTE); - - gpio_clearPin(PLL_PWR); /* PLL off */ - gpio_setPin(VCOVCC_SW); /* VCOVCC high enables RX VCO, TX VCO if low */ - gpio_setPin(WN_SW); /* 25kHz bandwidth */ - gpio_clearPin(DMR_SW); /* Disconnect HR_C5000 input IF signal and audio out */ - gpio_clearPin(FM_SW); /* Disconnect analog FM audio path */ - gpio_clearPin(RF_APC_SW); /* Disable RF power control */ - gpio_clearPin(TX_STG_EN); /* Disable TX power stage */ - gpio_clearPin(RX_STG_EN); /* Disable RX input stage */ - - /* - * Configure and enable DAC - */ - gpio_setMode(APC_TV, INPUT_ANALOG); - gpio_setMode(MOD2_BIAS, INPUT_ANALOG); - - RCC->APB1ENR |= RCC_APB1ENR_DACEN; - DAC->CR = DAC_CR_EN2 | DAC_CR_EN1; - DAC->DHR12R2 = 0; - DAC->DHR12R1 = 0; - - /* - * Enable and configure PLL - */ - gpio_setPin(PLL_PWR); - SKY73210_init(); - - /* - * Configure HR_C5000 - */ - C5000_init(); - - /* - * Modulation bias settings, as per TYT firmware. - */ - DAC->DHR12R2 = (calData->freqAdjustMid)*4 + 0x600; - C5000_setModOffset(calData->freqAdjustMid); -} - -void radio_terminate() -{ - SKY73210_terminate(); - - gpio_clearPin(PLL_PWR); /* PLL off */ - gpio_clearPin(DMR_SW); /* Disconnect HR_C5000 input IF signal and audio out */ - gpio_clearPin(FM_SW); /* Disconnect analog FM audio path */ - gpio_clearPin(RF_APC_SW); /* Disable RF power control */ - gpio_clearPin(TX_STG_EN); /* Disable TX power stage */ - gpio_clearPin(RX_STG_EN); /* Disable RX input stage */ - - DAC->DHR12R2 = 0; - DAC->DHR12R1 = 0; - RCC->APB1ENR &= ~RCC_APB1ENR_DACEN; -} - -void radio_setBandwidth(const enum bandwidth bw) -{ - switch(bw) - { - case BW_12_5: - gpio_clearPin(WN_SW); - C5000_setModFactor(0x1E); - break; - - case BW_20: - gpio_setPin(WN_SW); - C5000_setModFactor(0x30); - break; - - case BW_25: - gpio_setPin(WN_SW); - C5000_setModFactor(0x3C); - break; - - default: - break; - } -} - -void radio_setOpmode(const enum opmode mode) -{ - currOpMode = mode; - switch(mode) - { - case FM: - gpio_clearPin(DMR_SW); - gpio_setPin(FM_SW); - C5000_fmMode(); - break; - - case DMR: - gpio_clearPin(FM_SW); - gpio_setPin(DMR_SW); - //C5000_dmrMode(); - break; - - default: - break; - } -} - -void radio_setVcoFrequency(const freq_t frequency, const bool isTransmitting) -{ - float freq = ((float) frequency); - - if(!isTransmitting) - { - freq = freq - IF_FREQ; - } - - SKY73210_setFrequency(freq, 5); -} - -void radio_setCSS(const tone_t rxCss, const tone_t txCss) -{ - (void) rxCss; - float tone = ((float) txCss) / 10.0f; - toneGen_setToneFreq(tone); -} - -bool radio_checkRxDigitalSquelch() -{ - return true; -} - -void radio_enableRx() -{ - gpio_clearPin(TX_STG_EN); - - gpio_clearPin(RF_APC_SW); - gpio_setPin(VCOVCC_SW); - - DAC->DHR12L1 = vtune_rx * 0xFF; - - gpio_setPin(RX_STG_EN); - - if(currOpMode == FM) - { - gpio_setPin(FM_MUTE); - } -} - -void radio_enableTx(const float txPower, const bool enableCss) -{ - gpio_clearPin(RX_STG_EN); - - gpio_setPin(RF_APC_SW); - gpio_clearPin(VCOVCC_SW); - - /* - * TODO: increase granularity - */ - uint8_t apc = (txPower > 1.0f) ? txpwr_hi : txpwr_lo; - DAC->DHR12L1 = apc * 0xFF; - - if(currOpMode == FM) - { - C5000_startAnalogTx(); - } - - gpio_setPin(TX_STG_EN); - - if(enableCss) - { - toneGen_toneOn(); - } -} - -void radio_disableRtx() -{ - /* If we are currently transmitting, stop tone and C5000 TX */ - if(gpio_readPin(TX_STG_EN) == 1) - { - toneGen_toneOff(); - C5000_stopAnalogTx(); - } - - gpio_clearPin(TX_STG_EN); - gpio_clearPin(RX_STG_EN); - gpio_clearPin(FM_MUTE); -} - -void radio_updateCalibrationParams(const rtxStatus_t* rtxCfg) -{ - /* Tuning voltage for RX input filter */ - vtune_rx = interpCalParameter(rtxCfg->rxFrequency, calData->rxFreq, - calData->rxSensitivity, 9); - - /* APC voltage for TX output power control */ - txpwr_lo = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, - calData->txLowPower, 9); - - txpwr_hi = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, - calData->txHighPower, 9); - - /* HR_C5000 modulation amplitude */ - const uint8_t *Ical = calData->sendIrange; - const uint8_t *Qcal = calData->sendQrange; - - if(rtxCfg->opMode == FM) - { - Ical = calData->analogSendIrange; - Qcal = calData->analogSendQrange; - } - - uint8_t I = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, Ical, 9); - uint8_t Q = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, Qcal, 9); - - C5000_setModAmplitude(I, Q); -} - -float radio_getRssi(const freq_t rxFreq) -{ - /* - * On MD3x0 devices, RSSI value is get by reading the analog RSSI output - * from second IF stage (GT3136 IC). - * The corresponding power value is obtained through the linear correlation - * existing between measured voltage in mV and power in dBm. While gain is - * constant, offset depends from the rx frequency. - */ - - uint32_t offset_index = (rxFreq - 400035000)/10000000; - - if(rxFreq < 401035000) offset_index = 0; - if(rxFreq > 479995000) offset_index = 8; - - float rssi_mv = adc1_getMeasurement(ADC_RSSI_CH); - float rssi_dbm = (rssi_mv - rssi_offset[offset_index]) / rssi_gain; - return rssi_dbm; -} diff --git a/platform/drivers/baseband/radio_MD3x0.cpp b/platform/drivers/baseband/radio_MD3x0.cpp new file mode 100644 index 00000000..3be213c7 --- /dev/null +++ b/platform/drivers/baseband/radio_MD3x0.cpp @@ -0,0 +1,349 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * 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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HR_C5000.h" +#include "SKY72310.h" + +static const freq_t IF_FREQ = 49950000; // Intermediate frequency: 49.95MHz + +const md3x0Calib_t *calData; // Pointer to calibration data +const rtxStatus_t *config; // Pointer to data structure with radio configuration + +uint8_t vtune_rx = 0; // Tuning voltage for RX input filter +uint8_t txpwr_lo = 0; // APC voltage for TX output power control, low power +uint8_t txpwr_hi = 0; // APC voltage for TX output power control, high power + +enum opstatus radioStatus; // Current operating status + +HR_C5000& C5000 = HR_C5000::instance(); // HR_C5000 driver + +/* + * Parameters for RSSI voltage (mV) to input power (dBm) conversion. + * Gain is constant, while offset values are aligned to calibration frequency + * test points. + * Thanks to Wojciech SP5WWP for the measurements! + */ +const float rssi_gain = 22.0f; +const float rssi_offset[] = {3277.618f, 3654.755f, 3808.191f, + 3811.318f, 3804.936f, 3806.591f, + 3723.882f, 3621.373f, 3559.782f}; + + +void _setBandwidth(const enum bandwidth bw) +{ + switch(bw) + { + case BW_12_5: + gpio_clearPin(WN_SW); + C5000.setModFactor(0x1E); + break; + + case BW_20: + gpio_setPin(WN_SW); + C5000.setModFactor(0x30); + break; + + case BW_25: + gpio_setPin(WN_SW); + C5000.setModFactor(0x3C); + break; + + default: + break; + } +} + +void radio_init(const rtxStatus_t *rtxState) +{ + /* + * Load calibration data + */ + calData = reinterpret_cast< const md3x0Calib_t * >(platform_getCalibrationData()); + + config = rtxState; + radioStatus = OFF; + + /* + * Configure RTX GPIOs + */ + gpio_setMode(PLL_PWR, OUTPUT); + gpio_setMode(VCOVCC_SW, OUTPUT); + gpio_setMode(DMR_SW, OUTPUT); + gpio_setMode(WN_SW, OUTPUT); + gpio_setMode(FM_SW, OUTPUT); + gpio_setMode(RF_APC_SW, OUTPUT); + gpio_setMode(TX_STG_EN, OUTPUT); + gpio_setMode(RX_STG_EN, OUTPUT); + + gpio_setMode(FM_MUTE, OUTPUT); + gpio_clearPin(FM_MUTE); + + gpio_clearPin(PLL_PWR); // PLL off + gpio_setPin(VCOVCC_SW); // VCOVCC high enables RX VCO, TX VCO if low + gpio_setPin(WN_SW); // 25kHz bandwidth + gpio_clearPin(DMR_SW); // Disconnect HR_C5000 input IF signal and audio out + gpio_clearPin(FM_SW); // Disconnect analog FM audio path + gpio_clearPin(RF_APC_SW); // Disable TX power control + gpio_clearPin(TX_STG_EN); // Disable TX power stage + gpio_clearPin(RX_STG_EN); // Disable RX input stage + + /* + * Configure and enable DAC + */ + gpio_setMode(APC_TV, INPUT_ANALOG); + gpio_setMode(MOD2_BIAS, INPUT_ANALOG); + RCC->APB1ENR |= RCC_APB1ENR_DACEN; + DAC->CR = DAC_CR_EN2 | DAC_CR_EN1; + DAC->DHR12R2 = 0; + DAC->DHR12R1 = 0; + + /* + * Enable and configure PLL + */ + gpio_setPin(PLL_PWR); + SKY73210_init(); + + /* + * Configure HR_C5000 + */ + C5000.init(); + + /* + * Modulation bias settings, as per TYT firmware. + */ + DAC->DHR12R2 = (calData->freqAdjustMid)*4 + 0x600; + C5000.setModOffset(calData->freqAdjustMid); +} + +void radio_terminate() +{ + SKY73210_terminate(); + + gpio_clearPin(PLL_PWR); // PLL off + gpio_clearPin(DMR_SW); // Disconnect HR_C5000 input IF signal and audio out + gpio_clearPin(FM_SW); // Disconnect analog FM audio path + gpio_clearPin(RF_APC_SW); // Disable RF power control + gpio_clearPin(TX_STG_EN); // Disable TX power stage + gpio_clearPin(RX_STG_EN); // Disable RX input stage + + DAC->DHR12R2 = 0; + DAC->DHR12R1 = 0; + RCC->APB1ENR &= ~RCC_APB1ENR_DACEN; +} + +void radio_setOpmode(const enum opmode mode) +{ + switch(mode) + { + case FM: + gpio_clearPin(DMR_SW); // Disconnect analog paths for DMR + gpio_setPin(FM_SW); // Enable analog RX stage after superhet + C5000.fmMode(); // HR_C5000 in FM mode + C5000.setInputGain(0xC8); // Input gain, as per TYT firmware + break; + + case DMR: + gpio_clearPin(FM_SW); // Disable analog RX stage after superhet + gpio_setPin(DMR_SW); // Enable analog paths for DMR + //C5000_dmrMode(); + break; + + case M17: + gpio_clearPin(DMR_SW); // Disconnect analog paths for DMR + gpio_setPin(FM_SW); // Enable analog RX stage after superhet + C5000.fmMode(); // HR_C5000 in FM mode + C5000.setInputGain(0xA0); // Input gain, found experimentally + break; + + default: + break; + } +} + +bool radio_checkRxDigitalSquelch() +{ + return true; +} + +void radio_enableRx() +{ + gpio_clearPin(TX_STG_EN); // Disable TX PA + + gpio_clearPin(RF_APC_SW); // APC/TV used for RX filter tuning + gpio_setPin(VCOVCC_SW); // Enable RX VCO + + // Set PLL frequency and filter tuning voltage + SKY73210_setFrequency(config->rxFrequency - IF_FREQ, 5); + DAC->DHR12L1 = vtune_rx * 0xFF; + + gpio_setPin(RX_STG_EN); // Enable RX LNA + + if(config->opMode == FM) + { + gpio_setPin(FM_MUTE); // In FM mode, unmute audio path towards speaker + } + + radioStatus = RX; +} + +void radio_enableTx() +{ + if(config->txDisable == 1) return; + + gpio_clearPin(RX_STG_EN); // Disable RX LNA + + gpio_setPin(RF_APC_SW); // APC/TV in power control mode + gpio_clearPin(VCOVCC_SW); // Enable TX VCO + + // Set PLL frequency and TX output power + SKY73210_setFrequency(config->txFrequency, 5); + + // Constrain output power between 1W and 5W. + float power = std::max(std::min(config->txPower, 5.0f), 1.0f); + float pwrHi = static_cast< float >(txpwr_hi); + float pwrLo = static_cast< float >(txpwr_lo); + float apc = pwrLo + (pwrHi - pwrLo)/4.0f*(power - 1.0f); + DAC->DHR12L1 = static_cast< uint8_t >(apc) * 0xFF; + + switch(config->opMode) + { + case FM: + { + FmConfig cfg = (config->bandwidth == BW_12_5) ? FmConfig::BW_12p5kHz + : FmConfig::BW_25kHz; + C5000.startAnalogTx(TxAudioSource::MIC, cfg | FmConfig::PREEMPH_EN); + } + break; + + case M17: + C5000.startAnalogTx(TxAudioSource::LINE_IN, FmConfig::BW_25kHz); + break; + + default: + break; + } + + gpio_setPin(TX_STG_EN); // Enable TX PA + + if(config->txToneEn == 1) + { + toneGen_toneOn(); // Enable CTSS + } + + radioStatus = TX; +} + +void radio_disableRtx() +{ + // If we are currently transmitting, stop tone and C5000 TX + if(radioStatus == TX) + { + toneGen_toneOff(); + C5000.stopAnalogTx(); + } + + gpio_clearPin(TX_STG_EN); // Disable TX PA + gpio_clearPin(RX_STG_EN); // Disable RX LNA + gpio_clearPin(FM_MUTE); // Mute analog path towards the audio amplifier + + radioStatus = OFF; +} + +void radio_updateConfiguration() +{ + // Tuning voltage for RX input filter + vtune_rx = interpCalParameter(config->rxFrequency, calData->rxFreq, + calData->rxSensitivity, 9); + + // APC voltage for TX output power control + txpwr_lo = interpCalParameter(config->txFrequency, calData->txFreq, + calData->txLowPower, 9); + + txpwr_hi = interpCalParameter(config->txFrequency, calData->txFreq, + calData->txHighPower, 9); + + // HR_C5000 modulation amplitude + const uint8_t *Ical = calData->sendIrange; + const uint8_t *Qcal = calData->sendQrange; + + if(config->opMode == FM) + { + Ical = calData->analogSendIrange; + Qcal = calData->analogSendQrange; + } + + uint8_t I = interpCalParameter(config->txFrequency, calData->txFreq, Ical, 9); + uint8_t Q = interpCalParameter(config->txFrequency, calData->txFreq, Qcal, 9); + + C5000.setModAmplitude(I, Q); + + // Set bandwidth, force 12.5kHz for DMR mode + enum bandwidth bandwidth = static_cast< enum bandwidth >(config->bandwidth); + if(config->opMode == DMR) bandwidth = BW_12_5; + _setBandwidth(bandwidth); + + // Set CTCSS tone + float tone = static_cast< float >(config->txTone) / 10.0f; + toneGen_setToneFreq(tone); + + /* + * Update VCO frequency and tuning parameters if current operating status + * is different from OFF. + * This is done by calling again the corresponding functions, which is safe + * to do and avoids code duplication. + */ + if(radioStatus == RX) radio_enableRx(); + if(radioStatus == TX) radio_enableTx(); +} + +float radio_getRssi() +{ + /* + * On MD3x0 devices, RSSI value is get by reading the analog RSSI output + * from second IF stage (GT3136 IC). + * The corresponding power value is obtained through the linear correlation + * existing between measured voltage in mV and power in dBm. While gain is + * constant, offset depends from the rx frequency. + */ + + freq_t rxFreq = config->rxFrequency; + uint32_t offset_index = (rxFreq - 400035000)/10000000; + + if(rxFreq < 401035000) offset_index = 0; + if(rxFreq > 479995000) offset_index = 8; + + float rssi_mv = adc1_getMeasurement(ADC_RSSI_CH); + float rssi_dbm = (rssi_mv - rssi_offset[offset_index]) / rssi_gain; + return rssi_dbm; +} + +enum opstatus radio_getStatus() +{ + return radioStatus; +}