mirror of
https://github.com/phdlee/uvk5cec
synced 2024-11-21 07:32:46 +00:00
1080 lines
28 KiB
C
1080 lines
28 KiB
C
/* Copyright 2023 Dual Tachyon
|
|
* https://github.com/DualTachyon
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "driver/bk4819-regs.h"
|
|
#include <string.h>
|
|
|
|
#include "am_fix.h"
|
|
#include "app/dtmf.h"
|
|
#ifdef ENABLE_FMRADIO
|
|
#include "app/fm.h"
|
|
#endif
|
|
#include "audio.h"
|
|
#include "bsp/dp32g030/gpio.h"
|
|
#include "dcs.h"
|
|
#include "driver/bk4819.h"
|
|
#include "driver/eeprom.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/system.h"
|
|
#include "frequencies.h"
|
|
#include "functions.h"
|
|
#include "helper/battery.h"
|
|
#include "misc.h"
|
|
#include "radio.h"
|
|
#include "settings.h"
|
|
#include "ui/menu.h"
|
|
|
|
VFO_Info_t *gTxVfo;
|
|
VFO_Info_t *gRxVfo;
|
|
VFO_Info_t *gCurrentVfo;
|
|
DCS_CodeType_t gCurrentCodeType;
|
|
VfoState_t VfoState[2];
|
|
|
|
const char gModulationStr[MODULATION_UKNOWN][4] = {
|
|
[MODULATION_FM]="FM",
|
|
[MODULATION_AM]="AM",
|
|
[MODULATION_USB]="SSB",
|
|
|
|
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
|
[MODULATION_BYP]="BYP",
|
|
[MODULATION_RAW]="RAW"
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList)
|
|
{
|
|
// return true if the channel appears valid
|
|
if (!IS_MR_CHANNEL(channel))
|
|
return false;
|
|
|
|
const ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
|
|
|
if (att.band > BAND7_470MHz)
|
|
return false;
|
|
|
|
if (!checkScanList || scanList > 1)
|
|
return true;
|
|
|
|
if (scanList ? !att.scanlist2 : !att.scanlist1)
|
|
return false;
|
|
|
|
const uint8_t PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH1[scanList];
|
|
const uint8_t PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH2[scanList];
|
|
|
|
return PriorityCh1 != channel && PriorityCh2 != channel;
|
|
}
|
|
|
|
uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
|
|
{
|
|
for (unsigned int i = 0; IS_MR_CHANNEL(i); i++, Channel += Direction) {
|
|
if (Channel == 0xFF) {
|
|
Channel = MR_CHANNEL_LAST;
|
|
} else if (!IS_MR_CHANNEL(Channel)) {
|
|
Channel = MR_CHANNEL_FIRST;
|
|
}
|
|
|
|
if (RADIO_CheckValidChannel(Channel, bCheckScanList, VFO)) {
|
|
return Channel;
|
|
}
|
|
}
|
|
|
|
return 0xFF;
|
|
}
|
|
|
|
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t Frequency)
|
|
{
|
|
memset(pInfo, 0, sizeof(*pInfo));
|
|
|
|
pInfo->Band = FREQUENCY_GetBand(Frequency);
|
|
pInfo->SCANLIST1_PARTICIPATION = false;
|
|
pInfo->SCANLIST2_PARTICIPATION = false;
|
|
pInfo->STEP_SETTING = STEP_12_5kHz;
|
|
pInfo->StepFrequency = gStepFrequencyTable[pInfo->STEP_SETTING];
|
|
pInfo->CHANNEL_SAVE = ChannelSave;
|
|
pInfo->FrequencyReverse = false;
|
|
pInfo->OUTPUT_POWER = OUTPUT_POWER_LOW;
|
|
pInfo->freq_config_RX.Frequency = Frequency;
|
|
pInfo->freq_config_TX.Frequency = Frequency;
|
|
pInfo->pRX = &pInfo->freq_config_RX;
|
|
pInfo->pTX = &pInfo->freq_config_TX;
|
|
pInfo->Compander = 0; // off
|
|
|
|
if (ChannelSave == (FREQ_CHANNEL_FIRST + BAND2_108MHz))
|
|
pInfo->Modulation = MODULATION_AM;
|
|
else
|
|
pInfo->Modulation = MODULATION_FM;
|
|
|
|
RADIO_ConfigureSquelchAndOutputPower(pInfo);
|
|
}
|
|
|
|
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure)
|
|
{
|
|
VFO_Info_t *pVfo = &gEeprom.VfoInfo[VFO];
|
|
|
|
if (!gSetting_350EN) {
|
|
if (gEeprom.FreqChannel[VFO] == FREQ_CHANNEL_FIRST + BAND5_350MHz)
|
|
gEeprom.FreqChannel[VFO] = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
|
|
|
if (gEeprom.ScreenChannel[VFO] == FREQ_CHANNEL_FIRST + BAND5_350MHz)
|
|
gEeprom.ScreenChannel[VFO] = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
|
}
|
|
|
|
uint8_t channel = gEeprom.ScreenChannel[VFO];
|
|
|
|
if (IS_VALID_CHANNEL(channel)) {
|
|
#ifdef ENABLE_NOAA
|
|
if (IS_NOAA_CHANNEL(channel))
|
|
{
|
|
RADIO_InitInfo(pVfo, gEeprom.ScreenChannel[VFO], NoaaFrequencyTable[channel - NOAA_CHANNEL_FIRST]);
|
|
|
|
if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF)
|
|
return;
|
|
|
|
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
|
|
|
gUpdateStatus = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (IS_MR_CHANNEL(channel)) {
|
|
channel = RADIO_FindNextChannel(channel, RADIO_CHANNEL_UP, false, VFO);
|
|
if (channel == 0xFF) {
|
|
channel = gEeprom.FreqChannel[VFO];
|
|
gEeprom.ScreenChannel[VFO] = gEeprom.FreqChannel[VFO];
|
|
}
|
|
else {
|
|
gEeprom.ScreenChannel[VFO] = channel;
|
|
gEeprom.MrChannel[VFO] = channel;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
channel = FREQ_CHANNEL_LAST - 1;
|
|
|
|
ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
|
if (att.__val == 0xFF) { // invalid/unused channel
|
|
if (IS_MR_CHANNEL(channel)) {
|
|
channel = gEeprom.FreqChannel[VFO];
|
|
gEeprom.ScreenChannel[VFO] = channel;
|
|
}
|
|
|
|
uint8_t bandIdx = channel - FREQ_CHANNEL_FIRST;
|
|
RADIO_InitInfo(pVfo, channel, frequencyBandTable[bandIdx].lower);
|
|
return;
|
|
}
|
|
|
|
uint8_t band = att.band;
|
|
if (band > BAND7_470MHz) {
|
|
band = BAND6_400MHz;
|
|
}
|
|
|
|
bool bParticipation1;
|
|
bool bParticipation2;
|
|
if (IS_MR_CHANNEL(channel)) {
|
|
bParticipation1 = att.scanlist1;
|
|
bParticipation2 = att.scanlist2;
|
|
}
|
|
else {
|
|
band = channel - FREQ_CHANNEL_FIRST;
|
|
bParticipation1 = true;
|
|
bParticipation2 = true;
|
|
}
|
|
|
|
pVfo->Band = band;
|
|
pVfo->SCANLIST1_PARTICIPATION = bParticipation1;
|
|
pVfo->SCANLIST2_PARTICIPATION = bParticipation2;
|
|
pVfo->CHANNEL_SAVE = channel;
|
|
|
|
uint16_t base;
|
|
if (IS_MR_CHANNEL(channel))
|
|
base = channel * 16;
|
|
else
|
|
base = 0x0C80 + ((channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);
|
|
|
|
if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(channel))
|
|
{
|
|
uint8_t tmp;
|
|
uint8_t data[8];
|
|
|
|
// ***************
|
|
|
|
EEPROM_ReadBuffer(base + 8, data, sizeof(data));
|
|
|
|
tmp = data[3] & 0x0F;
|
|
if (tmp > TX_OFFSET_FREQUENCY_DIRECTION_SUB)
|
|
tmp = 0;
|
|
pVfo->TX_OFFSET_FREQUENCY_DIRECTION = tmp;
|
|
tmp = data[3] >> 4;
|
|
if (tmp >= MODULATION_UKNOWN)
|
|
tmp = MODULATION_FM;
|
|
pVfo->Modulation = tmp;
|
|
|
|
tmp = data[6];
|
|
if (tmp >= STEP_N_ELEM)
|
|
tmp = STEP_12_5kHz;
|
|
pVfo->STEP_SETTING = tmp;
|
|
pVfo->StepFrequency = gStepFrequencyTable[tmp];
|
|
|
|
tmp = data[7];
|
|
if (tmp > (ARRAY_SIZE(gSubMenu_SCRAMBLER) - 1))
|
|
tmp = 0;
|
|
pVfo->SCRAMBLING_TYPE = tmp;
|
|
|
|
pVfo->freq_config_RX.CodeType = (data[2] >> 0) & 0x0F;
|
|
pVfo->freq_config_TX.CodeType = (data[2] >> 4) & 0x0F;
|
|
|
|
tmp = data[0];
|
|
switch (pVfo->freq_config_RX.CodeType)
|
|
{
|
|
default:
|
|
case CODE_TYPE_OFF:
|
|
pVfo->freq_config_RX.CodeType = CODE_TYPE_OFF;
|
|
tmp = 0;
|
|
break;
|
|
|
|
case CODE_TYPE_CONTINUOUS_TONE:
|
|
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
|
tmp = 0;
|
|
break;
|
|
|
|
case CODE_TYPE_DIGITAL:
|
|
case CODE_TYPE_REVERSE_DIGITAL:
|
|
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
|
tmp = 0;
|
|
break;
|
|
}
|
|
pVfo->freq_config_RX.Code = tmp;
|
|
|
|
tmp = data[1];
|
|
switch (pVfo->freq_config_TX.CodeType)
|
|
{
|
|
default:
|
|
case CODE_TYPE_OFF:
|
|
pVfo->freq_config_TX.CodeType = CODE_TYPE_OFF;
|
|
tmp = 0;
|
|
break;
|
|
|
|
case CODE_TYPE_CONTINUOUS_TONE:
|
|
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
|
tmp = 0;
|
|
break;
|
|
|
|
case CODE_TYPE_DIGITAL:
|
|
case CODE_TYPE_REVERSE_DIGITAL:
|
|
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
|
tmp = 0;
|
|
break;
|
|
}
|
|
pVfo->freq_config_TX.Code = tmp;
|
|
|
|
if (data[4] == 0xFF)
|
|
{
|
|
pVfo->FrequencyReverse = false;
|
|
pVfo->CHANNEL_BANDWIDTH = BK4819_FILTER_BW_WIDE;
|
|
pVfo->OUTPUT_POWER = OUTPUT_POWER_LOW;
|
|
pVfo->BUSY_CHANNEL_LOCK = false;
|
|
}
|
|
else
|
|
{
|
|
const uint8_t d4 = data[4];
|
|
pVfo->FrequencyReverse = !!((d4 >> 0) & 1u);
|
|
pVfo->CHANNEL_BANDWIDTH = !!((d4 >> 1) & 1u);
|
|
pVfo->OUTPUT_POWER = ((d4 >> 2) & 3u);
|
|
pVfo->BUSY_CHANNEL_LOCK = !!((d4 >> 4) & 1u);
|
|
}
|
|
|
|
if (data[5] == 0xFF)
|
|
{
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
pVfo->DTMF_DECODING_ENABLE = false;
|
|
#endif
|
|
pVfo->DTMF_PTT_ID_TX_MODE = PTT_ID_OFF;
|
|
}
|
|
else
|
|
{
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
pVfo->DTMF_DECODING_ENABLE = ((data[5] >> 0) & 1u) ? true : false;
|
|
#endif
|
|
pVfo->DTMF_PTT_ID_TX_MODE = ((data[5] >> 1) & 7u);
|
|
}
|
|
|
|
// ***************
|
|
|
|
struct {
|
|
uint32_t Frequency;
|
|
uint32_t Offset;
|
|
} __attribute__((packed)) info;
|
|
EEPROM_ReadBuffer(base, &info, sizeof(info));
|
|
if(info.Frequency==0xFFFFFFFF)
|
|
pVfo->freq_config_RX.Frequency = frequencyBandTable[band].lower;
|
|
else
|
|
pVfo->freq_config_RX.Frequency = info.Frequency;
|
|
|
|
if (info.Offset >= _1GHz_in_KHz)
|
|
info.Offset = _1GHz_in_KHz / 100;
|
|
|
|
pVfo->TX_OFFSET_FREQUENCY = info.Offset;
|
|
|
|
// ***************
|
|
}
|
|
|
|
uint32_t frequency = pVfo->freq_config_RX.Frequency;
|
|
|
|
// fix previously set incorrect band
|
|
band = FREQUENCY_GetBand(frequency);
|
|
|
|
if (frequency < frequencyBandTable[band].lower)
|
|
frequency = frequencyBandTable[band].lower;
|
|
else if (frequency > frequencyBandTable[band].upper)
|
|
frequency = frequencyBandTable[band].upper;
|
|
else if (channel >= FREQ_CHANNEL_FIRST)
|
|
frequency = FREQUENCY_RoundToStep(frequency, pVfo->StepFrequency);
|
|
|
|
pVfo->freq_config_RX.Frequency = frequency;
|
|
|
|
if (frequency >= frequencyBandTable[BAND2_108MHz].upper && frequency < frequencyBandTable[BAND2_108MHz].upper)
|
|
pVfo->TX_OFFSET_FREQUENCY_DIRECTION = TX_OFFSET_FREQUENCY_DIRECTION_OFF;
|
|
else if (!IS_MR_CHANNEL(channel))
|
|
pVfo->TX_OFFSET_FREQUENCY = FREQUENCY_RoundToStep(pVfo->TX_OFFSET_FREQUENCY, pVfo->StepFrequency);
|
|
|
|
RADIO_ApplyOffset(pVfo);
|
|
|
|
if (IS_MR_CHANNEL(channel))
|
|
{ // 16 bytes allocated to the channel name but only 10 used, the rest are 0's
|
|
SETTINGS_FetchChannelName(pVfo->Name, channel);
|
|
}
|
|
|
|
if (!pVfo->FrequencyReverse)
|
|
{
|
|
pVfo->pRX = &pVfo->freq_config_RX;
|
|
pVfo->pTX = &pVfo->freq_config_TX;
|
|
}
|
|
else
|
|
{
|
|
pVfo->pRX = &pVfo->freq_config_TX;
|
|
pVfo->pTX = &pVfo->freq_config_RX;
|
|
}
|
|
|
|
if (!gSetting_350EN)
|
|
{
|
|
FREQ_Config_t *pConfig = pVfo->pRX;
|
|
if (pConfig->Frequency >= 35000000 && pConfig->Frequency < 40000000)
|
|
pConfig->Frequency = 43300000;
|
|
}
|
|
|
|
pVfo->Compander = att.compander;
|
|
|
|
RADIO_ConfigureSquelchAndOutputPower(pVfo);
|
|
}
|
|
|
|
void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
|
{
|
|
|
|
|
|
// *******************************
|
|
// squelch
|
|
|
|
FREQUENCY_Band_t Band = FREQUENCY_GetBand(pInfo->pRX->Frequency);
|
|
uint16_t Base = (Band < BAND4_174MHz) ? 0x1E60 : 0x1E00;
|
|
|
|
if (gEeprom.SQUELCH_LEVEL == 0)
|
|
{ // squelch == 0 (off)
|
|
pInfo->SquelchOpenRSSIThresh = 0; // 0 ~ 255
|
|
pInfo->SquelchOpenNoiseThresh = 127; // 127 ~ 0
|
|
pInfo->SquelchCloseGlitchThresh = 255; // 255 ~ 0
|
|
|
|
pInfo->SquelchCloseRSSIThresh = 0; // 0 ~ 255
|
|
pInfo->SquelchCloseNoiseThresh = 127; // 127 ~ 0
|
|
pInfo->SquelchOpenGlitchThresh = 255; // 255 ~ 0
|
|
}
|
|
else
|
|
{ // squelch >= 1
|
|
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
|
|
// VHF UHF
|
|
EEPROM_ReadBuffer(Base + 0x00, &pInfo->SquelchOpenRSSIThresh, 1); // 50 10
|
|
EEPROM_ReadBuffer(Base + 0x10, &pInfo->SquelchCloseRSSIThresh, 1); // 40 5
|
|
|
|
EEPROM_ReadBuffer(Base + 0x20, &pInfo->SquelchOpenNoiseThresh, 1); // 65 90
|
|
EEPROM_ReadBuffer(Base + 0x30, &pInfo->SquelchCloseNoiseThresh, 1); // 70 100
|
|
|
|
EEPROM_ReadBuffer(Base + 0x40, &pInfo->SquelchCloseGlitchThresh, 1); // 90 90
|
|
EEPROM_ReadBuffer(Base + 0x50, &pInfo->SquelchOpenGlitchThresh, 1); // 100 100
|
|
|
|
uint16_t rssi_open = pInfo->SquelchOpenRSSIThresh;
|
|
uint16_t rssi_close = pInfo->SquelchCloseRSSIThresh;
|
|
uint16_t noise_open = pInfo->SquelchOpenNoiseThresh;
|
|
uint16_t noise_close = pInfo->SquelchCloseNoiseThresh;
|
|
uint16_t glitch_open = pInfo->SquelchOpenGlitchThresh;
|
|
uint16_t glitch_close = pInfo->SquelchCloseGlitchThresh;
|
|
|
|
#if ENABLE_SQUELCH_MORE_SENSITIVE
|
|
// make squelch a little more sensitive
|
|
//
|
|
// getting the best setting here is still experimental, bare with me
|
|
//
|
|
// note that 'noise' and 'glitch' values are inverted compared to 'rssi' values
|
|
|
|
#if 0
|
|
rssi_open = (rssi_open * 8) / 9;
|
|
noise_open = (noise_open * 9) / 8;
|
|
glitch_open = (glitch_open * 9) / 8;
|
|
#else
|
|
// even more sensitive .. use when RX bandwidths are fixed (no weak signal auto adjust)
|
|
rssi_open = (rssi_open * 1) / 2;
|
|
noise_open = (noise_open * 2) / 1;
|
|
glitch_open = (glitch_open * 2) / 1;
|
|
#endif
|
|
|
|
#else
|
|
// more sensitive .. use when RX bandwidths are fixed (no weak signal auto adjust)
|
|
rssi_open = (rssi_open * 3) / 4;
|
|
noise_open = (noise_open * 4) / 3;
|
|
glitch_open = (glitch_open * 4) / 3;
|
|
#endif
|
|
|
|
rssi_close = (rssi_open * 9) / 10;
|
|
noise_close = (noise_open * 10) / 9;
|
|
glitch_close = (glitch_open * 10) / 9;
|
|
|
|
// ensure the 'close' threshold is lower than the 'open' threshold
|
|
if (rssi_close == rssi_open && rssi_close >= 2)
|
|
rssi_close -= 2;
|
|
if (noise_close == noise_open && noise_close <= 125)
|
|
noise_close += 2;
|
|
if (glitch_close == glitch_open && glitch_close <= 253)
|
|
glitch_close += 2;
|
|
|
|
pInfo->SquelchOpenRSSIThresh = (rssi_open > 255) ? 255 : rssi_open;
|
|
pInfo->SquelchCloseRSSIThresh = (rssi_close > 255) ? 255 : rssi_close;
|
|
pInfo->SquelchOpenNoiseThresh = (noise_open > 127) ? 127 : noise_open;
|
|
pInfo->SquelchCloseNoiseThresh = (noise_close > 127) ? 127 : noise_close;
|
|
pInfo->SquelchOpenGlitchThresh = (glitch_open > 255) ? 255 : glitch_open;
|
|
pInfo->SquelchCloseGlitchThresh = (glitch_close > 255) ? 255 : glitch_close;
|
|
}
|
|
|
|
// *******************************
|
|
// output power
|
|
|
|
Band = FREQUENCY_GetBand(pInfo->pTX->Frequency);
|
|
|
|
uint8_t Txp[3];
|
|
EEPROM_ReadBuffer(0x1ED0 + (Band * 16) + (pInfo->OUTPUT_POWER * 3), Txp, 3);
|
|
|
|
#ifdef ENABLE_REDUCE_LOW_MID_TX_POWER
|
|
// make low and mid even lower
|
|
if (pInfo->OUTPUT_POWER == OUTPUT_POWER_LOW) {
|
|
Txp[0] /= 5;
|
|
Txp[1] /= 5;
|
|
Txp[2] /= 5;
|
|
}
|
|
else if (pInfo->OUTPUT_POWER == OUTPUT_POWER_MID){
|
|
Txp[0] /= 3;
|
|
Txp[1] /= 3;
|
|
Txp[2] /= 3;
|
|
}
|
|
#endif
|
|
|
|
pInfo->TXP_CalculatedSetting = FREQUENCY_CalculateOutputPower(
|
|
Txp[0],
|
|
Txp[1],
|
|
Txp[2],
|
|
frequencyBandTable[Band].lower,
|
|
(frequencyBandTable[Band].lower + frequencyBandTable[Band].upper) / 2,
|
|
frequencyBandTable[Band].upper,
|
|
pInfo->pTX->Frequency);
|
|
|
|
// *******************************
|
|
}
|
|
|
|
void RADIO_ApplyOffset(VFO_Info_t *pInfo)
|
|
{
|
|
uint32_t Frequency = pInfo->freq_config_RX.Frequency;
|
|
|
|
switch (pInfo->TX_OFFSET_FREQUENCY_DIRECTION)
|
|
{
|
|
case TX_OFFSET_FREQUENCY_DIRECTION_OFF:
|
|
break;
|
|
case TX_OFFSET_FREQUENCY_DIRECTION_ADD:
|
|
Frequency += pInfo->TX_OFFSET_FREQUENCY;
|
|
break;
|
|
case TX_OFFSET_FREQUENCY_DIRECTION_SUB:
|
|
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
|
|
break;
|
|
}
|
|
|
|
if (Frequency < frequencyBandTable[0].lower)
|
|
Frequency = frequencyBandTable[0].lower;
|
|
else if (Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
|
|
Frequency = frequencyBandTable[BAND_N_ELEM - 1].upper;
|
|
|
|
pInfo->freq_config_TX.Frequency = Frequency;
|
|
}
|
|
|
|
static void RADIO_SelectCurrentVfo(void)
|
|
{
|
|
// if crossband is active and DW not the gCurrentVfo is gTxVfo (gTxVfo/TX_VFO is only ever changed by the user)
|
|
// otherwise it is set to gRxVfo which is set to gTxVfo in RADIO_SelectVfos
|
|
// so in the end gCurrentVfo is equal to gTxVfo unless dual watch changes it on incomming transmition (again, this can only happen when XB off)
|
|
// note: it is called only in certain situations so could be not up-to-date
|
|
gCurrentVfo = (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF || gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) ? gRxVfo : gTxVfo;
|
|
}
|
|
|
|
void RADIO_SelectVfos(void)
|
|
{
|
|
// if crossband without DW is used then RX_VFO is the opposite to the TX_VFO
|
|
gEeprom.RX_VFO = (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF || gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) ? gEeprom.TX_VFO : !gEeprom.TX_VFO;
|
|
|
|
gTxVfo = &gEeprom.VfoInfo[gEeprom.TX_VFO];
|
|
gRxVfo = &gEeprom.VfoInfo[gEeprom.RX_VFO];
|
|
|
|
RADIO_SelectCurrentVfo();
|
|
}
|
|
|
|
void RADIO_SetupRegisters(bool switchToForeground)
|
|
{
|
|
BK4819_FilterBandwidth_t Bandwidth = gRxVfo->CHANNEL_BANDWIDTH;
|
|
|
|
AUDIO_AudioPathOff();
|
|
|
|
gEnableSpeaker = false;
|
|
|
|
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
|
|
|
switch (Bandwidth)
|
|
{
|
|
default:
|
|
Bandwidth = BK4819_FILTER_BW_WIDE;
|
|
[[fallthrough]];
|
|
case BK4819_FILTER_BW_WIDE:
|
|
case BK4819_FILTER_BW_NARROW:
|
|
#ifdef ENABLE_AM_FIX
|
|
// BK4819_SetFilterBandwidth(Bandwidth, gRxVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
|
BK4819_SetFilterBandwidth(Bandwidth, true);
|
|
#else
|
|
BK4819_SetFilterBandwidth(Bandwidth, false);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
|
|
|
|
BK4819_SetupPowerAmplifier(0, 0);
|
|
|
|
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
|
|
|
while (1)
|
|
{
|
|
const uint16_t Status = BK4819_ReadRegister(BK4819_REG_0C);
|
|
if ((Status & 1u) == 0) // INTERRUPT REQUEST
|
|
break;
|
|
|
|
BK4819_WriteRegister(BK4819_REG_02, 0);
|
|
SYSTEM_DelayMs(1);
|
|
}
|
|
BK4819_WriteRegister(BK4819_REG_3F, 0);
|
|
|
|
// mic gain 0.5dB/step 0 to 31
|
|
BK4819_WriteRegister(BK4819_REG_7D, 0xE940 | (gEeprom.MIC_SENSITIVITY_TUNING & 0x1f));
|
|
|
|
uint32_t Frequency;
|
|
#ifdef ENABLE_NOAA
|
|
if (!IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE) || !gIsNoaaMode)
|
|
Frequency = gRxVfo->pRX->Frequency;
|
|
else
|
|
Frequency = NoaaFrequencyTable[gNoaaChannel];
|
|
#else
|
|
Frequency = gRxVfo->pRX->Frequency;
|
|
#endif
|
|
BK4819_SetFrequency(Frequency);
|
|
|
|
BK4819_SetupSquelch(
|
|
gRxVfo->SquelchOpenRSSIThresh, gRxVfo->SquelchCloseRSSIThresh,
|
|
gRxVfo->SquelchOpenNoiseThresh, gRxVfo->SquelchCloseNoiseThresh,
|
|
gRxVfo->SquelchCloseGlitchThresh, gRxVfo->SquelchOpenGlitchThresh);
|
|
|
|
BK4819_PickRXFilterPathBasedOnFrequency(Frequency);
|
|
|
|
// what does this in do ?
|
|
BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_RX_ENABLE, true);
|
|
|
|
// AF RX Gain and DAC
|
|
//BK4819_WriteRegister(BK4819_REG_48, 0xB3A8); // 1011 00 111010 1000
|
|
BK4819_WriteRegister(BK4819_REG_48,
|
|
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
|
( 0u << 10) | // AF Rx Gain-1
|
|
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
|
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
|
|
|
|
|
uint16_t InterruptMask = BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
|
|
|
#ifdef ENABLE_NOAA
|
|
if (!IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
|
#endif
|
|
{
|
|
if (gRxVfo->Modulation == MODULATION_FM)
|
|
{ // FM
|
|
uint8_t CodeType = gRxVfo->pRX->CodeType;
|
|
uint8_t Code = gRxVfo->pRX->Code;
|
|
|
|
switch (CodeType)
|
|
{
|
|
default:
|
|
case CODE_TYPE_OFF:
|
|
BK4819_SetCTCSSFrequency(670);
|
|
|
|
//#ifndef ENABLE_CTCSS_TAIL_PHASE_SHIFT
|
|
BK4819_SetTailDetection(550); // QS's 55Hz tone method
|
|
//#else
|
|
// BK4819_SetTailDetection(670); // 67Hz
|
|
//#endif
|
|
|
|
InterruptMask = BK4819_REG_3F_CxCSS_TAIL | BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
|
break;
|
|
|
|
case CODE_TYPE_CONTINUOUS_TONE:
|
|
BK4819_SetCTCSSFrequency(CTCSS_Options[Code]);
|
|
|
|
//#ifndef ENABLE_CTCSS_TAIL_PHASE_SHIFT
|
|
BK4819_SetTailDetection(550); // QS's 55Hz tone method
|
|
//#else
|
|
// BK4819_SetTailDetection(CTCSS_Options[Code]);
|
|
//#endif
|
|
|
|
InterruptMask = 0
|
|
| BK4819_REG_3F_CxCSS_TAIL
|
|
| BK4819_REG_3F_CTCSS_FOUND
|
|
| BK4819_REG_3F_CTCSS_LOST
|
|
| BK4819_REG_3F_SQUELCH_FOUND
|
|
| BK4819_REG_3F_SQUELCH_LOST;
|
|
|
|
break;
|
|
|
|
case CODE_TYPE_DIGITAL:
|
|
case CODE_TYPE_REVERSE_DIGITAL:
|
|
BK4819_SetCDCSSCodeWord(DCS_GetGolayCodeWord(CodeType, Code));
|
|
InterruptMask = 0
|
|
| BK4819_REG_3F_CxCSS_TAIL
|
|
| BK4819_REG_3F_CDCSS_FOUND
|
|
| BK4819_REG_3F_CDCSS_LOST
|
|
| BK4819_REG_3F_SQUELCH_FOUND
|
|
| BK4819_REG_3F_SQUELCH_LOST;
|
|
break;
|
|
}
|
|
|
|
if (gRxVfo->SCRAMBLING_TYPE > 0 && gSetting_ScrambleEnable)
|
|
BK4819_EnableScramble(gRxVfo->SCRAMBLING_TYPE - 1);
|
|
else
|
|
BK4819_DisableScramble();
|
|
}
|
|
}
|
|
#ifdef ENABLE_NOAA
|
|
else
|
|
{
|
|
BK4819_SetCTCSSFrequency(2625);
|
|
InterruptMask = 0
|
|
| BK4819_REG_3F_CTCSS_FOUND
|
|
| BK4819_REG_3F_CTCSS_LOST
|
|
| BK4819_REG_3F_SQUELCH_FOUND
|
|
| BK4819_REG_3F_SQUELCH_LOST;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_VOX
|
|
if (gEeprom.VOX_SWITCH && gCurrentVfo->Modulation == MODULATION_FM
|
|
#ifdef ENABLE_NOAA
|
|
&& !IS_NOAA_CHANNEL(gCurrentVfo->CHANNEL_SAVE)
|
|
#endif
|
|
#ifdef ENABLE_FMRADIO
|
|
&& !gFmRadioMode
|
|
#endif
|
|
){
|
|
BK4819_EnableVox(gEeprom.VOX1_THRESHOLD, gEeprom.VOX0_THRESHOLD);
|
|
InterruptMask |= BK4819_REG_3F_VOX_FOUND | BK4819_REG_3F_VOX_LOST;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
BK4819_DisableVox();
|
|
}
|
|
|
|
// RX expander
|
|
BK4819_SetCompander((gRxVfo->Modulation == MODULATION_FM && gRxVfo->Compander >= 2) ? gRxVfo->Compander : 0);
|
|
|
|
#if 0
|
|
if (!gRxVfo->DTMF_DECODING_ENABLE && !gSetting_KILLED)
|
|
{
|
|
BK4819_DisableDTMF();
|
|
}
|
|
else
|
|
{
|
|
BK4819_EnableDTMF();
|
|
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
|
}
|
|
#else
|
|
BK4819_DisableDTMF();
|
|
|
|
if (gCurrentFunction != FUNCTION_TRANSMIT) {
|
|
BK4819_EnableDTMF();
|
|
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
|
}
|
|
#endif
|
|
|
|
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
|
|
|
// enable/disable BK4819 selected interrupts
|
|
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
|
|
|
|
FUNCTION_Init();
|
|
|
|
if (switchToForeground)
|
|
FUNCTION_Select(FUNCTION_FOREGROUND);
|
|
}
|
|
|
|
#ifdef ENABLE_NOAA
|
|
void RADIO_ConfigureNOAA(void)
|
|
{
|
|
uint8_t ChanAB;
|
|
|
|
gUpdateStatus = true;
|
|
|
|
if (gEeprom.NOAA_AUTO_SCAN)
|
|
{
|
|
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
|
{
|
|
if (!IS_NOAA_CHANNEL(gEeprom.ScreenChannel[0]))
|
|
{
|
|
if (!IS_NOAA_CHANNEL(gEeprom.ScreenChannel[1]))
|
|
{
|
|
gIsNoaaMode = false;
|
|
return;
|
|
}
|
|
ChanAB = 1;
|
|
}
|
|
else
|
|
ChanAB = 0;
|
|
|
|
if (!gIsNoaaMode)
|
|
gNoaaChannel = gEeprom.VfoInfo[ChanAB].CHANNEL_SAVE - NOAA_CHANNEL_FIRST;
|
|
|
|
gIsNoaaMode = true;
|
|
return;
|
|
}
|
|
|
|
if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
|
{
|
|
gIsNoaaMode = true;
|
|
gNoaaChannel = gRxVfo->CHANNEL_SAVE - NOAA_CHANNEL_FIRST;
|
|
gNOAA_Countdown_10ms = NOAA_countdown_2_10ms;
|
|
gScheduleNOAA = false;
|
|
}
|
|
else
|
|
gIsNoaaMode = false;
|
|
}
|
|
else
|
|
gIsNoaaMode = false;
|
|
}
|
|
#endif
|
|
|
|
void RADIO_SetTxParameters(void)
|
|
{
|
|
BK4819_FilterBandwidth_t Bandwidth = gCurrentVfo->CHANNEL_BANDWIDTH;
|
|
|
|
AUDIO_AudioPathOff();
|
|
|
|
gEnableSpeaker = false;
|
|
|
|
BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_RX_ENABLE, false);
|
|
|
|
switch (Bandwidth)
|
|
{
|
|
default:
|
|
Bandwidth = BK4819_FILTER_BW_WIDE;
|
|
[[fallthrough]];
|
|
case BK4819_FILTER_BW_WIDE:
|
|
case BK4819_FILTER_BW_NARROW:
|
|
#ifdef ENABLE_AM_FIX
|
|
// BK4819_SetFilterBandwidth(Bandwidth, gCurrentVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
|
BK4819_SetFilterBandwidth(Bandwidth, true);
|
|
#else
|
|
BK4819_SetFilterBandwidth(Bandwidth, false);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
BK4819_SetFrequency(gCurrentVfo->pTX->Frequency);
|
|
|
|
// TX compressor
|
|
BK4819_SetCompander((gRxVfo->Modulation == MODULATION_FM && (gRxVfo->Compander == 1 || gRxVfo->Compander >= 3)) ? gRxVfo->Compander : 0);
|
|
|
|
BK4819_PrepareTransmit();
|
|
|
|
SYSTEM_DelayMs(10);
|
|
|
|
BK4819_PickRXFilterPathBasedOnFrequency(gCurrentVfo->pTX->Frequency);
|
|
|
|
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, true);
|
|
|
|
SYSTEM_DelayMs(5);
|
|
|
|
BK4819_SetupPowerAmplifier(gCurrentVfo->TXP_CalculatedSetting, gCurrentVfo->pTX->Frequency);
|
|
|
|
SYSTEM_DelayMs(10);
|
|
|
|
switch (gCurrentVfo->pTX->CodeType)
|
|
{
|
|
default:
|
|
case CODE_TYPE_OFF:
|
|
BK4819_ExitSubAu();
|
|
break;
|
|
|
|
case CODE_TYPE_CONTINUOUS_TONE:
|
|
BK4819_SetCTCSSFrequency(CTCSS_Options[gCurrentVfo->pTX->Code]);
|
|
break;
|
|
|
|
case CODE_TYPE_DIGITAL:
|
|
case CODE_TYPE_REVERSE_DIGITAL:
|
|
BK4819_SetCDCSSCodeWord(DCS_GetGolayCodeWord(gCurrentVfo->pTX->CodeType, gCurrentVfo->pTX->Code));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RADIO_SetModulation(ModulationMode_t modulation)
|
|
{
|
|
BK4819_AF_Type_t mod;
|
|
switch(modulation) {
|
|
default:
|
|
case MODULATION_FM:
|
|
mod = BK4819_AF_FM;
|
|
break;
|
|
case MODULATION_AM:
|
|
mod = BK4819_AF_AM;
|
|
break;
|
|
case MODULATION_USB:
|
|
mod = BK4819_AF_BASEBAND2;
|
|
break;
|
|
|
|
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
|
case MODULATION_BYP:
|
|
mod = BK4819_AF_UNKNOWN3;
|
|
break;
|
|
case MODULATION_RAW:
|
|
mod = BK4819_AF_BASEBAND1;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
BK4819_SetAF(mod);
|
|
|
|
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
|
|
BK4819_WriteRegister(BK4819_REG_3D, modulation == MODULATION_USB ? 0 : 0x2AAB);
|
|
BK4819_SetRegValue(afcDisableRegSpec, modulation != MODULATION_FM);
|
|
|
|
RADIO_SetupAGC(modulation == MODULATION_AM, false);
|
|
}
|
|
|
|
void RADIO_SetupAGC(bool listeningAM, bool disable)
|
|
{
|
|
static uint8_t lastSettings;
|
|
uint8_t newSettings = (listeningAM << 1) | (disable << 1);
|
|
if(lastSettings == newSettings)
|
|
return;
|
|
lastSettings = newSettings;
|
|
|
|
|
|
if(!listeningAM) { // if not actively listening AM we don't need any AM specific regulation
|
|
BK4819_SetAGC(!disable);
|
|
BK4819_InitAGC(false);
|
|
}
|
|
else {
|
|
#ifdef ENABLE_AM_FIX
|
|
if(gSetting_AM_fix) { // if AM fix active lock AGC so AM-fix can do it's job
|
|
BK4819_SetAGC(0);
|
|
AM_fix_enable(!disable);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
BK4819_SetAGC(!disable);
|
|
BK4819_InitAGC(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RADIO_SetVfoState(VfoState_t State)
|
|
{
|
|
if (State == VFO_STATE_NORMAL) {
|
|
VfoState[0] = VFO_STATE_NORMAL;
|
|
VfoState[1] = VFO_STATE_NORMAL;
|
|
} else if (State == VFO_STATE_VOLTAGE_HIGH) {
|
|
VfoState[0] = VFO_STATE_VOLTAGE_HIGH;
|
|
VfoState[1] = VFO_STATE_TX_DISABLE;
|
|
} else {
|
|
// 1of11
|
|
const unsigned int vfo = (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF) ? gEeprom.RX_VFO : gEeprom.TX_VFO;
|
|
VfoState[vfo] = State;
|
|
}
|
|
|
|
gVFOStateResumeCountdown_500ms = (State == VFO_STATE_NORMAL) ? 0 : vfo_state_resume_countdown_500ms;
|
|
gUpdateDisplay = true;
|
|
}
|
|
|
|
|
|
void RADIO_PrepareTX(void)
|
|
{
|
|
VfoState_t State = VFO_STATE_NORMAL; // default to OK to TX
|
|
|
|
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
|
{ // dual-RX is enabled
|
|
|
|
gDualWatchCountdown_10ms = dual_watch_count_after_tx_10ms;
|
|
gScheduleDualWatch = false;
|
|
|
|
if (!gRxVfoIsActive)
|
|
{ // use the current RX vfo
|
|
gEeprom.RX_VFO = gEeprom.TX_VFO;
|
|
gRxVfo = gTxVfo;
|
|
gRxVfoIsActive = true;
|
|
}
|
|
|
|
// let the user see that DW is not active
|
|
gDualWatchActive = false;
|
|
gUpdateStatus = true;
|
|
}
|
|
|
|
RADIO_SelectCurrentVfo();
|
|
|
|
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
|
|
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
|
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
|
#endif
|
|
){
|
|
// TX frequency not allowed
|
|
State = VFO_STATE_TX_DISABLE;
|
|
} else if (SerialConfigInProgress()) {
|
|
// TX is disabled or config upload/download in progress
|
|
State = VFO_STATE_TX_DISABLE;
|
|
} else if (gCurrentVfo->BUSY_CHANNEL_LOCK && gCurrentFunction == FUNCTION_RECEIVE) {
|
|
// busy RX'ing a station
|
|
State = VFO_STATE_BUSY;
|
|
} else if (gBatteryDisplayLevel == 0) {
|
|
// charge your battery !git co
|
|
State = VFO_STATE_BAT_LOW;
|
|
} else if (gBatteryDisplayLevel > 6) {
|
|
// over voltage .. this is being a pain
|
|
State = VFO_STATE_VOLTAGE_HIGH;
|
|
}
|
|
#ifndef ENABLE_TX_WHEN_AM
|
|
else if (gCurrentVfo->Modulation != MODULATION_FM) {
|
|
// not allowed to TX if in AM mode
|
|
State = VFO_STATE_TX_DISABLE;
|
|
}
|
|
#endif
|
|
|
|
if (State != VFO_STATE_NORMAL) {
|
|
// TX not allowed
|
|
RADIO_SetVfoState(State);
|
|
|
|
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
|
gAlarmState = ALARM_STATE_OFF;
|
|
#endif
|
|
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
|
#endif
|
|
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
|
|
return;
|
|
}
|
|
|
|
// TX is allowed
|
|
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
if (gDTMF_ReplyState == DTMF_REPLY_ANI)
|
|
{
|
|
gDTMF_IsTx = gDTMF_CallMode == DTMF_CALL_MODE_DTMF;
|
|
|
|
if (gDTMF_IsTx) {
|
|
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
|
gDTMF_TxStopCountdown_500ms = DTMF_txstop_countdown_500ms;
|
|
} else {
|
|
gDTMF_CallState = DTMF_CALL_STATE_CALL_OUT;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FUNCTION_Select(FUNCTION_TRANSMIT);
|
|
|
|
gTxTimerCountdown_500ms = 0; // no timeout
|
|
|
|
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
|
if (gAlarmState == ALARM_STATE_OFF)
|
|
#endif
|
|
{
|
|
if (gEeprom.TX_TIMEOUT_TIMER == 0)
|
|
gTxTimerCountdown_500ms = 60; // 30 sec
|
|
else if (gEeprom.TX_TIMEOUT_TIMER < (ARRAY_SIZE(gSubMenu_TOT) - 1))
|
|
gTxTimerCountdown_500ms = 120 * gEeprom.TX_TIMEOUT_TIMER; // minutes
|
|
else
|
|
gTxTimerCountdown_500ms = 120 * 15; // 15 minutes
|
|
}
|
|
|
|
gTxTimeoutReached = false;
|
|
gFlagEndTransmission = false;
|
|
gRTTECountdown = 0;
|
|
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
|
#endif
|
|
}
|
|
|
|
void RADIO_EnableCxCSS(void)
|
|
{
|
|
switch (gCurrentVfo->pTX->CodeType) {
|
|
case CODE_TYPE_DIGITAL:
|
|
case CODE_TYPE_REVERSE_DIGITAL:
|
|
BK4819_EnableCDCSS();
|
|
break;
|
|
default:
|
|
BK4819_EnableCTCSS();
|
|
break;
|
|
}
|
|
|
|
SYSTEM_DelayMs(200);
|
|
}
|
|
|
|
void RADIO_SendEndOfTransmission(void)
|
|
{
|
|
BK4819_PlayRoger();
|
|
DTMF_SendEndOfTransmission();
|
|
|
|
// send the CTCSS/DCS tail tone - allows the receivers to mute the usual FM squelch tail/crash
|
|
RADIO_EnableCxCSS();
|
|
RADIO_SetupRegisters(false);
|
|
}
|
|
|
|
void RADIO_PrepareCssTX(void)
|
|
{
|
|
RADIO_PrepareTX();
|
|
|
|
SYSTEM_DelayMs(200);
|
|
|
|
RADIO_EnableCxCSS();
|
|
RADIO_SetupRegisters(true);
|
|
}
|