/*
 * Copyright (C) 2019      Kai Ludwig, DG4KLU
 * Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
 *                         Colin, G4EML
 *                         Daniel Caujolle-Bert, F1RMB
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
 *    in the documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
 *    incorporation in a commercial product or incorporation into a product or project which allows commercial use.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include "functions/calibration.h"
#include "functions/ticks.h"
#include "hardware/HR-C6000.h"
#include "hardware/AT1846S.h"
#include "functions/settings.h"
#include "functions/trx.h"
#include "functions/rxPowerSaving.h"
#include "functions/aprs.h"
#include "user_interface/menuSystem.h"
#include "user_interface/uiUtilities.h"
#include <FreeRTOS.h>
#include <string.h>


#if USE_DATASHEET_RANGES
const frequencyHardwareBand_t RADIO_HARDWARE_FREQUENCY_BANDS[RADIO_BANDS_TOTAL_NUM] =  {
													{
														.minFreq=13600000,
														.maxFreq=17400000
													},// VHF
#if !(defined(PLATFORM_MD9600) || defined(PLATORM_MD380)
													{
														.minFreq=20000000,
														.maxFreq=26000000
													},// 220Mhz
#endif
													{
														.minFreq=40000000,
														.maxFreq=52000000
													}// UHF
};
#else
const frequencyHardwareBand_t RADIO_HARDWARE_FREQUENCY_BANDS[RADIO_BANDS_TOTAL_NUM] =  {
													{
														.calIQTableMinFreq = 13600000,
														.calPowerTableMinFreq = 13500000,
														.minFreq=12700000,
														.maxFreq=17800000
													},// VHF
#if !(defined(PLATFORM_MD9600) || defined(PLATFORM_MD380))
													{
														.calIQTableMinFreq = 13600000,
														.calPowerTableMinFreq = 13500000,
														.minFreq=19000000,
														.maxFreq=28200000
													},// 220Mhz
#endif
													{
														.calIQTableMinFreq = 40000000,
														.calPowerTableMinFreq = 40000000,
														.minFreq=38000000,
														.maxFreq=56400000
													}// UHF
};
#endif

#define TRX_SQUELCH_MAX    70
#define TRX_SQUELCH_HIST  3
#define TRX_SQUELCH_INC   3
const uint8_t TRX_NUM_CTCSS = 50U;

const uint16_t TRX_CTCSSTones[] = {
		670,  693,  719,  744,  770,  797,  825,  854,  885,  915,
		948,  974, 1000, 1035, 1072, 1109, 1148, 1188, 1230, 1273,
		1318, 1365, 1413, 1462, 1514, 1567, 1598, 1622, 1655, 1679,
		1713, 1738, 1773, 1799, 1835, 1862, 1899, 1928, 1966, 1995,
		2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541
};

const uint16_t TRX_DCS_TONE = 13440;  // 134.4Hz is the data rate of the DCS bitstream (and a reason not to use that tone for CTCSS)
const uint8_t TRX_NUM_DCS = 83U;

const uint16_t TRX_DCSCodes[] = {
		0x023, 0x025, 0x026, 0x031, 0x032, 0x043, 0x047, 0x051, 0x054, 0x065, 0x071, 0x072, 0x073, 0x074,
		0x114, 0x115, 0x116, 0x125, 0x131, 0x132, 0x134, 0x143, 0x152, 0x155, 0x156, 0x162, 0x165, 0x172, 0x174,
		0x205, 0x223, 0x226, 0x243, 0x244, 0x245, 0x251, 0x261, 0x263, 0x265, 0x271,
		0x306, 0x311, 0x315, 0x331, 0x343, 0x345, 0x351, 0x364, 0x365, 0x371,
		0x411, 0x412, 0x413, 0x423, 0x431, 0x432, 0x445, 0x464, 0x465, 0x466,
		0x503, 0x506, 0x516, 0x532, 0x546, 0x565,
		0x606, 0x612, 0x624, 0x627, 0x631, 0x632, 0x654, 0x662, 0x664,
		0x703, 0x712, 0x723, 0x731, 0x732, 0x734, 0x743, 0x754
};


frequencyBand_t USER_FREQUENCY_BANDS[RADIO_BANDS_TOTAL_NUM] =  {
													{
														.minFreq=14400000,
														.maxFreq=14800000
													},// VHF
													{
														.minFreq=22200000,
														.maxFreq=22500000
													},// 220Mhz
													{
														.minFreq=42000000,
														.maxFreq=45000000
													}// UHF
};

const frequencyBand_t DEFAULT_USER_FREQUENCY_BANDS[RADIO_BANDS_TOTAL_NUM] =  {
													{
														.minFreq=14400000,
														.maxFreq=14800000
													},// VHF
													{
														.minFreq=22200000,
														.maxFreq=22500000
													},// 220Mhz
													{
														.minFreq=42000000,
														.maxFreq=45000000
													}// UHF
};

//const uint32_t RSSI_NOISE_SAMPLE_PERIOD_PIT = 25U;// 25 milliseconds

static volatile ticksTimer_t trxNextRssiNoiseSampleTimer = { 0, 0 };
static volatile ticksTimer_t trxNextSquelchCheckingTimer = { 0, 0 };

static uint8_t trxCssMeasureCount = 0;

static uint8_t currentCC = 1;

#define CTCSS_HOLD_DELAY            6
#define SQUELCH_CLOSE_DELAY         1

#define SIZE_OF_FILL_BUFFER       128 // Tested by Jose EA5SW, and it's needed, 64 makes the beeps and audio to disappear.

static bool rxCSSactive = false;
//static uint8_t rxCSSTriggerCount = 0;
static int trxCurrentDMRTimeSlot;

volatile uint8_t trxTxVox;
volatile uint8_t trxTxMic;

volatile uint16_t txDACDrivePower;
volatile uint8_t analogIGain;
volatile uint8_t analogQGain;
volatile uint8_t digitalIGain;
volatile uint8_t digitalQGain;
volatile int8_t Mod2Offset;

volatile bool trxIsTransmittingDMR;
volatile uint32_t trxDMRstartTime;

static uint8_t voice_gain_tx = 0x31; // default voice_gain_tx fro calibration, needs to be declared here in case calibration:OFF

volatile bool trxTransmissionEnabled = false;
volatile bool trxIsTransmitting = false;
volatile bool txPAEnabled = false;

uint32_t trxTalkGroupOrPcId = 9;// Set to local TG just in case there is some problem with it not being loaded
uint32_t trxDMRID = 0;// Set ID to 0. Not sure if its valid. This value needs to be loaded from the codeplug.


// DTMF Order: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, *, #
const int trxDTMFfreq1[] = { 1336, 1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1477, 1633, 1633, 1633, 1633, 1209, 1477 };
const int trxDTMFfreq2[] = {  941,  697,  697,  697,  770,  770,  770,  852,  852,  852,  697,  770,  852,  941,  941,  941 };

calibrationPowerValues_t trxPowerSettings;

static bool powerUpDownState = true;

static uint8_t trxAnalogFilterLevel = ANALOG_FILTER_CSS;

volatile bool trxDMRSynchronisedRSSIReadPending = false;

static uint8_t trxSaveVoiceGainTx = 0xff;
static uint16_t trxSaveDeviation = 0xff;


static void trxUpdateC6000Calibration(void);
static void trxUpdateRadioCalibration(void);

//
// =================================================================
//

uint8_t trxGetAnalogFilterLevel(void)
{
	return trxAnalogFilterLevel;
}

void trxSetAnalogFilterLevel(uint8_t newFilterLevel)
{
	trxAnalogFilterLevel = newFilterLevel;
}

int trxGetMode(void)
{
	return currentRadioDevice->currentMode;
}

bool trxGetBandwidthIs25kHz(void)
{
	return currentRadioDevice->currentBandWidthIs25kHz;
}

void trxSetModeAndBandwidth(int mode, bool bandwidthIs25kHz)
{
	if (rxPowerSavingIsRxOn() == false)
	{
		rxPowerSavingSetState(ECOPHASE_POWERSAVE_INACTIVE);
	}

	currentRadioDevice->digitalSignalReceived = false;
	currentRadioDevice->analogSignalReceived = false;
	ticksTimerStart((ticksTimer_t *)&trxNextRssiNoiseSampleTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	ticksTimerStart((ticksTimer_t *)&trxNextSquelchCheckingTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	trxCssMeasureCount = 0;

	// DMR (digital) is disabled, hence force it
	// to RADIO_MODE_ANALOG (has we could currently be in RADIO_MODE_NONE (CPS))
	if (uiDataGlobal.dmrDisabled && (mode == RADIO_MODE_DIGITAL))
	{
		mode = RADIO_MODE_ANALOG;
	}

	if ((mode != currentRadioDevice->currentMode) || (bandwidthIs25kHz != currentRadioDevice->currentBandWidthIs25kHz))
	{
		currentRadioDevice->currentMode = mode;

		taskENTER_CRITICAL();
		switch(mode)
		{
			case RADIO_MODE_NONE:// not truly off
				soundTerminateSound();
				HRC6000TerminateDigital();
				radioSetMode(RADIO_MODE_NONE);
				trxUpdateC6000Calibration();
				trxUpdateRadioCalibration();
				break;
			case RADIO_MODE_ANALOG:
				currentRadioDevice->currentBandWidthIs25kHz = bandwidthIs25kHz;
				//radioSetAudioPath(true);							//select the FM audio Path
				HRC6000TerminateDigital();
				radioSetMode(RADIO_MODE_ANALOG);
				trxUpdateC6000Calibration();
				radioSetIF(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND], currentRadioDevice->currentBandWidthIs25kHz);
				trxUpdateRadioCalibration();
				break;
			case RADIO_MODE_DIGITAL:
				currentRadioDevice->currentBandWidthIs25kHz = BANDWIDTH_12P5KHZ;// DMR bandwidth is 12.5kHz
				radioSetMode(RADIO_MODE_DIGITAL);
				trxUpdateC6000Calibration();
				radioSetIF(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND], currentRadioDevice->currentBandWidthIs25kHz);
				trxUpdateRadioCalibration();
				HRC6000InitDigital();
				break;
		}
		taskEXIT_CRITICAL();
	}
	else
	{
		switch (mode)
		{
			case RADIO_MODE_ANALOG:
				audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
				break;

			case RADIO_MODE_DIGITAL:
				HRC6000ResetTimeSlotDetection();
				// We need to reset the slot state because some part of the UI
				// are getting stuck, like the green LED and QSO info, while navigating the UI.
				if (slotState != DMR_STATE_IDLE)
				{
					slotState = DMR_STATE_RX_END;
				}
				break;

			case RADIO_MODE_NONE:
				// nop
				break;
		}
	}
}

uint32_t trxGetNextOrPrevBandFromFrequency(uint32_t frequency, bool nextBand)
{
	if (nextBand)
	{
		if (frequency > RADIO_HARDWARE_FREQUENCY_BANDS[RADIO_BANDS_TOTAL_NUM - 1].maxFreq)
		{
			return 0; // First band
		}

		for(uint32_t band = 0; band < RADIO_BANDS_TOTAL_NUM - 1; band++)
		{
			if (frequency > RADIO_HARDWARE_FREQUENCY_BANDS[band].maxFreq && frequency < RADIO_HARDWARE_FREQUENCY_BANDS[band + 1].minFreq)
			{
				return (band + 1); // Next band
			}
		}
	}
	else
	{
		if (frequency < RADIO_HARDWARE_FREQUENCY_BANDS[0].minFreq)
		{
			return (RADIO_BANDS_TOTAL_NUM - 1); // Last band
		}

		for (uint32_t band = 1; band < RADIO_BANDS_TOTAL_NUM; band++)
		{
			if (frequency < RADIO_HARDWARE_FREQUENCY_BANDS[band].minFreq && frequency > RADIO_HARDWARE_FREQUENCY_BANDS[band - 1].maxFreq)
			{
				return (band - 1); // Prev band
			}
		}
	}

	return FREQUENCY_OUT_OF_BAND;
}

uint32_t trxGetBandFromFrequency(uint32_t frequency)
{
	for (uint32_t i = 0; i < RADIO_BANDS_TOTAL_NUM; i++)
	{
		if ((frequency >= RADIO_HARDWARE_FREQUENCY_BANDS[i].minFreq) && (frequency <= RADIO_HARDWARE_FREQUENCY_BANDS[i].maxFreq))
		{
			return i;
		}
	}

	return FREQUENCY_OUT_OF_BAND;
}

bool trxCheckFrequencyInAmateurBand(uint32_t frequency)
{
	if (nonVolatileSettings.txFreqLimited == BAND_LIMITS_FROM_CPS)
	{
		return ((frequency >= USER_FREQUENCY_BANDS[RADIO_BAND_VHF].minFreq) && (frequency <= USER_FREQUENCY_BANDS[RADIO_BAND_VHF].maxFreq)) ||
			((frequency >= USER_FREQUENCY_BANDS[RADIO_BAND_UHF].minFreq) && (frequency <= USER_FREQUENCY_BANDS[RADIO_BAND_UHF].maxFreq));
	}
	else if (nonVolatileSettings.txFreqLimited == BAND_LIMITS_ON_LEGACY_DEFAULT)
	{
		return ((frequency >= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_VHF].minFreq) && (frequency <= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_VHF].maxFreq)) ||
#if !(defined(PLATFORM_MD9600) || defined(PLATFORM_MD380))
			((frequency >= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_220MHz].minFreq) && (frequency <= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_220MHz].maxFreq)) ||
#endif
			((frequency >= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_UHF].minFreq) && (frequency <= DEFAULT_USER_FREQUENCY_BANDS[RADIO_BAND_UHF].maxFreq));
	}

	return true;// Setting must be BAND_LIMITS_NONE
}

void trxReadVoxAndMicStrength(void)
{
	radioReadVoxAndMicStrength();
}

// Need to postpone the next AT1846ReadRSSIAndNoise() call (see trxReadRSSIAndNoise())
// msOverride parameter is used if > 0
void trxPostponeReadRSSIAndNoise(uint32_t msOverride)
{
	ticksTimerStart((ticksTimer_t *)&trxNextRssiNoiseSampleTimer, (msOverride > 0 ? msOverride : RSSI_NOISE_SAMPLE_PERIOD_PIT));
}

// Check RSSI and Noise
void trxReadRSSIAndNoise(bool force)
{
	if (rxPowerSavingIsRxOn() && (ticksTimerHasExpired((ticksTimer_t *)&trxNextRssiNoiseSampleTimer) || force))
	{
		radioReadRSSIAndNoiseForBand(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]);
		ticksTimerStart((ticksTimer_t *)&trxNextRssiNoiseSampleTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	}
}

bool trxCarrierDetected(RadioDevice_t deviceId)
{
	TRXDevice_t *radioDevice = &radioDevices[deviceId];// Get pointer to device to make code below more efficient
	uint8_t squelch = 0;

	trxReadRSSIAndNoise(true); // We need to get the RSSI and noise now.

	switch(radioDevice->currentMode)
	{
		case RADIO_MODE_NONE:
			return false;
			break;

		case RADIO_MODE_ANALOG:
			if (currentChannelData->sql != 0)
			{
				squelch = TRX_SQUELCH_MAX - ((currentChannelData->sql - 1) * TRX_SQUELCH_INC);
			}
			else
			{
				squelch = TRX_SQUELCH_MAX - ((nonVolatileSettings.squelchDefaults[radioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]] - 1) * TRX_SQUELCH_INC);
			}
			break;

		case RADIO_MODE_DIGITAL:
			squelch = TRX_SQUELCH_MAX - ((nonVolatileSettings.squelchDefaults[radioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]] - 1) * TRX_SQUELCH_INC);
			break;
	}
	return (radioDevice->trxRxNoise < squelch);
}

bool trxCheckDigitalSquelch(RadioDevice_t deviceId)
{
	TRXDevice_t *radioDevice = &radioDevices[deviceId];// Get pointer to device to make code below more efficient

	if (ticksTimerHasExpired((ticksTimer_t *)&trxNextSquelchCheckingTimer))
	{
		if (radioDevice->currentMode != RADIO_MODE_NONE)
		{
			uint8_t squelch;

			squelch = TRX_SQUELCH_MAX - ((nonVolatileSettings.squelchDefaults[radioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]] - 1) * TRX_SQUELCH_INC);

			if (radioDevice->trxRxNoise < squelch)
			{
				if ((uiDataGlobal.rxBeepState & RX_BEEP_CARRIER_HAS_STARTED) == 0)
				{
					uiDataGlobal.rxBeepState |= (RX_BEEP_CARRIER_HAS_STARTED | RX_BEEP_CARRIER_HAS_STARTED_EXEC);
				}

				if(!radioDevice->digitalSignalReceived)
				{
					radioDevice->digitalSignalReceived = true;
					LedWrite(LED_GREEN, 1);
				}
			}
			else
			{
				if (radioDevice->digitalSignalReceived)
				{
					radioDevice->digitalSignalReceived = false;
					LedWrite(LED_GREEN, 0);
				}

				if (uiDataGlobal.rxBeepState & RX_BEEP_CARRIER_HAS_STARTED)
				{
					uiDataGlobal.rxBeepState = RX_BEEP_CARRIER_HAS_ENDED;
				}
			}
		}

		ticksTimerStart((ticksTimer_t *)&trxNextSquelchCheckingTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	}
	return radioDevice->digitalSignalReceived;
}

void trxTerminateCheckAnalogSquelch(RadioDevice_t deviceId)
{
	audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
	radioDevices[deviceId].analogSignalReceived = false;
	radioDevices[deviceId].analogTriggeredAudio = false;
	trxCssMeasureCount = 0;
}

bool trxCheckAnalogSquelch(void)
{
	if (trxIsTransmitting)
	{
		if (aprsTxProgress != APRS_TX_IN_PROGRESS)
		{
			audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
		}
		currentRadioDevice->analogSignalReceived = false;
		currentRadioDevice->analogTriggeredAudio = false;
		return false;
	}

	if (uiVFOModeSweepScanning(false) || (currentRadioDevice->currentMode == RADIO_MODE_NONE))
	{
		return false;
	}

	trxReadRSSIAndNoise(0);

	if (ticksTimerHasExpired((ticksTimer_t *)&trxNextSquelchCheckingTimer))
	{
		uint8_t squelch;

		// check for variable squelch control
		if (currentChannelData->sql != 0)
		{
			squelch = TRX_SQUELCH_MAX - ((currentChannelData->sql - 1) * TRX_SQUELCH_INC);
		}
		else
		{
			squelch = TRX_SQUELCH_MAX - ((nonVolatileSettings.squelchDefaults[currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]] - 1) * TRX_SQUELCH_INC);
		}

		if (currentRadioDevice->trxRxNoise < squelch) //noise less than squelch level = signal present.
		{
			if ((currentRadioDevice->analogSignalReceived == false) || (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF) == 0)            // open squelch if this is the first occurrence or if the audio amp was turned off by something else.
			{
				currentRadioDevice->analogSignalReceived = true;
				LedWrite(LED_GREEN, 1);

				// FM: Replace Carrier beeps with Talker beeps if Caller beep option is selected.
				if (((nonVolatileSettings.beepOptions & BEEP_RX_CARRIER) == 0) && (nonVolatileSettings.beepOptions & BEEP_RX_TALKER))
				{
					if ((uiDataGlobal.rxBeepState & RX_BEEP_TALKER_HAS_STARTED) == 0)
					{
						uiDataGlobal.rxBeepState |= (RX_BEEP_TALKER_HAS_STARTED | RX_BEEP_TALKER_HAS_STARTED_EXEC);
					}
				}
				else
				{
					if ((uiDataGlobal.rxBeepState & RX_BEEP_CARRIER_HAS_STARTED) == 0)
					{
						uiDataGlobal.rxBeepState |= (RX_BEEP_CARRIER_HAS_STARTED | RX_BEEP_CARRIER_HAS_STARTED_EXEC);
					}
				}

				currentRadioDevice->analogTriggeredAudio = true;
				trxCssMeasureCount = 0;
			}
		}
		if (currentRadioDevice->trxRxNoise > squelch + TRX_SQUELCH_HIST)							//add hysteresis to delay squelch closing. This prevents squelch chattering.
		{
			if (currentRadioDevice->analogSignalReceived || LedRead(LED_GREEN))
			{
				currentRadioDevice->analogSignalReceived = false;
				LedWrite(LED_GREEN, 0);

				// FM: Replace Carrier beeps with Talker beeps if Caller beep option is selected.
				if (((nonVolatileSettings.beepOptions & BEEP_RX_CARRIER) == 0) && (nonVolatileSettings.beepOptions & BEEP_RX_TALKER))
				{
					if (uiDataGlobal.rxBeepState & RX_BEEP_TALKER_HAS_STARTED)
					{
						uiDataGlobal.rxBeepState |= (RX_BEEP_TALKER_HAS_ENDED | RX_BEEP_TALKER_HAS_ENDED_EXEC);
					}
				}
				else
				{
					if (uiDataGlobal.rxBeepState & RX_BEEP_CARRIER_HAS_STARTED)
					{
						uiDataGlobal.rxBeepState = RX_BEEP_CARRIER_HAS_ENDED;
					}
				}

				currentRadioDevice->analogTriggeredAudio = false;
				trxCssMeasureCount = 0;
			}
		}

		bool cssFlag = (rxCSSactive ? trxCheckCSSFlag(currentChannelData->rxTone) : false);

		if (currentRadioDevice->analogSignalReceived)
		{
			if (((audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF) == 0) && ((rxCSSactive == false) || cssFlag))
			{
				if (currentRadioDevice->analogTriggeredAudio) // Execute that block of code just once after valid signal is received.
				{
					taskENTER_CRITICAL();
					if (!voicePromptsIsPlaying())
					{
						radioSetAudioPath(true);						//Select the FM Audio Path
						audioAmpEnable(AUDIO_AMP_CHANNEL_RF);
						displayLightTrigger(false);
						currentRadioDevice->analogTriggeredAudio = false;
						trxCssMeasureCount = 0;
					}
					taskEXIT_CRITICAL();
				}
			}
			else if (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF)
			{
				if (rxCSSactive && (cssFlag == false)) // CSS disappeared.
				{
					trxCssMeasureCount++;
					// If using CTCSS or DCS and signal isn't lost, allow some loss of tone / code.
					// Note:
					//    It's not unusual to have the CSS detection failing (CTCSS, depending of the sub-tone) if
					//    the signal is over-modulated/deviated, so waiting for 150ms is fine, and almost needed.
					//    Waiting for shorter time will just constantly disable and enable the audio Amp.
					if (trxCssMeasureCount >= CTCSS_HOLD_DELAY)
					{
						audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
						currentRadioDevice->analogSignalReceived = false;
						currentRadioDevice->analogTriggeredAudio = false;
						trxCssMeasureCount = 0;
					}
				}
				else
				{
					trxCssMeasureCount = 0;
				}
			}
		}
		else
		{
			if (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF)
			{
				trxCssMeasureCount++;
				// If using CTCSS or DCS and signal isn't lost, allow some loss of tone / code
				//
				// NOTE: Currently it is NOT waiting at all.
				//
				if ((rxCSSactive == false) || (trxCssMeasureCount >= SQUELCH_CLOSE_DELAY))
				{
					audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
					trxCssMeasureCount = 0;
				}
			}
		}

		ticksTimerStart((ticksTimer_t *)&trxNextSquelchCheckingTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	}

	return currentRadioDevice->analogSignalReceived;
}

void trxResetSquelchesState(RadioDevice_t deviceId)
{
	radioDevices[deviceId].digitalSignalReceived = false;
	radioDevices[deviceId].analogSignalReceived = false;
}

void trxSetFrequency(uint32_t fRx, uint32_t fTx, int dmrMode)
{
	//
	// Freq could be identical, but not the power of the current channel
	//
	if (currentChannelData->libreDMR_Power != 0x00)
	{
		currentRadioDevice->txPowerLevel = currentChannelData->libreDMR_Power - 1;
	}
	else
	{
		currentRadioDevice->txPowerLevel = nonVolatileSettings.txPowerLevel;
	}

	if (dmrMode == DMR_MODE_AUTO)
	{
		// Most DMR radios determine whether to use Active or Passive DMR depending on whether the Tx and Rx freq are the same
		// This prevents split simplex operation, but since no other radio appears to support split freq simplex
		// Its easier to do things the same way as othe radios, and revisit this again in the future if split freq simplex is required.
		if (fRx == fTx)
		{
			currentRadioDevice->trxDMRModeTx = DMR_MODE_DMO;
			currentRadioDevice->trxDMRModeRx = DMR_MODE_DMO;
		}
		else
		{
			currentRadioDevice->trxDMRModeTx = DMR_MODE_RMO;
			currentRadioDevice->trxDMRModeRx = DMR_MODE_RMO;
		}
	}
	else
	{
		currentRadioDevice->trxDMRModeTx = dmrMode;
		currentRadioDevice->trxDMRModeRx = dmrMode;
	}

	if ((currentRadioDevice->currentRxFrequency != fRx) || (currentRadioDevice->currentTxFrequency != fTx))
	{
		if (rxPowerSavingIsRxOn() == false)
		{
			rxPowerSavingSetState(ECOPHASE_POWERSAVE_INACTIVE);
		}

		taskENTER_CRITICAL();
		currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND] = trxGetBandFromFrequency(fRx);

		currentRadioDevice->currentRxFrequency = fRx;
		currentRadioDevice->currentTxFrequency = fTx;

		if (currentRadioDevice->currentMode == RADIO_MODE_DIGITAL)
		{
			HRC6000TerminateDigital();
		}

		trxUpdateC6000Calibration();
		trxUpdateRadioCalibration();

		radioSetFrequency(currentRadioDevice->currentRxFrequency, false);
		trxSetRX();
		radioSetIF(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND], currentRadioDevice->currentBandWidthIs25kHz);

		if (currentRadioDevice->currentMode == RADIO_MODE_DIGITAL)
		{
			HRC6000InitDigital();
		}

		ticksTimerStart((ticksTimer_t *)&trxNextRssiNoiseSampleTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
		ticksTimerStart((ticksTimer_t *)&trxNextSquelchCheckingTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
		taskEXIT_CRITICAL();
	}
}

uint32_t trxGetFrequency(void)
{
	if (trxTransmissionEnabled)
	{
		return currentRadioDevice->currentTxFrequency;
	}

	return currentRadioDevice->currentRxFrequency;
}

void trxSetRX(void)
{
	if (currentRadioDevice->currentMode == RADIO_MODE_ANALOG)
	{
		trxActivateRx(true);
	}
}

void trxConfigurePA_DAC_ForFrequencyBand(void)
{
	currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND] = trxGetBandFromFrequency(currentRadioDevice->currentTxFrequency);
	calibrationGetPowerForFrequency(currentRadioDevice->currentTxFrequency, &trxPowerSettings);
	currentRadioDevice->lastSetTxFrequency = currentRadioDevice->currentTxFrequency;
	currentRadioDevice->lastSetTxPowerLevel = currentRadioDevice->txPowerLevel;

	trxUpdate_PA_DAC_Drive();
}

void trxSetTX(void)
{
	trxConfigurePA_DAC_ForFrequencyBand();

	trxTransmissionEnabled = true;

	if (currentRadioDevice->currentMode == RADIO_MODE_ANALOG)
	{
		trxActivateTx(true);
	}
}

void trxActivateRx(bool critical)
{
	UNUSED_PARAMETER(critical);

	trxIsTransmittingDMR = false;
	radioSetRx(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]);
	radioSetFrequency(currentRadioDevice->currentRxFrequency, false);
	radioSetIF(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND], currentRadioDevice->currentBandWidthIs25kHz);

	trxUpdateC6000Calibration();// This seems to be needed, otherwise after transmission has ended the Rx appears to have a considerable freq offset

	ticksTimerStart((ticksTimer_t *)&trxNextRssiNoiseSampleTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
	ticksTimerStart((ticksTimer_t *)&trxNextSquelchCheckingTimer, RSSI_NOISE_SAMPLE_PERIOD_PIT);
}

void trxActivateTx(bool critical)
{
	UNUSED_PARAMETER(critical);

	if (currentRadioDevice->currentMode == RADIO_MODE_NONE)
	{
		return;
	}

	txPAEnabled = true;
	currentRadioDevice->trxRxSignal = 0;
	currentRadioDevice->trxRxNoise = 255;

	radioSetFrequency(currentRadioDevice->currentTxFrequency, true);

	radioSetTx(currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]);
}

//start of DMR transmission so do a full activation of the transmitter
void trxActivateDMRTx(void)
{
	trxActivateTx(false);
	trxIsTransmittingDMR = true;
}

//Already transmitting so just turn the output signal On or off
void trxFastDMRTx(bool tx)
{
	radioFastTx(tx);
}

void trxSetPowerFromLevel(uint8_t powerLevel)
{
	currentRadioDevice->txPowerLevel = powerLevel;
}

void trxUpdate_PA_DAC_Drive(void)
{
	static const float fractionalPowers[3][4] = {
#if defined(PLATFORM_RT84_DM1701)
		// DM1701 or RT84 which have same RF hardware
		{0.45f, 0.75f, 0.25f, 0.53f},// VHF
		{0.45f, 0.75f, 0.25f, 0.53f},// 220Mhz - ESTIMATED - NOT TESTED PROBABLY NOT CORRECT
		{0.46f, 0.73f, 0.14f, 0.36f},// UHF

#else
	#if defined(PLATFORM_VARIANT_UV380_PLUS_10W)
		// 10W UV380
		{0.58f, 0.83f, 0.21f, 0.45f},// VHF
		{0.58f, 0.83f, 0.21f, 0.45f},// 220Mhz - ESTIMATED - NOT TESTED PROBABLY NOT CORRECT
		{0.55f, 0.75f, 0.17f, 0.43f},// UHF

	#else
		//  5W UV380
		{0.35f, 0.70f, 0.34f, 0.61f},// VHF
		{0.38f, 0.70f, 0.30f, 0.59f},// 220Mhz - ESTIMATED - NOT TESTED PROBABLY NOT CORRECT
		{0.40f, 0.70f, 0.25f, 0.55f},// UHF
	#endif
#endif
	};//fractionalPowers

#if defined(PLATFORM_VARIANT_UV380_PLUS_10W)
	switch(currentRadioDevice->txPowerLevel)
	{
		case 0:// 50mW
			if(trxPowerSettings.veryLowPower > 160)
			{
				txDACDrivePower = trxPowerSettings.veryLowPower - 160 ;		           //50mW power setting using a typical value for low gain radios
			}
			else
			{
				txDACDrivePower = 0 ;		           //min power setting for high gain radios (may still be more than 50mW)
			}
			break;
		case 1:// 250mW
			txDACDrivePower = trxPowerSettings.veryLowPower;
			break;
		case 2:// 500mW
			txDACDrivePower = trxPowerSettings.veryLowPower + ((trxPowerSettings.lowPower - trxPowerSettings.veryLowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][0]);
			break;
		case 3:// 750mW
			txDACDrivePower = trxPowerSettings.veryLowPower + ((trxPowerSettings.lowPower - trxPowerSettings.veryLowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][1]);
			break;
		case 4:// 1W
			txDACDrivePower = trxPowerSettings.lowPower;
			break;
		case 5:// 2W
			txDACDrivePower = trxPowerSettings.lowPower + ((trxPowerSettings.midPower - trxPowerSettings.lowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][2]);//calculate based on mid and low datapoints
			break;
		case 6:// 3W
			txDACDrivePower = trxPowerSettings.lowPower + ((trxPowerSettings.midPower - trxPowerSettings.lowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][3]);//calculate based on mid and low datapoints
			break;
		case 7:// 5W
			txDACDrivePower = trxPowerSettings.midPower;
			break;
		case 8:// 10W
			txDACDrivePower = trxPowerSettings.highPower;
			break;
		case 9:// +W-
			txDACDrivePower = nonVolatileSettings.userPower;
			break;
		default:
			txDACDrivePower = trxPowerSettings.lowPower;
			break;
	}
#else
	switch(currentRadioDevice->txPowerLevel)
	{
		case 0:// 50mW
			if(trxPowerSettings.veryLowPower > 160)
			{
				txDACDrivePower = trxPowerSettings.veryLowPower - 160 ;		           //50mW power setting using a typical value for low gain radios
			}
			else
			{
				txDACDrivePower = 0 ;		           //min power setting for high gain radios (may still be more than 50mW)
			}
			break;
		case 1:// 250mW
			txDACDrivePower = trxPowerSettings.veryLowPower;
			break;
		case 2:// 500mW
			txDACDrivePower = trxPowerSettings.veryLowPower + ((trxPowerSettings.lowPower - trxPowerSettings.veryLowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][0]);
			break;
		case 3:// 750mW
			txDACDrivePower = trxPowerSettings.veryLowPower + ((trxPowerSettings.lowPower - trxPowerSettings.veryLowPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][1]);
			break;
		case 4:// 1W
			txDACDrivePower = trxPowerSettings.lowPower;
			break;
		case 5:// 2W
			txDACDrivePower = trxPowerSettings.midPower;// 2W on 5W radios
			break;
		case 6:// 3W
			txDACDrivePower = trxPowerSettings.midPower + ((trxPowerSettings.highPower - trxPowerSettings.midPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][2]);//calculate based on high and mid datapoints
			break;
		case 7:// 4W
			txDACDrivePower = trxPowerSettings.midPower + ((trxPowerSettings.highPower - trxPowerSettings.midPower) * fractionalPowers[currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND]][3]);//calculate based on high and mid datapoints
			break;
		case 8:// 5W
			txDACDrivePower = trxPowerSettings.highPower;
			break;
		case 9:// +W-
			txDACDrivePower = nonVolatileSettings.userPower;
			break;
		default:
			txDACDrivePower = trxPowerSettings.lowPower;
			break;
	}
#endif

	if (txDACDrivePower > MAX_PA_DAC_VALUE)
	{
		txDACDrivePower = MAX_PA_DAC_VALUE;
	}
}

uint16_t trxGetPA_DAC_Drive(void)
{
	return txDACDrivePower;
}

uint8_t trxGetPowerLevel(void)
{
	return currentRadioDevice->txPowerLevel;
}

void trxCalcBandAndFrequencyOffset(CalibrationBand_t *calibrationBand, uint32_t *freq_offset)
{
// NOTE. For crossband duplex DMR, the calibration potentially needs to be changed every time the Tx/Rx is switched over on each 30ms cycle
// But at the moment this is an unnecessary complication and I'll just use the Rx frequency to get the calibration offsets

	if (currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND] == RADIO_BAND_UHF)
	{
		*calibrationBand = CalibrationBandUHF;
		*freq_offset = (currentRadioDevice->currentTxFrequency - 40000000) / 1000000;
		if (*freq_offset > 8)
		{
			*freq_offset = 8;
		}
	}
	else
	{
		*calibrationBand = CalibrationBandVHF;
		*freq_offset = (currentRadioDevice->currentTxFrequency - 13600000) / 950000;
		if (*freq_offset > 4)
		{
			*freq_offset = 4;
		}
	}
}

static void trxUpdateC6000Calibration(void)
{
	int8_t cal = calibrationGetMod2Offset(currentRadioDevice->trxCurrentBand[trxTransmissionEnabled ? TRX_TX_FREQ_BAND : TRX_RX_FREQ_BAND]);
	SPI0WritePageRegByte(0x04, 0x47, cal);			// Set the reference tuning offset
	SPI0WritePageRegByte(0x04, 0x48, ((cal < 0) ? 0x03 : 0x00));
	SPI0WritePageRegByte(0x04, 0x04, cal);									//Set MOD 2 Offset (Cal Value)
}

static void trxUpdateRadioCalibration(void)
{
	analogIGain = calibrationGetAnalogIGainForFrequency(currentRadioDevice->currentTxFrequency);
	analogQGain = calibrationGetAnalogQGainForFrequency(currentRadioDevice->currentTxFrequency);
	digitalIGain = calibrationGetDigitalIGainForFrequency(currentRadioDevice->currentTxFrequency);
	digitalQGain = calibrationGetDigitalQGainForFrequency(currentRadioDevice->currentTxFrequency);
	Mod2Offset = calibrationGetMod2Offset(currentRadioDevice->trxCurrentBand[trxTransmissionEnabled ? TRX_TX_FREQ_BAND : TRX_RX_FREQ_BAND]);
}

void trxSetDMRColourCode(uint8_t colourCode)
{
	if (rxPowerSavingIsRxOn() == false)
	{
		rxPowerSavingSetState(ECOPHASE_POWERSAVE_INACTIVE);
	}

	SPI0WritePageRegByte(0x04, 0x1F, (colourCode << 4)); // DMR Colour code in upper 4 bits.
	currentCC = colourCode;
}

uint8_t trxGetDMRColourCode(void)
{
	return currentCC;
}

int trxGetDMRTimeSlot(void)
{
	return trxCurrentDMRTimeSlot;
}

void trxSetDMRTimeSlot(int timeslot, bool resync)
{
	if (rxPowerSavingIsRxOn() == false)
	{
		rxPowerSavingSetState(ECOPHASE_POWERSAVE_INACTIVE);
	}

	trxCurrentDMRTimeSlot = timeslot;

	if (resync)
	{
		HRC6000ResyncTimeSlot();
	}
}

void trxUpdateTsForCurrentChannelWithSpecifiedContact(CodeplugContact_t *contactData)
{
	// Contact TS override ?
	if ((nonVolatileSettings.overrideTG == 0) && (contactData->reserve1 & CODEPLUG_CONTACT_FLAG_NO_TS_OVERRIDE) == 0x00)
	{
		if (tsIsContactHasBeenOverriddenFromCurrentChannel())
		{
			trxCurrentDMRTimeSlot = (tsGetManualOverrideFromCurrentChannel() - 1);
		}
		else
		{
			trxCurrentDMRTimeSlot = ((contactData->reserve1 & CODEPLUG_CONTACT_FLAG_TS_OVERRIDE_TIMESLOT_MASK) != 0) ? 1 : 0;
		}
	}
	else
	{
		int8_t overriddenTS = tsGetManualOverrideFromCurrentChannel();

		// No manual override
		if (overriddenTS == 0)
		{
			// Apply channnel TS
			trxCurrentDMRTimeSlot = (codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_TIMESLOT_TWO) != 0) ? 1 : 0;
		}
		else
		{
			// Restore overriden TS (as previous contact may have changed it
			trxCurrentDMRTimeSlot = (overriddenTS - 1);
		}
	}

	HRC6000ResyncTimeSlot();
}

// Codeplug format (hex) -> octal
static uint16_t convertCSSNative2BinaryCodedOctal(uint16_t nativeCSS)
{
	uint16_t octalCSS = 0;
	uint16_t shift = 0;

	while (nativeCSS)
	{
		octalCSS += (nativeCSS & 0xF) << shift;
		nativeCSS >>= 4;
		shift += 3;
	}
	return octalCSS;
}

void trxSetTxCSS(uint16_t tone)
{
	CodeplugCSSTypes_t type = codeplugGetCSSType(tone);

	if (type == CSS_TYPE_NONE)
	{
		radioTxCSSOff();
	}
	else if (type == CSS_TYPE_CTCSS)
	{
		// value that is stored is 100 time the tone freq but its stored in the codeplug as freq times 10
		tone *= 10;
		radioTxCTCSOn(tone);
	}
	else if (type & CSS_TYPE_DCS)
	{
		uint16_t code = convertCSSNative2BinaryCodedOctal(tone & ~CSS_TYPE_DCS_MASK);

		radioTxDCSOn(code, ((type & CSS_TYPE_DCS_INVERTED) != 0));
	}
}

void trxSetRxCSS(RadioDevice_t deviceId, uint16_t tone)
{
	CodeplugCSSTypes_t type = codeplugGetCSSType(tone);

	if (type == CSS_TYPE_NONE)
	{
		radioRxCSSOff(deviceId);
		rxCSSactive = false;
	}
	else if (type == CSS_TYPE_CTCSS)
	{
		// value that is stored is 100 time the tone freq but its stored in the codeplug as freq times 10
		tone *= 10;
		radioRxCTCSOn(deviceId, tone);
		rxCSSactive = (trxAnalogFilterLevel != ANALOG_FILTER_NONE);
		// Force closing the AudioAmp
		audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
		radioDevices[deviceId].analogSignalReceived = false;
		radioDevices[deviceId].analogTriggeredAudio = false;
	}
	else if (type & CSS_TYPE_DCS)
	{
		uint16_t code = convertCSSNative2BinaryCodedOctal(tone & ~CSS_TYPE_DCS_MASK);

		radioRxDCSOn(deviceId, code, ((type & CSS_TYPE_DCS_INVERTED) != 0));
		rxCSSactive = (trxAnalogFilterLevel != ANALOG_FILTER_NONE);
		// Force closing the AudioAmp
		audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
		radioDevices[deviceId].analogSignalReceived = false;
		radioDevices[deviceId].analogTriggeredAudio = false;
	}
}

bool trxCheckCSSFlag(uint16_t tone)
{
	CodeplugCSSTypes_t type = codeplugGetCSSType(tone);

	return ((type != CSS_TYPE_NONE) && (radioCheckCSS(tone, type)));
}

uint8_t trxGetCalibrationVoiceGainTx(void)
{
	return voice_gain_tx;
}

void trxSetTone1(int toneFreq)
{
    radioSetTone1(toneFreq);
}

void trxSetDTMF(int code)
{
	if (code < 16)
	{
		HRC6000SetDTMF(code);
	}
}

void trxDTMFoff(bool enableMic)
{
	HRC6000DTMFoff(enableMic);
}

void trxSetMicGainFM(uint8_t gain)
{
	radioSetMicGainFM(gain);
}

void trxEnableTransmission(void)
{
	LedWrite(LED_GREEN, 0);
	LedWrite(LED_RED, 1);
	trxSetTX();
}

void trxDisableTransmission(void)
{
	LedWrite(LED_RED, 0);
	trxActivateRx(true);
}

// Returns true if the HR-C6000 has been powered off
bool trxPowerUpDownRxAndC6000(bool powerUp, bool includeC6000, bool includeMic)
{
	bool status = false;

	// Check the radio is not transmitting.
	if ((powerUp == powerUpDownState) || trxTransmissionEnabled || trxIsTransmitting)
	{
		return false;
	}

	// Force HRC6000 to power cycles in any ECO mode on STM32 platforms, as it seems
	// there are some bad batches that breaks Beep and Audio until the operator enables
	// monitor mode or restarts the radio.
#if defined(STM32F405xx)
	includeC6000 = true;
#endif

	if (powerUp)
	{
		radioPowerOn();
		radioSetBandwidth(currentRadioDevice->currentBandWidthIs25kHz);

		if (includeC6000 && (voicePromptsIsPlaying() == false))
		{
			uint8_t spi_values[SIZE_OF_FILL_BUFFER];

			// Always power up the C6000 even if its may already be powered up, because VP was playing
			HAL_GPIO_WritePin(C6000_PWD_GPIO_Port, C6000_PWD_Pin, GPIO_PIN_RESET); // Power Up the C6000
			// Allow some time to the C6000 to get ready
			vTaskDelay((10U / portTICK_PERIOD_MS));

			HRC6000SetDmrRxGain(0);							//temporarily set the gain to 0. Any less and the buffer flush doesn't seem to work.
			memset(spi_values, 0xAA, SIZE_OF_FILL_BUFFER);
			SPI0ClearPageRegByteWithMask(0x04, 0x06, 0xFD, 0x02); // SET OpenMusic bit (play Boot sound and Call Prompts)
			SPI0WritePageRegByteArray(0x03, 0x00, spi_values, SIZE_OF_FILL_BUFFER);
			SPI0ClearPageRegByteWithMask(0x04, 0x06, 0xFD, 0x00); // CLEAR OpenMusic bit (play Boot sound and Call Prompts)

			SPI0WritePageRegByte(0x04, 0x06, 0x21); // Use SPI vocoder under MCU control

			HRC6000SetDmrRxGain(getVolumeControl());			//restore gain to the volume control setting

			// Needs to reset all the audio (I2S BUS/buffering and sound counters).
			vTaskDelay((10U / portTICK_PERIOD_MS));
			soundInit();
		}

#if 0
		// Enable the IRQ, conditionally.
		if (NVIC_GetEnableIRQ(PORTC_IRQn) == 0)
		{
			NVIC_EnableIRQ(PORTC_IRQn);
		}
#endif
	}
	else
	{
		taskENTER_CRITICAL();

		radioPowerOff(false, includeMic);

#ifdef USE_AT1846S_DEEP_SLEEP
		radioWriteReg2byte(0x30, 0x00, 0x00); // Now enter power down mode
#endif

		if (!voicePromptsIsPlaying())
		{
			if (includeC6000)
			{
				// Ensure the ISR has exited before powering off the chip.
				while (HRC6000IRQHandlerIsRunning());

				HAL_GPIO_WritePin(C6000_PWD_GPIO_Port, C6000_PWD_Pin, GPIO_PIN_SET); // Power Up the C6000
				status = true;
			}
		}
		taskEXIT_CRITICAL();
	}

	powerUpDownState = powerUp;

	return status;
}

void trxInvalidateCurrentFrequency(void)
{
	currentRadioDevice->currentRxFrequency = FREQUENCY_UNSET;
	currentRadioDevice->currentTxFrequency = FREQUENCY_UNSET;
	currentRadioDevice->currentMode = RADIO_MODE_NONE;
}

void trxSelectVoiceChannel(uint8_t channel)
{
	radioSelectVoiceChannel(channel, &trxSaveVoiceGainTx, &trxSaveDeviation);
}

void trxRxAndTxOff(bool critical)
{
	UNUSED_PARAMETER(critical);
}

void trxRxOn(bool critical)
{
	UNUSED_PARAMETER(critical);
}

#if defined(MDUV380_VERSION_2) || defined (MDUV380_VERSION_4) || defined (MDUV380_VERSION_1)
#define VHF_RSSI_OFFSET -135
#define UHF_RSSI_OFFSET -145
#define VHF_RSSI_DIVISOR 2
#define UHF_RSSI_DIVISOR 2
#else
#define VHF_RSSI_OFFSET -155
#define UHF_RSSI_OFFSET -155
#define VHF_RSSI_DIVISOR 1.95
#define UHF_RSSI_DIVISOR 1.95
#endif

int trxGetRSSIdBm(RadioDevice_t deviceId)
{
	int dBm = 0;

	if (radioDevices[deviceId].trxCurrentBand[TRX_RX_FREQ_BAND] == RADIO_BAND_UHF)
	{
		// Use fixed point maths to scale the RSSI value to dBm, based on data from VK4JWT and VK7ZJA
		dBm = -151 + radioDevices[deviceId].trxRxSignal;// Note no the RSSI value on UHF does not need to be scaled like it does on VHF
	}
	else
	{
		// VHF
		// Use fixed point maths to scale the RSSI value to dBm, based on data from VK4JWT and VK7ZJA
		dBm = -164 + ((radioDevices[deviceId].trxRxSignal * 32) / 27);
	}

	return dBm;
}

int trxGetNoisedBm(RadioDevice_t deviceId)
{
	int dBm = 0;

	if (radioDevices[deviceId].trxCurrentBand[TRX_RX_FREQ_BAND] == RADIO_BAND_UHF)
	{
		dBm = -151 + radioDevices[deviceId].trxRxNoise;// Note no the RSSI value on UHF does not need to be scaled like it does on VHF
	}
	else
	{
		// VHF
		dBm = -164 + ((radioDevices[deviceId].trxRxNoise * 32) / 27);
	}

	return dBm;
}

int trxGetSNRMargindBm(RadioDevice_t deviceId)
{
	return (trxGetRSSIdBm(deviceId) - trxGetNoisedBm(deviceId));
}

uint8_t trxGetSignalRaw(RadioDevice_t deviceId)
{
	return radioDevices[deviceId].trxRxSignal;
}

uint8_t trxGetNoiseRaw(RadioDevice_t deviceId)
{
	return radioDevices[deviceId].trxRxNoise;
}
