diff --git a/openrtx/include/rtx/OpMode.h b/openrtx/include/rtx/OpMode.h new file mode 100644 index 00000000..75bb58df --- /dev/null +++ b/openrtx/include/rtx/OpMode.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2021 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 * + ***************************************************************************/ + +#ifndef OPMODE_H +#define OPMODE_H + +#include "rtx.h" + +/** + * This class provides a standard interface for all the operating modes. + * The class is then specialised for each operating mode and its implementation + * groups all the common code required to manage the given mode, like data + * encoding and decoding, squelch management, ... + */ + +class OpMode +{ +public: + + /** + * Constructor. + */ + OpMode() { } + + /** + * Destructor. + */ + virtual ~OpMode() { } + + /** + * Enable the operating mode. + * + * Application must ensure this function is being called when entering the + * new operating mode and always before the first call of "update". + */ + virtual void enable() { } + + /** + * Disable the operating mode. This function ensures that, after being + * called, the radio, the audio amplifier and the microphone are in OFF state. + * + * Application must ensure this function is being called when exiting the + * current operating mode. + */ + virtual void disable() { } + + /** + * Update the internal FSM. + * Application code has to call this function periodically, to ensure proper + * functionality. + * + * @param status: pointer to the rtxStatus_t structure containing the current + * RTX status. Internal FSM may change the current value of the opStatus flag. + * @param newCfg: flag used inform the internal FSM that a new RTX configuration + * has been applied. + */ + virtual void update(rtxStatus_t *const status, const bool newCfg) + { + (void) status; + (void) newCfg; + } + + /** + * Get the mode identifier corresponding to the OpMode class. + * + * @return the corresponding flag from the opmode enum. + */ + virtual opmode getID() + { + return NONE; + } +}; + +#endif /* OPMODE_H */ diff --git a/openrtx/include/rtx/OpMode_FM.h b/openrtx/include/rtx/OpMode_FM.h new file mode 100644 index 00000000..b91ec5da --- /dev/null +++ b/openrtx/include/rtx/OpMode_FM.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2021 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 * + ***************************************************************************/ + +#ifndef OPMODE_FM_H +#define OPMODE_FM_H + +#include "OpMode.h" + +/** + * Specialisation of the OpMode class for the management of analog FM operating + * mode. + */ + +class OpMode_FM : public OpMode +{ +public: + + /** + * Constructor. + */ + OpMode_FM(); + + /** + * Destructor. + */ + ~OpMode_FM(); + + /** + * Enable the operating mode. + * + * Application must ensure this function is being called when entering the + * new operating mode and always before the first call of "update". + */ + virtual void enable() override; + + /** + * Disable the operating mode. This function ensures that, after being + * called, the radio, the audio amplifier and the microphone are in OFF state. + * + * Application must ensure this function is being called when exiting the + * current operating mode. + */ + virtual void disable() override; + + /** + * Update the internal FSM. + * Application code has to call this function periodically, to ensure proper + * functionality. + * + * @param status: pointer to the rtxStatus_t structure containing the current + * RTX status. Internal FSM may change the current value of the opStatus flag. + * @param newCfg: flag used inform the internal FSM that a new RTX configuration + * has been applied. + */ + virtual void update(rtxStatus_t *const status, const bool newCfg) override; + + /** + * Get the mode identifier corresponding to the OpMode class. + * + * @return the corresponding flag from the opmode enum. + */ + virtual opmode getID() override + { + return FM; + } + +private: + + bool sqlOpen; ///< Flag for current squelch status. + bool enterRx; ///< Flag for RX management. +}; + +#endif /* OPMODE_FM_H */ diff --git a/openrtx/include/rtx.h b/openrtx/include/rtx/rtx.h similarity index 95% rename from openrtx/include/rtx.h rename to openrtx/include/rtx/rtx.h index 88675c10..395feba1 100644 --- a/openrtx/include/rtx.h +++ b/openrtx/include/rtx/rtx.h @@ -63,8 +63,10 @@ enum bandwidth */ enum opmode { - FM = 0, /**< Analog FM */ - DMR = 1 /**< DMR */ + NONE = 0, /**< No opMode selected */ + FM = 1, /**< Analog FM */ + DMR = 2, /**< DMR */ + M17 = 3 /**< M17 */ }; /** diff --git a/openrtx/src/rtx.c b/openrtx/src/rtx.c deleted file mode 100644 index c36329ca..00000000 --- a/openrtx/src/rtx.c +++ /dev/null @@ -1,287 +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 -#ifdef PLATFORM_MDUV3x0 -#include "../../platform/drivers/baseband/HR_C6000.h" -#endif - -pthread_mutex_t *cfgMutex; /* Mutex for incoming config messages */ - -const rtxStatus_t *newCnf; /* Pointer for incoming config messages */ -rtxStatus_t rtxStatus; /* RTX driver status */ - -bool sqlOpen; /* Flag for squelch open/close */ -bool enterRx; /* Flag for RX mode activation */ - -float rssi; /* Current RSSI in dBm */ - -/* - * Unfortunately on MD-UV3x0 radios the volume knob does not regulate - * the amplitude of the analog signal towards the audio amplifier but it - * rather serves to provide a digital value to be fed into the HR_C6000 - * lineout DAC gain. We thus have to place the #ifdef'd piece of code - * below to keep the real volume level consistent with the knob position. - * Knob position is given by an analog signal in the range 0 - 1500mV, - * which has to be mapped in a range between 1 and 31. - */ -#ifdef PLATFORM_MDUV3x0 -void _setVolume() -{ - float level = (platform_getVolumeLevel() / 1560.0f) * 30.0f; - uint8_t volume = ((uint8_t) (level + 0.5f)); - - // Mute volume when knob is set below 10% - if(volume < 1) - audio_disableAmp(); - else - { - audio_enableAmp(); - /* Update HR_C6000 gain only if volume changed */ - static uint8_t old_volume = 0; - if(volume != old_volume) - { - // Setting HR_C6000 volume to 0 = max volume - C6000_setDacGain(volume); - old_volume = volume; - } - } -} -#endif - -void rtx_init(pthread_mutex_t *m) -{ - /* Initialise mutex for configuration access */ - cfgMutex = m; - newCnf = NULL; - - /* - * Default initialisation for rtx status - */ - rtxStatus.opMode = FM; - rtxStatus.bandwidth = BW_25; - rtxStatus.txDisable = 0; - rtxStatus.opStatus = OFF; - rtxStatus.rxFrequency = 430000000; - rtxStatus.txFrequency = 430000000; - rtxStatus.txPower = 0.0f; - rtxStatus.sqlLevel = 1; - rtxStatus.rxToneEn = 0; - rtxStatus.rxTone = 0; - rtxStatus.txToneEn = 0; - rtxStatus.txTone = 0; - - sqlOpen = false; - enterRx = false; - - /* - * Initialise low-level platform-specific driver - */ - radio_init(); - - /* - * Initial value for RSSI filter - */ - rssi = radio_getRssi(rtxStatus.rxFrequency); -} - -void rtx_terminate() -{ - radio_terminate(); -} - -void rtx_configure(const rtxStatus_t *cfg) -{ - /* - * NOTE: an incoming configuration may overwrite a preceding one not yet - * read by the radio task. This mechanism ensures that the radio driver - * always gets the most recent configuration. - */ - - pthread_mutex_lock(cfgMutex); - newCnf = cfg; - pthread_mutex_unlock(cfgMutex); -} - -rtxStatus_t rtx_getCurrentStatus() -{ - return rtxStatus; -} - -void rtx_taskFunc() -{ - /* Check if there is a pending new configuration and, in case, read it. */ - bool reconfigure = false; - if(pthread_mutex_trylock(cfgMutex) == 0) - { - if(newCnf != NULL) - { - /* Copy new configuration and override opStatus flags */ - uint8_t tmp = rtxStatus.opStatus; - memcpy(&rtxStatus, newCnf, sizeof(rtxStatus_t)); - rtxStatus.opStatus = tmp; - - reconfigure = true; - newCnf = NULL; - } - - pthread_mutex_unlock(cfgMutex); - } - - if(reconfigure) - { - /* Update HW configuration */ - radio_setOpmode(rtxStatus.opMode); - radio_setBandwidth(rtxStatus.bandwidth); - radio_setCSS(rtxStatus.rxTone, rtxStatus.txTone); - radio_updateCalibrationParams(&rtxStatus); - - /* - * If currently transmitting or receiving, update VCO frequency and - * call again enableRx/enableTx. - * This is done because the new configuration may have changed the - * RX and TX frequencies, requiring an update of both the VCO - * settings and of some tuning parameters, like APC voltage, which - * are managed by enableRx/enableTx. - */ - if(rtxStatus.opStatus == TX) - { - radio_setVcoFrequency(rtxStatus.txFrequency, true); - radio_enableTx(rtxStatus.txPower, rtxStatus.txToneEn); - } - - if(rtxStatus.opStatus == RX) - { - radio_setVcoFrequency(rtxStatus.rxFrequency, false); - radio_enableRx(); - } - - /* TODO: temporarily force to RX mode if rtx is off. */ - if(rtxStatus.opStatus == OFF) enterRx = true; - } - - /* RX logic */ - if(rtxStatus.opStatus == RX) - { - /* - * RSSI-based squelch mechanism, with 15 levels from -140dBm to -70dBm. - * - * RSSI value is passed through a filter with a time constant of 60ms - * (cut-off frequency of 15Hz) at an update rate of 33.3Hz. - * - * The low pass filter skips an update step if a new configuration has - * just been applied. This is a workaround for the AT1846S returning a - * full-scale RSSI value immediately after one of its parameters changed, - * thus causing the squelch to open briefly. - */ - if(!reconfigure) - { - rssi = 0.74*radio_getRssi(rtxStatus.rxFrequency) + 0.26*rssi; - } - - float squelch = -127.0f + rtxStatus.sqlLevel * 66.0f / 15.0f; - - if((sqlOpen == false) && (rssi > (squelch + 0.1f))) - { - audio_enableAmp(); - sqlOpen = true; - } - - if((sqlOpen == true) && (rssi < (squelch - 0.1f))) - { - audio_disableAmp(); - sqlOpen = false; - } - - #ifdef PLATFORM_MDUV3x0 - if(sqlOpen == true) - { - // Set output volume by changing the HR_C6000 DAC gain - _setVolume(); - } - #endif - } - else if((rtxStatus.opMode == OFF) && enterRx) - { - radio_disableRtx(); - - radio_setVcoFrequency(rtxStatus.rxFrequency, false); - radio_enableRx(); - rtxStatus.opStatus = RX; - enterRx = false; - - /* Reinitialise RSSI filter state */ - rssi = radio_getRssi(rtxStatus.rxFrequency); - } - - /* TX logic */ - if(platform_getPttStatus() && (rtxStatus.opStatus != TX)) - { - audio_disableAmp(); - radio_disableRtx(); - - audio_enableMic(); - radio_setVcoFrequency(rtxStatus.txFrequency, true); - radio_enableTx(rtxStatus.txPower, rtxStatus.txToneEn); - - rtxStatus.opStatus = TX; - } - - if(!platform_getPttStatus() && (rtxStatus.opStatus == TX)) - { - audio_disableMic(); - radio_disableRtx(); - - rtxStatus.opStatus = OFF; - enterRx = true; - } - - /* Led control logic */ - switch(rtxStatus.opStatus) - { - case RX: - if(sqlOpen) - platform_ledOn(GREEN); - else - platform_ledOff(GREEN); - - break; - - case TX: - platform_ledOff(GREEN); - platform_ledOn(RED); - break; - - default: - platform_ledOff(GREEN); - platform_ledOff(RED); - break; - } -} - -float rtx_getRssi() -{ - return rssi; -} diff --git a/openrtx/src/rtx/OpMode_FM.cpp b/openrtx/src/rtx/OpMode_FM.cpp new file mode 100644 index 00000000..22c0b1a8 --- /dev/null +++ b/openrtx/src/rtx/OpMode_FM.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** + * Copyright (C) 2021 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 + +/** + * \internal + * On MD-UV3x0 radios the volume knob does not regulate the amplitude of the + * analog signal towards the audio amplifier but it rather serves to provide a + * digital value to be fed into the HR_C6000 lineout DAC gain. We thus have to + * provide the helper function below to keep the real volume level consistent + * with the knob position. + * Current knob position corresponds to an analog signal in the range 0 - 1500mV, + * which has to be mapped in a range between 1 and 31. + */ +#ifdef PLATFORM_MDUV3x0 +void _setVolume() +{ + float level = (platform_getVolumeLevel() / 1560.0f) * 30.0f; + uint8_t volume = ((uint8_t) (level + 0.5f)); + + // Mute volume when knob is set below 10% + if(volume < 1) + { + audio_disableAmp(); + } + else + { + audio_enableAmp(); + + // Update HR_C6000 gain only if volume changed + static uint8_t old_volume = 0; + if(volume != old_volume) + { + // Setting HR_C6000 volume to 0 = max volume + C6000_setDacGain(volume); + old_volume = volume; + } + } +} +#endif + +OpMode_FM::OpMode_FM() : sqlOpen(false), enterRx(true) +{ +} + +OpMode_FM::~OpMode_FM() +{ +} + +void OpMode_FM::enable() +{ + // When starting, close squelch and prepare for entering in RX mode. + sqlOpen = false; + enterRx = true; +} + +void OpMode_FM::disable() +{ + // Clean shutdown. + audio_disableAmp(); + audio_disableMic(); + radio_disableRtx(); + sqlOpen = false; + enterRx = false; +} + +void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg) +{ + // RX logic + if(status->opStatus == RX) + { + float squelch = -127.0f + status->sqlLevel * 66.0f / 15.0f; + float rssi = rtx_getRssi(); + + if((sqlOpen == false) && (rssi > (squelch + 0.1f))) + { + audio_enableAmp(); + sqlOpen = true; + } + + if((sqlOpen == true) && (rssi < (squelch - 0.1f))) + { + audio_disableAmp(); + sqlOpen = false; + } + + #ifdef PLATFORM_MDUV3x0 + if(sqlOpen == true) + { + // Set output volume by changing the HR_C6000 DAC gain + _setVolume(); + } + #endif + } + else if((status->opMode == OFF) && enterRx) + { + radio_disableRtx(); + + radio_setVcoFrequency(status->rxFrequency, false); + radio_enableRx(); + status->opStatus = RX; + enterRx = false; + } + + /* TX logic */ + if(platform_getPttStatus() && (status->opStatus != TX) && + (status->txDisable == 0)) + { + audio_disableAmp(); + radio_disableRtx(); + + audio_enableMic(); + radio_setVcoFrequency(status->txFrequency, true); + radio_enableTx(status->txPower, status->txToneEn); + + status->opStatus = TX; + } + + if(!platform_getPttStatus() && (status->opStatus == TX)) + { + audio_disableMic(); + radio_disableRtx(); + + status->opStatus = OFF; + enterRx = true; + } + + /* Led control logic */ + switch(status->opStatus) + { + case RX: + if(sqlOpen) + { + platform_ledOn(GREEN); + } + else + { + platform_ledOff(GREEN); + } + + break; + + case TX: + platform_ledOff(GREEN); + platform_ledOn(RED); + break; + + default: + platform_ledOff(GREEN); + platform_ledOff(RED); + break; + } +} diff --git a/openrtx/src/rtx/rtx.cpp b/openrtx/src/rtx/rtx.cpp new file mode 100644 index 00000000..baca4fb7 --- /dev/null +++ b/openrtx/src/rtx/rtx.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** + * 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 + +pthread_mutex_t *cfgMutex; // Mutex for incoming config messages + +const rtxStatus_t *newCnf; // Pointer for incoming config messages +rtxStatus_t rtxStatus; // RTX driver status + +float rssi; // Current RSSI in dBm +bool reinitFilter; // Flag for RSSI filter re-initialisation + +OpMode *currMode; // Pointer to currently active opMode handler +OpMode noMode; // Empty opMode handler for opmode::NONE +OpMode_FM fmMode; // FM mode handler + +void rtx_init(pthread_mutex_t *m) +{ + // Initialise mutex for configuration access + cfgMutex = m; + newCnf = NULL; + + /* + * Default initialisation for rtx status + */ + rtxStatus.opMode = NONE; + rtxStatus.bandwidth = BW_25; + rtxStatus.txDisable = 0; + rtxStatus.opStatus = OFF; + rtxStatus.rxFrequency = 430000000; + rtxStatus.txFrequency = 430000000; + rtxStatus.txPower = 0.0f; + rtxStatus.sqlLevel = 1; + rtxStatus.rxToneEn = 0; + rtxStatus.rxTone = 0; + rtxStatus.txToneEn = 0; + rtxStatus.txTone = 0; + currMode = &noMode; + + /* + * Initialise low-level platform-specific driver + */ + radio_init(); + + /* + * Initial value for RSSI filter + */ + rssi = radio_getRssi(rtxStatus.rxFrequency); + reinitFilter = false; +} + +void rtx_terminate() +{ + rtxStatus.opStatus = OFF; + rtxStatus.opMode = NONE; + currMode->disable(); + radio_terminate(); +} + +void rtx_configure(const rtxStatus_t *cfg) +{ + /* + * NOTE: an incoming configuration may overwrite a preceding one not yet + * read by the radio task. This mechanism ensures that the radio driver + * always gets the most recent configuration. + */ + + pthread_mutex_lock(cfgMutex); + newCnf = cfg; + pthread_mutex_unlock(cfgMutex); +} + +rtxStatus_t rtx_getCurrentStatus() +{ + return rtxStatus; +} + +void rtx_taskFunc() +{ + // Check if there is a pending new configuration and, in case, read it. + bool reconfigure = false; + if(pthread_mutex_trylock(cfgMutex) == 0) + { + if(newCnf != NULL) + { + // Copy new configuration and override opStatus flags + uint8_t tmp = rtxStatus.opStatus; + memcpy(&rtxStatus, newCnf, sizeof(rtxStatus_t)); + rtxStatus.opStatus = tmp; + + reconfigure = true; + newCnf = NULL; + } + + pthread_mutex_unlock(cfgMutex); + } + + if(reconfigure) + { + /* + * Handle change of opMode: + * - deactivate current opMode and switch operating status to "OFF"; + * - update pointer to current mode handler to the OpMode object for the + * selected mode; + * - enable the new mode handler + */ + if(currMode->getID() != rtxStatus.opMode) + { + currMode->disable(); + rtxStatus.opStatus = OFF; + + switch(rtxStatus.opMode) + { + case NONE: currMode = &noMode; break; + case FM: currMode = &fmMode; break; + default: currMode = &noMode; + } + + currMode->enable(); + } + } + + /* + * RSSI update block, run only when radio is in RX mode. + * + * RSSI value is passed through a filter with a time constant of 60ms + * (cut-off frequency of 15Hz) at an update rate of 33.3Hz. + * + * The low pass filter skips an update step if a new configuration has + * just been applied. This is a workaround for the AT1846S returning a + * full-scale RSSI value immediately after one of its parameters changed, + * thus causing the squelch to open briefly. + * + * Also, the RSSI filter is re-initialised every time radio stage is + * switched back from TX/OFF to RX. This provides a workaround for some + * radios reporting a full-scale RSSI value when transmitting. + */ + if(rtxStatus.opStatus == RX) + { + + if(!reconfigure) + { + if(!reinitFilter) + { + rssi = 0.74*radio_getRssi(rtxStatus.rxFrequency) + 0.26*rssi; + } + else + { + rssi = radio_getRssi(rtxStatus.rxFrequency); + reinitFilter = false; + } + } + } + else + { + // Reinit required if current operating status is TX or OFF + reinitFilter = true; + } + + /* + * Forward the periodic update step to the currently active opMode handler. + * Call is placed after RSSI update to allow handler's code have a fresh + * version of the RSSI level. + */ + currMode->update(&rtxStatus, reconfigure); +} + +float rtx_getRssi() +{ + return rssi; +}