From 3e6b169b7b50b3aaafca9b9800a639e72d618ebb Mon Sep 17 00:00:00 2001 From: Armel FAUVEAU Date: Sun, 3 May 2026 22:12:54 +0200 Subject: [PATCH] Improve fast scan detection and display metadata --- App/app/chFrScanner.c | 149 +++++++++++++++++++++++++++++++++++++----- App/app/chFrScanner.h | 5 ++ App/radio.h | 1 + App/settings.c | 93 ++++++++++++++++++++++++++ App/settings.h | 20 ++++++ App/ui/main.c | 17 +++-- 6 files changed, 264 insertions(+), 21 deletions(-) diff --git a/App/app/chFrScanner.c b/App/app/chFrScanner.c index e03b28d1..8518466a 100644 --- a/App/app/chFrScanner.c +++ b/App/app/chFrScanner.c @@ -1,4 +1,6 @@ +#include + #include "app/app.h" #include "app/chFrScanner.h" #include "audio.h" @@ -61,6 +63,8 @@ static void CHFRSCANNER_AbortActiveReception(void) #define SCAN_FAST_PRECHECK_STEPS 6 #define SCAN_FAST_RSSI_MARGIN 16 #define SCAN_FAST_SQUELCH_MARGIN 8 +#define SCAN_FAST_WEAK_MARGIN 8 +#define SCAN_FAST_RECHECK_DELAY_US 350 #define SCAN_FAST_FINE_STEP_LIMIT 250 #define SCAN_FAST_FINE_REFINE_SPAN 1000 #define SCAN_FAST_FINE_REFINE_MAX 80 @@ -86,6 +90,8 @@ static uint16_t scanFastReg30; static uint16_t scanFastNoiseFloor = SCAN_FAST_RSSI_MAX; static uint32_t scanFastPrevFrequency; static bool scanFastLastFullTuneCandidate; +static VFO_Info_t scanFastDisplayVfo; +static bool scanFastDisplayVfoValid; static void ScanFastResetState(void) { @@ -96,6 +102,7 @@ static void ScanFastResetState(void) scanFastNoiseFloor = SCAN_FAST_RSSI_MAX; scanFastPrevFrequency = 0; scanFastLastFullTuneCandidate = false; + scanFastDisplayVfoValid = false; } static void ScanFastResetNoiseFloor(void) @@ -131,25 +138,133 @@ static void ScanFastTune(uint32_t frequency) BK4819_WriteRegister(BK4819_REG_30, scanFastReg30); } -static bool ScanFastIsCandidate(uint16_t rssi) +const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void) { - if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX) + if (!scanFastDisplayVfoValid || gScanStateDir == SCAN_OFF || FUNCTION_IsRx()) + return NULL; + + return &scanFastDisplayVfo; +} + +static bool ScanFastUpdateDisplayVfo(uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation) +{ + ChannelScanDisplayInfo_t info; + + if (!SETTINGS_FetchChannelScanDisplayInfo(channel, &info)) { - scanFastNoiseFloor = rssi; + scanFastDisplayVfoValid = false; 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; + scanFastDisplayVfo = gEeprom.VfoInfo[gEeprom.RX_VFO]; - if (rssi >= noiseTrigger && rssi >= squelchTrigger) + scanFastDisplayVfo.CHANNEL_SAVE = channel; + scanFastDisplayVfo.freq_config_RX = info.rx; + scanFastDisplayVfo.freq_config_TX = info.tx; + scanFastDisplayVfo.TX_OFFSET_FREQUENCY = info.offset; + scanFastDisplayVfo.StepFrequency = info.stepFrequency; + scanFastDisplayVfo.STEP_SETTING = info.stepSetting; + scanFastDisplayVfo.Modulation = info.modulation; + scanFastDisplayVfo.TX_OFFSET_FREQUENCY_DIRECTION = info.txOffsetFrequencyDirection; + scanFastDisplayVfo.OUTPUT_POWER = info.outputPower; + scanFastDisplayVfo.FrequencyReverse = info.frequencyReverse; + scanFastDisplayVfo.CHANNEL_BANDWIDTH = info.channelBandwidth; + scanFastDisplayVfo.BUSY_CHANNEL_LOCK = info.busyChannelLock; + scanFastDisplayVfo.TX_LOCK = info.txLock; +#ifdef ENABLE_DTMF_CALLING + scanFastDisplayVfo.DTMF_DECODING_ENABLE = info.dtmfDecodingEnable; +#endif + scanFastDisplayVfo.DTMF_PTT_ID_TX_MODE = info.dtmfPttIdTxMode; + + if (!scanFastDisplayVfo.FrequencyReverse) + { + scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_RX; + scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_TX; + } + else + { + scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_TX; + scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_RX; + } + + scanFastDisplayVfoValid = true; + + if (frequency) + *frequency = info.rx.Frequency; + if (modulation) + *modulation = info.modulation; + + return true; +} + +static uint16_t ScanFastSaturatingAdd(uint16_t value, uint16_t add) +{ + return (value > SCAN_FAST_RSSI_MAX - add) ? SCAN_FAST_RSSI_MAX : (uint16_t)(value + add); +} + +static uint16_t ScanFastSaturatingSub(uint16_t value, uint16_t sub) +{ + return (value > sub) ? (uint16_t)(value - sub) : 0; +} + +static uint16_t ScanFastGetNoiseTrigger(void) +{ + return ScanFastSaturatingAdd(scanFastNoiseFloor, SCAN_FAST_RSSI_MARGIN); +} + +static uint16_t ScanFastGetSquelchTrigger(void) +{ + return ScanFastSaturatingSub(gRxVfo->SquelchOpenRSSIThresh, SCAN_FAST_SQUELCH_MARGIN); +} + +static bool ScanFastIsNearCandidate(uint16_t rssi) +{ + const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN); + const uint16_t squelchTrigger = ScanFastGetSquelchTrigger(); + + if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX) + return gRxVfo->SquelchOpenRSSIThresh > 0 && + rssiWithMargin >= gRxVfo->SquelchOpenRSSIThresh; + + return rssiWithMargin >= ScanFastGetNoiseTrigger() && + rssiWithMargin >= squelchTrigger; +} + +static uint16_t ScanFastReadCandidateRssi(void) +{ + uint16_t rssi = ScanFastReadRssi(); + + if (ScanFastIsNearCandidate(rssi)) + { + SYSTICK_DelayUs(SCAN_FAST_RECHECK_DELAY_US); + + const uint16_t retryRssi = ScanFastReadRssi(); + if (retryRssi > rssi) + rssi = retryRssi; + } + + return rssi; +} + +static bool ScanFastIsCandidate(uint16_t rssi) +{ + const uint16_t squelchTrigger = ScanFastGetSquelchTrigger(); + + if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX) + { + scanFastNoiseFloor = rssi; + return gRxVfo->SquelchOpenRSSIThresh > 0 && + rssi >= gRxVfo->SquelchOpenRSSIThresh; + } + + const uint16_t noiseTrigger = ScanFastGetNoiseTrigger(); + const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN); + + if ((rssi >= noiseTrigger && rssi >= squelchTrigger) || + (rssiWithMargin >= noiseTrigger && rssiWithMargin >= squelchTrigger)) + { return true; + } if (rssi < scanFastNoiseFloor) scanFastNoiseFloor = rssi; @@ -284,7 +399,7 @@ static scan_fast_result_t ScanRangeFastPrecheck(void) ScanFastTune(freq); - const uint16_t rssi = ScanFastReadRssi(); + const uint16_t rssi = ScanFastReadCandidateRssi(); if (ScanFastIsCandidate(rssi)) { ScanRangeFastRefineCandidate(rssi); @@ -308,7 +423,7 @@ static bool MemChannelFastPrecheck(uint16_t channel) return true; } - if (!SETTINGS_FetchChannelScanInfo(channel, &frequency, &modulation)) + if (!ScanFastUpdateDisplayVfo(channel, &frequency, &modulation)) { scanFastLastFullTuneCandidate = false; return true; @@ -320,7 +435,7 @@ static bool MemChannelFastPrecheck(uint16_t channel) scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC; ScanFastTune(frequency); - if (ScanFastIsCandidate(ScanFastReadRssi())) + if (ScanFastIsCandidate(ScanFastReadCandidateRssi())) { scanFastLastFullTuneCandidate = true; return true; // signal detected: let the full tune path follow @@ -419,7 +534,9 @@ void CHFRSCANNER_ManualResume(const int8_t scan_direction) { CHFRSCANNER_Start(false, scan_direction); - gScanPauseDelayIn_10ms = 1; + gScanPauseDelayIn_10ms = (gRxVfo->SquelchOpenRSSIThresh == 0) + ? scan_pause_delay_in_3_10ms + : 1; gScheduleScanListen = false; } diff --git a/App/app/chFrScanner.h b/App/app/chFrScanner.h index 84e36348..26ca3ef9 100644 --- a/App/app/chFrScanner.h +++ b/App/app/chFrScanner.h @@ -4,6 +4,8 @@ #include #include +#include "radio.h" + // scan direction, if not equal SCAN_OFF indicates // that we are in a process of scanning channels/frequencies extern int8_t gScanStateDir; @@ -20,6 +22,9 @@ void CHFRSCANNER_Stop(void); void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction); void CHFRSCANNER_ManualResume(const int8_t scan_direction); void CHFRSCANNER_ContinueScanning(void); +#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER +const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void); +#endif #if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES) void CHFRSCANNER_ScanRange(void); diff --git a/App/radio.h b/App/radio.h index 8fa28fa3..e6ae7f01 100644 --- a/App/radio.h +++ b/App/radio.h @@ -156,6 +156,7 @@ uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScan void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency); void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure); void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo); +void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp); void RADIO_ApplyOffset(VFO_Info_t *pInfo); void RADIO_SelectVfos(void); void RADIO_SetupRegisters(bool switchToForeground); diff --git a/App/settings.c b/App/settings.c index ecbf9e9e..6e993032 100644 --- a/App/settings.c +++ b/App/settings.c @@ -604,6 +604,99 @@ bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, return info.frequency != 0 && info.frequency != 0xFFFFFFFF; } +bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info) +{ + if (info == NULL) + return false; + + struct + { + uint32_t frequency; + uint32_t offset; + uint8_t data[8]; + } __attribute__((packed)) raw; + + PY25Q16_ReadBuffer(channel * 16, &raw, sizeof(raw)); + + if (raw.frequency == 0 || raw.frequency == 0xFFFFFFFF) + return false; + + memset(info, 0, sizeof(*info)); + + info->rx.Frequency = raw.frequency; + info->tx.Frequency = raw.frequency; + info->offset = (raw.offset >= _1GHz_in_KHz) ? (_1GHz_in_KHz / 100) : raw.offset; + + info->rx.CodeType = (raw.data[2] >> 0) & 0x0F; + info->tx.CodeType = (raw.data[2] >> 4) & 0x0F; + RADIO_ValidateAndSetCode(&info->rx, raw.data[0]); + RADIO_ValidateAndSetCode(&info->tx, raw.data[1]); + + uint8_t tmp = raw.data[3] & 0x0F; + if (tmp > TX_OFFSET_FREQUENCY_DIRECTION_SUB) + tmp = TX_OFFSET_FREQUENCY_DIRECTION_OFF; + info->txOffsetFrequencyDirection = tmp; + + tmp = raw.data[3] >> 4; + if (tmp >= MODULATION_UKNOWN) + tmp = MODULATION_FM; + info->modulation = (ModulationMode_t)tmp; + + tmp = raw.data[6]; + if (tmp >= STEP_N_ELEM) + tmp = STEP_12_5kHz; + info->stepSetting = (STEP_Setting_t)tmp; + info->stepFrequency = gStepFrequencyTable[tmp]; + + if (raw.data[4] == 0xFF) + { + info->frequencyReverse = false; + info->channelBandwidth = BANDWIDTH_WIDE; + info->outputPower = OUTPUT_POWER_LOW1; + info->busyChannelLock = false; + info->txLock = true; + } + else + { + const uint8_t d4 = raw.data[4]; + info->frequencyReverse = !!((d4 >> 0) & 1u); + info->channelBandwidth = !!((d4 >> 1) & 1u); + info->outputPower = ((d4 >> 2) & 7u); + info->busyChannelLock = !!((d4 >> 5) & 1u); + info->txLock = !!((d4 >> 6) & 1u); + } + + switch (info->txOffsetFrequencyDirection) + { + case TX_OFFSET_FREQUENCY_DIRECTION_ADD: + info->tx.Frequency = raw.frequency + info->offset; + break; + case TX_OFFSET_FREQUENCY_DIRECTION_SUB: + info->tx.Frequency = raw.frequency - info->offset; + break; + default: + break; + } + + if (raw.data[5] == 0xFF) + { +#ifdef ENABLE_DTMF_CALLING + info->dtmfDecodingEnable = false; +#endif + info->dtmfPttIdTxMode = PTT_ID_OFF; + } + else + { +#ifdef ENABLE_DTMF_CALLING + info->dtmfDecodingEnable = (raw.data[5] >> 0) & 1u; +#endif + const uint8_t pttId = (raw.data[5] >> 1) & 7u; + info->dtmfPttIdTxMode = pttId < ARRAY_SIZE(gSubMenu_PTT_ID) ? pttId : PTT_ID_OFF; + } + + return true; +} + void SETTINGS_FetchChannelName(char *s, const uint16_t channel) { if (s == NULL) diff --git a/App/settings.h b/App/settings.h index 5ea7efcb..b833c61d 100644 --- a/App/settings.h +++ b/App/settings.h @@ -307,10 +307,30 @@ typedef struct { extern EEPROM_Config_t gEeprom; +typedef struct { + FREQ_Config_t rx; + FREQ_Config_t tx; + uint32_t offset; + uint16_t stepFrequency; + STEP_Setting_t stepSetting; + ModulationMode_t modulation; + uint8_t txOffsetFrequencyDirection; + uint8_t outputPower; + bool frequencyReverse; + uint8_t channelBandwidth; + uint8_t busyChannelLock; + uint8_t txLock; +#ifdef ENABLE_DTMF_CALLING + uint8_t dtmfDecodingEnable; +#endif + PTT_ID_t dtmfPttIdTxMode; +} ChannelScanDisplayInfo_t; + void SETTINGS_InitEEPROM(void); void SETTINGS_LoadCalibration(void); uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel); bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation); +bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info); void SETTINGS_FetchChannelName(char *s, const uint16_t channel); void SETTINGS_FactoryReset(bool bIsAll); #ifdef ENABLE_FMRADIO diff --git a/App/ui/main.c b/App/ui/main.c index d650db40..7df0ccc4 100644 --- a/App/ui/main.c +++ b/App/ui/main.c @@ -1748,6 +1748,11 @@ void UI_DisplayMain(void) String[0] = '\0'; const VFO_Info_t *vfoInfo = &gEeprom.VfoInfo[vfo_num]; +#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER + const VFO_Info_t *scanDisplayVfo = CHFRSCANNER_GetScanDisplayVfo(); + if (vfo_num == gEeprom.RX_VFO && scanDisplayVfo != NULL) + vfoInfo = scanDisplayVfo; +#endif // show the modulation symbol const char * s = ""; @@ -1937,10 +1942,12 @@ void UI_DisplayMain(void) #endif #if ENABLE_FEAT_F4HWN + const uint8_t displayBandwidth = vfoInfo->CHANNEL_BANDWIDTH; + #ifdef ENABLE_FEAT_F4HWN_NARROWER bool narrower = 0; - if(vfoInfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1) + if(displayBandwidth == BANDWIDTH_NARROW && gSetting_set_nfm == 1) { narrower = 1; } @@ -1948,23 +1955,23 @@ void UI_DisplayMain(void) if (gSetting_set_gui) { const char *bandWidthNames[] = {"W", "N", "N+"}; - UI_PrintStringSmallNormal(bandWidthNames[vfoInfo->CHANNEL_BANDWIDTH + narrower], LCD_WIDTH + 80, 0, line + 1); + UI_PrintStringSmallNormal(bandWidthNames[displayBandwidth + narrower], LCD_WIDTH + 80, 0, line + 1); } else { const char *bandWidthNames[] = {"WIDE", "NAR", "NAR+"}; - GUI_DisplaySmallest(bandWidthNames[vfoInfo->CHANNEL_BANDWIDTH + narrower], 91, line == 0 ? 17 : 49, false, true); + GUI_DisplaySmallest(bandWidthNames[displayBandwidth + narrower], 91, line == 0 ? 17 : 49, false, true); } #else if (gSetting_set_gui) { const char *bandWidthNames[] = {"W", "N"}; - UI_PrintStringSmallNormal(bandWidthNames[vfoInfo->CHANNEL_BANDWIDTH], LCD_WIDTH + 80, 0, line + 1); + UI_PrintStringSmallNormal(bandWidthNames[displayBandwidth], LCD_WIDTH + 80, 0, line + 1); } else { const char *bandWidthNames[] = {"WIDE", "NAR"}; - GUI_DisplaySmallest(bandWidthNames[vfoInfo->CHANNEL_BANDWIDTH], 91, line == 0 ? 17 : 49, false, true); + GUI_DisplaySmallest(bandWidthNames[displayBandwidth], 91, line == 0 ? 17 : 49, false, true); } #endif #else