mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
737 lines
22 KiB
C
737 lines
22 KiB
C
|
|
#include "app/app.h"
|
|
#include "app/chFrScanner.h"
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
#include "driver/systick.h"
|
|
#endif
|
|
#include "functions.h"
|
|
#include "misc.h"
|
|
#include "settings.h"
|
|
//#include "debugging.h"
|
|
|
|
int8_t gScanStateDir;
|
|
bool gScanKeepResult;
|
|
bool gScanPauseMode;
|
|
|
|
#ifdef ENABLE_SCAN_RANGES
|
|
uint32_t gScanRangeStart;
|
|
uint32_t gScanRangeStop;
|
|
#endif
|
|
|
|
typedef enum {
|
|
SCAN_NEXT_CHAN_SCANLIST1 = 0,
|
|
SCAN_NEXT_CHAN_SCANLIST2,
|
|
SCAN_NEXT_CHAN_DUAL_WATCH,
|
|
SCAN_NEXT_CHAN_MR,
|
|
SCAN_NEXT_NUM
|
|
} scan_next_chan_t;
|
|
|
|
scan_next_chan_t currentScanList;
|
|
uint32_t initialFrqOrChan;
|
|
uint8_t initialCROSS_BAND_RX_TX;
|
|
|
|
#ifndef ENABLE_FEAT_F4HWN
|
|
uint32_t lastFoundFrqOrChan;
|
|
#else
|
|
uint32_t lastFoundFrqOrChan;
|
|
uint32_t lastFoundFrqOrChanOld;
|
|
#endif
|
|
|
|
static void NextFreqChannel(void);
|
|
static void NextMemChannel(void);
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
#define SCAN_FAST_PRECHECK_STEPS 6
|
|
#define SCAN_FAST_RSSI_MARGIN 16
|
|
#define SCAN_FAST_SQUELCH_MARGIN 8
|
|
#define SCAN_FAST_FINE_STEP_LIMIT 250
|
|
#define SCAN_FAST_FINE_REFINE_SPAN 1000
|
|
#define SCAN_FAST_FINE_REFINE_MAX 80
|
|
#define SCAN_FAST_FINE_RSSI_DROP 8
|
|
#define SCAN_FAST_RSSI_MAX 65535u
|
|
// Settle loop guard: at most this many 1us waits while the BK4819 glitch
|
|
// indicator stays above SCAN_FAST_GLITCH_THRESHOLD. Caps the worst-case
|
|
// settling time per step at ~50us before we read the RSSI anyway.
|
|
#define SCAN_FAST_GLITCH_GUARD_MAX 50
|
|
#define SCAN_FAST_GLITCH_THRESHOLD 200
|
|
// HF/VHF boundary in Hz: BK4819_PickRXFilterPathBasedOnFrequency() switches
|
|
// the front-end filter path here, so we only re-run that (relatively
|
|
// expensive) call when we actually cross the boundary.
|
|
#define SCAN_FAST_HF_VHF_BOUNDARY_HZ 28000000u
|
|
|
|
typedef enum {
|
|
SCAN_FAST_DISABLED,
|
|
SCAN_FAST_QUIET_BATCH,
|
|
SCAN_FAST_CANDIDATE
|
|
} scan_fast_result_t;
|
|
|
|
static uint16_t scanFastReg30;
|
|
static uint16_t scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
|
static uint32_t scanFastPrevFrequency;
|
|
static bool scanFastLastFullTuneCandidate;
|
|
|
|
static void ScanFastResetState(void)
|
|
{
|
|
// Called on every scan (re)start, after a reception, and on each
|
|
// wraparound to the start of the channel list / range. The noise
|
|
// floor is re-warmed up from current conditions instead of carrying
|
|
// stale calibration into a later sweep.
|
|
scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
|
scanFastPrevFrequency = 0;
|
|
scanFastLastFullTuneCandidate = false;
|
|
}
|
|
|
|
static uint16_t ScanFastReadRssi(void)
|
|
{
|
|
uint8_t guard = SCAN_FAST_GLITCH_GUARD_MAX;
|
|
while (guard-- && BK4819_GetGlitchIndicator() >= SCAN_FAST_GLITCH_THRESHOLD)
|
|
{
|
|
SYSTICK_DelayUs(1);
|
|
}
|
|
|
|
// Discard first read: after fast tuning the RSSI/AGC value may still be stale.
|
|
BK4819_GetRSSI();
|
|
return BK4819_GetRSSI();
|
|
}
|
|
|
|
static void ScanFastTune(uint32_t frequency)
|
|
{
|
|
if (scanFastPrevFrequency == 0 ||
|
|
((frequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ) !=
|
|
(scanFastPrevFrequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ)))
|
|
{
|
|
BK4819_PickRXFilterPathBasedOnFrequency(frequency);
|
|
}
|
|
|
|
scanFastPrevFrequency = frequency;
|
|
BK4819_SetFrequency(frequency);
|
|
BK4819_WriteRegister(BK4819_REG_30, 0);
|
|
BK4819_WriteRegister(BK4819_REG_30, scanFastReg30);
|
|
}
|
|
|
|
static bool ScanFastIsCandidate(uint16_t rssi)
|
|
{
|
|
if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX)
|
|
{
|
|
scanFastNoiseFloor = rssi;
|
|
return false;
|
|
}
|
|
|
|
const uint16_t noiseTrigger =
|
|
(scanFastNoiseFloor > SCAN_FAST_RSSI_MAX - SCAN_FAST_RSSI_MARGIN)
|
|
? SCAN_FAST_RSSI_MAX
|
|
: (uint16_t)(scanFastNoiseFloor + SCAN_FAST_RSSI_MARGIN);
|
|
const uint16_t squelchTrigger =
|
|
(gRxVfo->SquelchOpenRSSIThresh > SCAN_FAST_SQUELCH_MARGIN)
|
|
? (uint16_t)(gRxVfo->SquelchOpenRSSIThresh - SCAN_FAST_SQUELCH_MARGIN)
|
|
: 0;
|
|
|
|
if (rssi >= noiseTrigger && rssi >= squelchTrigger)
|
|
return true;
|
|
|
|
if (rssi < scanFastNoiseFloor)
|
|
scanFastNoiseFloor = rssi;
|
|
else
|
|
scanFastNoiseFloor = (uint16_t)((7u * scanFastNoiseFloor + rssi + 4u) >> 3);
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_SCAN_RANGES)
|
|
static void ScanRangeFastRefineCandidate(uint16_t firstRssi)
|
|
{
|
|
const uint16_t step = gRxVfo->StepFrequency;
|
|
if (step == 0 || step >= SCAN_FAST_FINE_STEP_LIMIT)
|
|
return;
|
|
|
|
uint16_t maxSteps = SCAN_FAST_FINE_REFINE_SPAN / step;
|
|
if (maxSteps == 0)
|
|
maxSteps = 1;
|
|
if (maxSteps > SCAN_FAST_FINE_REFINE_MAX)
|
|
maxSteps = SCAN_FAST_FINE_REFINE_MAX;
|
|
|
|
uint16_t bestRssi = firstRssi;
|
|
uint32_t bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
|
uint8_t fallingSteps = 0;
|
|
|
|
for (uint16_t i = 0; i < maxSteps; ++i)
|
|
{
|
|
const uint32_t prevRxFrequency = gRxVfo->pRX->Frequency;
|
|
|
|
gRxVfo->freq_config_RX.Frequency =
|
|
APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
|
RADIO_ApplyOffset(gRxVfo);
|
|
|
|
const uint32_t freq = gRxVfo->pRX->Frequency;
|
|
if ((gScanStateDir > 0 && freq < prevRxFrequency) ||
|
|
(gScanStateDir < 0 && freq > prevRxFrequency))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ScanFastTune(freq);
|
|
|
|
const uint16_t rssi = ScanFastReadRssi();
|
|
if (rssi > bestRssi)
|
|
{
|
|
bestRssi = rssi;
|
|
bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
|
fallingSteps = 0;
|
|
}
|
|
else if (bestRssi > rssi && bestRssi - rssi >= SCAN_FAST_FINE_RSSI_DROP)
|
|
{
|
|
if (++fallingSteps >= 3)
|
|
break;
|
|
}
|
|
}
|
|
|
|
gRxVfo->freq_config_RX.Frequency = bestFrequency;
|
|
RADIO_ApplyOffset(gRxVfo);
|
|
ScanFastTune(gRxVfo->pRX->Frequency);
|
|
}
|
|
|
|
static scan_fast_result_t ScanRangeFastPrecheck(void)
|
|
{
|
|
if (!gScanRangeStart)
|
|
return SCAN_FAST_DISABLED;
|
|
|
|
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
|
return SCAN_FAST_DISABLED;
|
|
|
|
// Mute AF DAC during the precheck sweep: avoids audio glitches and
|
|
// saves a few uA on each silent step. The bit is restored on the real
|
|
// tune by RADIO_SetupRegisters() in NextFreqChannel().
|
|
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
|
|
|
for (uint8_t i = 0; i < SCAN_FAST_PRECHECK_STEPS; ++i)
|
|
{
|
|
gRxVfo->freq_config_RX.Frequency =
|
|
APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
|
RADIO_ApplyOffset(gRxVfo);
|
|
|
|
const uint32_t freq = gRxVfo->pRX->Frequency;
|
|
|
|
// Detect wraparound: scanning forward but the new freq is lower
|
|
// than the previous one (or scanning backward but it's higher)
|
|
// means the range has wrapped from stop back to start. Reset the
|
|
// noise floor so the new pass re-warms up from current conditions.
|
|
if (scanFastPrevFrequency != 0 &&
|
|
((gScanStateDir > 0 && freq < scanFastPrevFrequency) ||
|
|
(gScanStateDir < 0 && freq > scanFastPrevFrequency)))
|
|
{
|
|
ScanFastResetState();
|
|
}
|
|
|
|
ScanFastTune(freq);
|
|
|
|
const uint16_t rssi = ScanFastReadRssi();
|
|
if (ScanFastIsCandidate(rssi))
|
|
{
|
|
ScanRangeFastRefineCandidate(rssi);
|
|
return SCAN_FAST_CANDIDATE;
|
|
}
|
|
}
|
|
|
|
return SCAN_FAST_QUIET_BATCH;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
static bool MemChannelFastPrecheck(uint16_t channel)
|
|
{
|
|
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
|
{
|
|
scanFastLastFullTuneCandidate = false;
|
|
return true;
|
|
}
|
|
|
|
const uint32_t frequency = SETTINGS_FetchChannelFrequency(channel);
|
|
if (frequency == 0 || frequency == 0xFFFFFFFF)
|
|
{
|
|
scanFastLastFullTuneCandidate = false;
|
|
return true;
|
|
}
|
|
|
|
// Mute AF DAC for the same reason as in ScanRangeFastPrecheck().
|
|
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
|
ScanFastTune(frequency);
|
|
|
|
if (ScanFastIsCandidate(ScanFastReadRssi()))
|
|
{
|
|
scanFastLastFullTuneCandidate = true;
|
|
return true; // signal detected: let the full tune path follow
|
|
}
|
|
|
|
// No signal here: still mirror the probed frequency in the VFO so the
|
|
// status line (channel name + frequency) keeps in sync as we skip.
|
|
// RADIO_ConfigureChannel() will overwrite these values cleanly when a
|
|
// candidate is eventually retained.
|
|
scanFastLastFullTuneCandidate = false;
|
|
gRxVfo->freq_config_RX.Frequency = frequency;
|
|
return false;
|
|
}
|
|
|
|
static void AdvanceMemScanList(const bool enabled)
|
|
{
|
|
if (enabled)
|
|
if (++currentScanList >= SCAN_NEXT_NUM)
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
|
}
|
|
|
|
static void SetMemScanProgressChannel(uint16_t channel)
|
|
{
|
|
gEeprom.MrChannel[ gEeprom.RX_VFO] = channel;
|
|
gEeprom.ScreenChannel[gEeprom.RX_VFO] = channel;
|
|
gRxVfo->CHANNEL_SAVE = channel;
|
|
}
|
|
#endif
|
|
|
|
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
|
|
void CHFRSCANNER_ScanRange(void) {
|
|
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
|
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
ScanFastResetState();
|
|
#endif
|
|
if(gScanRangeStart > gScanRangeStop)
|
|
SWAP(gScanRangeStart, gScanRangeStop);
|
|
}
|
|
#endif
|
|
|
|
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction)
|
|
{
|
|
if (storeBackupSettings) {
|
|
initialCROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
|
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
|
gScanKeepResult = false;
|
|
}
|
|
|
|
RADIO_SelectVfos();
|
|
|
|
gNextMrChannel = gRxVfo->CHANNEL_SAVE;
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
|
gScanStateDir = scan_direction;
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
ScanFastResetState();
|
|
#endif
|
|
|
|
if (IS_MR_CHANNEL(gNextMrChannel))
|
|
{
|
|
|
|
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
|
RADIO_NextValidList(1);
|
|
}
|
|
|
|
// channel mode
|
|
if (storeBackupSettings) {
|
|
initialFrqOrChan = gRxVfo->CHANNEL_SAVE;
|
|
lastFoundFrqOrChan = initialFrqOrChan;
|
|
}
|
|
NextMemChannel();
|
|
}
|
|
else
|
|
{ // frequency mode
|
|
if (storeBackupSettings) {
|
|
initialFrqOrChan = gRxVfo->freq_config_RX.Frequency;
|
|
lastFoundFrqOrChan = initialFrqOrChan;
|
|
}
|
|
NextFreqChannel();
|
|
}
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN
|
|
lastFoundFrqOrChanOld = lastFoundFrqOrChan;
|
|
#endif
|
|
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_2_10ms;
|
|
gScheduleScanListen = false;
|
|
gRxReceptionMode = RX_MODE_NONE;
|
|
gScanPauseMode = false;
|
|
}
|
|
|
|
void CHFRSCANNER_ManualResume(const int8_t scan_direction)
|
|
{
|
|
if (FUNCTION_IsRx())
|
|
{
|
|
// Abort the current reception before a user-forced scan step, otherwise
|
|
// HandleReceive() can re-apply the carrier resume delay on the next tick.
|
|
gMonitor = false;
|
|
gRxReceptionMode = RX_MODE_NONE;
|
|
gScanPauseMode = false;
|
|
|
|
FUNCTION_Init();
|
|
FUNCTION_Select(FUNCTION_FOREGROUND);
|
|
}
|
|
|
|
CHFRSCANNER_Start(false, scan_direction);
|
|
|
|
gScanPauseDelayIn_10ms = 1;
|
|
gScheduleScanListen = false;
|
|
}
|
|
|
|
/*
|
|
void CHFRSCANNER_ContinueScanning(void)
|
|
{
|
|
if (IS_FREQ_CHANNEL(gNextMrChannel))
|
|
{
|
|
if (gCurrentFunction == FUNCTION_INCOMING)
|
|
APP_StartListening(gMonitor ? FUNCTION_MONITOR : FUNCTION_RECEIVE);
|
|
else
|
|
NextFreqChannel(); // switch to next frequency
|
|
}
|
|
else
|
|
{
|
|
if (gCurrentCodeType == CODE_TYPE_OFF && gCurrentFunction == FUNCTION_INCOMING)
|
|
APP_StartListening(gMonitor ? FUNCTION_MONITOR : FUNCTION_RECEIVE);
|
|
else
|
|
NextMemChannel(); // switch to next channel
|
|
}
|
|
|
|
gScanPauseMode = false;
|
|
gRxReceptionMode = RX_MODE_NONE;
|
|
gScheduleScanListen = false;
|
|
}
|
|
*/
|
|
|
|
void CHFRSCANNER_ContinueScanning(void)
|
|
{
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
if (scanFastLastFullTuneCandidate &&
|
|
gCurrentFunction != FUNCTION_INCOMING &&
|
|
!g_SquelchLost)
|
|
{
|
|
// A rejected full-tune candidate is just a false RSSI hit. Keep the
|
|
// learned floor; resetting here can make the next channel blind when
|
|
// it is the real signal, especially while scanning down.
|
|
scanFastLastFullTuneCandidate = false;
|
|
}
|
|
#endif
|
|
|
|
if (gCurrentFunction == FUNCTION_INCOMING &&
|
|
(IS_FREQ_CHANNEL(gNextMrChannel) || gCurrentCodeType == CODE_TYPE_OFF))
|
|
{
|
|
APP_StartListening(gMonitor ? FUNCTION_MONITOR : FUNCTION_RECEIVE);
|
|
}
|
|
else
|
|
{
|
|
IS_FREQ_CHANNEL(gNextMrChannel) ? NextFreqChannel() : NextMemChannel();
|
|
}
|
|
|
|
gScanPauseMode = false;
|
|
gRxReceptionMode = RX_MODE_NONE;
|
|
gScheduleScanListen = false;
|
|
}
|
|
|
|
void CHFRSCANNER_Found(void)
|
|
{
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
// After a real reception the BK4819 AGC has shifted, biasing the next
|
|
// few RSSI readings high. Reset the precheck state so it warms up from
|
|
// the current noise floor instead of carrying stale calibration into
|
|
// the post-reception scan, which otherwise turns nearly every channel
|
|
// into a CANDIDATE and erases the speed gain.
|
|
ScanFastResetState();
|
|
#endif
|
|
|
|
if (gEeprom.SCAN_RESUME_MODE > 80) {
|
|
if (!gScanPauseMode) {
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_5_10ms * (gEeprom.SCAN_RESUME_MODE - 80) * 5;
|
|
gScanPauseMode = true;
|
|
}
|
|
} else {
|
|
gScanPauseDelayIn_10ms = 0;
|
|
}
|
|
|
|
// gScheduleScanListen is always false...
|
|
gScheduleScanListen = false;
|
|
|
|
/*
|
|
if(gEeprom.SCAN_RESUME_MODE > 1 && gEeprom.SCAN_RESUME_MODE < 26)
|
|
{
|
|
if (!gScanPauseMode)
|
|
{
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_5_10ms * (gEeprom.SCAN_RESUME_MODE - 1) * 5;
|
|
gScheduleScanListen = false;
|
|
gScanPauseMode = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gScanPauseDelayIn_10ms = 0;
|
|
gScheduleScanListen = false;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
switch (gEeprom.SCAN_RESUME_MODE)
|
|
{
|
|
case SCAN_RESUME_TO:
|
|
if (!gScanPauseMode)
|
|
{
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_1_10ms;
|
|
gScheduleScanListen = false;
|
|
gScanPauseMode = true;
|
|
}
|
|
break;
|
|
|
|
case SCAN_RESUME_CO:
|
|
case SCAN_RESUME_SE:
|
|
gScanPauseDelayIn_10ms = 0;
|
|
gScheduleScanListen = false;
|
|
break;
|
|
}
|
|
*/
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN
|
|
lastFoundFrqOrChanOld = lastFoundFrqOrChan;
|
|
#endif
|
|
|
|
if (IS_MR_CHANNEL(gRxVfo->CHANNEL_SAVE)) { //memory scan
|
|
lastFoundFrqOrChan = gRxVfo->CHANNEL_SAVE;
|
|
}
|
|
else { // frequency scan
|
|
lastFoundFrqOrChan = gRxVfo->freq_config_RX.Frequency;
|
|
}
|
|
|
|
|
|
gScanKeepResult = true;
|
|
}
|
|
|
|
void CHFRSCANNER_Stop(void)
|
|
{
|
|
if(initialCROSS_BAND_RX_TX != CROSS_BAND_OFF) {
|
|
gEeprom.CROSS_BAND_RX_TX = initialCROSS_BAND_RX_TX;
|
|
initialCROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
|
}
|
|
|
|
gScanStateDir = SCAN_OFF;
|
|
|
|
const uint32_t chFr = gScanKeepResult ? lastFoundFrqOrChan : initialFrqOrChan;
|
|
const bool channelChanged = chFr != initialFrqOrChan;
|
|
if (IS_MR_CHANNEL(gNextMrChannel)) {
|
|
gEeprom.MrChannel[gEeprom.RX_VFO] = chFr;
|
|
gEeprom.ScreenChannel[gEeprom.RX_VFO] = chFr;
|
|
RADIO_ConfigureChannel(gEeprom.RX_VFO, VFO_CONFIGURE_RELOAD);
|
|
|
|
if(channelChanged) {
|
|
SETTINGS_SaveVfoIndices();
|
|
gUpdateStatus = true;
|
|
}
|
|
}
|
|
else {
|
|
gRxVfo->freq_config_RX.Frequency = chFr;
|
|
RADIO_ApplyOffset(gRxVfo);
|
|
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
|
if(channelChanged) {
|
|
SETTINGS_SaveChannel(gRxVfo->CHANNEL_SAVE, gEeprom.RX_VFO, gRxVfo, 1);
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
|
gEeprom.CURRENT_STATE = 0;
|
|
SETTINGS_WriteCurrentState();
|
|
#endif
|
|
|
|
RADIO_SetupRegisters(true);
|
|
gUpdateDisplay = true;
|
|
}
|
|
|
|
static void NextFreqChannel(void)
|
|
{
|
|
#ifdef ENABLE_SCAN_RANGES
|
|
if(gScanRangeStart) {
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
const scan_fast_result_t fastResult = ScanRangeFastPrecheck();
|
|
|
|
if (fastResult == SCAN_FAST_QUIET_BATCH)
|
|
{
|
|
scanFastLastFullTuneCandidate = false;
|
|
gScanPauseDelayIn_10ms = 1;
|
|
gUpdateDisplay = true;
|
|
return;
|
|
}
|
|
|
|
if (fastResult == SCAN_FAST_DISABLED)
|
|
{
|
|
scanFastLastFullTuneCandidate = false;
|
|
gRxVfo->freq_config_RX.Frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
|
}
|
|
else
|
|
{
|
|
scanFastLastFullTuneCandidate = true;
|
|
}
|
|
#else
|
|
gRxVfo->freq_config_RX.Frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
scanFastLastFullTuneCandidate = false;
|
|
#endif
|
|
gRxVfo->freq_config_RX.Frequency = APP_SetFrequencyByStep(gRxVfo, gScanStateDir);
|
|
}
|
|
|
|
RADIO_ApplyOffset(gRxVfo);
|
|
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
|
RADIO_SetupRegisters(true);
|
|
|
|
#ifdef ENABLE_FASTER_CHANNEL_SCAN
|
|
gScanPauseDelayIn_10ms = 9; // 90ms
|
|
#else
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_6_10ms;
|
|
#endif
|
|
|
|
gUpdateDisplay = true;
|
|
}
|
|
|
|
static void NextMemChannel(void)
|
|
{
|
|
static uint16_t prev_mr_chan = 0;
|
|
const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1) ? gEeprom.SCAN_LIST_ENABLED : true;
|
|
const int16_t chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[0] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[0] : -1;
|
|
const int16_t chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[1] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[1] : -1;
|
|
const uint16_t prev_chan = gNextMrChannel;
|
|
uint16_t chan = 0;
|
|
|
|
//char str[64] = "";
|
|
|
|
if (enabled)
|
|
{
|
|
switch (currentScanList)
|
|
{
|
|
case SCAN_NEXT_CHAN_SCANLIST1:
|
|
prev_mr_chan = gNextMrChannel;
|
|
|
|
//sprintf(str, "-> Chan1 %d\n", chan1 + 1);
|
|
//LogUart(str);
|
|
|
|
if (chan1 >= 0)
|
|
{
|
|
if (RADIO_CheckValidChannel(chan1, false, gEeprom.SCAN_LIST_DEFAULT))
|
|
{
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
|
gNextMrChannel = chan1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
[[fallthrough]];
|
|
case SCAN_NEXT_CHAN_SCANLIST2:
|
|
|
|
//sprintf(str, "-> Chan2 %d\n", chan2 + 1);
|
|
//LogUart(str);
|
|
|
|
if (chan2 >= 0)
|
|
{
|
|
if (RADIO_CheckValidChannel(chan2, false, gEeprom.SCAN_LIST_DEFAULT))
|
|
{
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST2;
|
|
gNextMrChannel = chan2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
[[fallthrough]];
|
|
/*
|
|
case SCAN_NEXT_CHAN_SCANLIST3:
|
|
if (chan3 >= 0)
|
|
{
|
|
if (RADIO_CheckValidChannel(chan3, false, 0))
|
|
{
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST3;
|
|
gNextMrChannel = chan3;
|
|
break;
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
*/
|
|
// this bit doesn't yet work if the other VFO is a frequency
|
|
case SCAN_NEXT_CHAN_DUAL_WATCH:
|
|
// dual watch is enabled - include the other VFO in the scan
|
|
// if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
|
// {
|
|
// chan = (gEeprom.RX_VFO + 1) & 1u;
|
|
// chan = gEeprom.ScreenChannel[chan];
|
|
// if (IS_MR_CHANNEL(chan))
|
|
// {
|
|
// currentScanList = SCAN_NEXT_CHAN_DUAL_WATCH;
|
|
// gNextMrChannel = chan;
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
default:
|
|
case SCAN_NEXT_CHAN_MR:
|
|
currentScanList = SCAN_NEXT_CHAN_MR;
|
|
gNextMrChannel = prev_mr_chan;
|
|
chan = 0xFFFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!enabled || chan == 0xFFFF)
|
|
{
|
|
chan = RADIO_FindNextChannel(gNextMrChannel + gScanStateDir, gScanStateDir, true, gEeprom.SCAN_LIST_DEFAULT);
|
|
if (chan == 0xFFFF)
|
|
{ // no valid channel found -> wrapping back to the first channel
|
|
chan = MR_CHANNEL_FIRST;
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
// Wraparound: re-warm the precheck noise floor on the new pass
|
|
// so it tracks current RF conditions instead of an EMA that
|
|
// accumulated drift over the previous full sweep.
|
|
ScanFastResetState();
|
|
#endif
|
|
}
|
|
|
|
gNextMrChannel = chan;
|
|
|
|
//sprintf(str, "----> Chan %d\n", chan + 1);
|
|
//LogUart(str);
|
|
}
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
SetMemScanProgressChannel(gNextMrChannel);
|
|
|
|
if (!MemChannelFastPrecheck(gNextMrChannel))
|
|
{
|
|
gScanPauseDelayIn_10ms = 1;
|
|
gUpdateDisplay = true;
|
|
AdvanceMemScanList(enabled);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (gNextMrChannel != prev_chan
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
|| scanFastLastFullTuneCandidate
|
|
#endif
|
|
)
|
|
{
|
|
#ifndef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
gEeprom.MrChannel[ gEeprom.RX_VFO] = gNextMrChannel;
|
|
gEeprom.ScreenChannel[gEeprom.RX_VFO] = gNextMrChannel;
|
|
#endif
|
|
|
|
RADIO_ConfigureChannel(gEeprom.RX_VFO, VFO_CONFIGURE_RELOAD);
|
|
RADIO_SetupRegisters(true);
|
|
|
|
gUpdateDisplay = true;
|
|
}
|
|
|
|
#ifdef ENABLE_FASTER_CHANNEL_SCAN
|
|
gScanPauseDelayIn_10ms = 9; // 90ms .. <= ~60ms it misses signals (squelch response and/or PLL lock time) ?
|
|
#else
|
|
gScanPauseDelayIn_10ms = scan_pause_delay_in_3_10ms;
|
|
#endif
|
|
|
|
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
|
AdvanceMemScanList(enabled);
|
|
#else
|
|
if (enabled)
|
|
if (++currentScanList >= SCAN_NEXT_NUM)
|
|
currentScanList = SCAN_NEXT_CHAN_SCANLIST1; // back round we go
|
|
#endif
|
|
}
|