From edc4673983c9b890f79ba15e217736ffc6bb5173 Mon Sep 17 00:00:00 2001 From: Armel FAUVEAU Date: Tue, 9 Jun 2026 04:57:15 +0200 Subject: [PATCH] Add subaudible detection for scan ranges --- App/CMakeLists.txt | 1 + App/app/app.c | 4 +++ App/app/chFrScanner.c | 59 +++++++++++++++++++++++++++++++++++++++++++ App/app/chFrScanner.h | 8 ++++++ App/ui/main.c | 36 ++++++++++++++++++++++++++ CMakePresets.json | 2 ++ 6 files changed, 110 insertions(+) diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index bd6ac275..df9ffccc 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -198,6 +198,7 @@ enable_feature(ENABLE_FEAT_F4HWN_CTR) enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS) enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER) enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI) +enable_feature(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS) enable_feature(ENABLE_FEAT_F4HWN_VOL) enable_feature(ENABLE_FEAT_F4HWN_AUDIO) diff --git a/App/app/app.c b/App/app/app.c index 57ddb407..6befa8b0 100644 --- a/App/app/app.c +++ b/App/app/app.c @@ -1755,6 +1755,10 @@ void APP_TimeSlice10ms(void) SCANNER_TimeSlice10ms(); +#if defined(ENABLE_SCAN_RANGES) && defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE + CHFRSCANNER_UpdateCssDetection(); +#endif + #ifdef ENABLE_AIRCOPY if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER && gAirCopyIsSendMode == 1) { if (!AIRCOPY_SendMessage()) { diff --git a/App/app/chFrScanner.c b/App/app/chFrScanner.c index 4b5d1f06..7c13d690 100644 --- a/App/app/chFrScanner.c +++ b/App/app/chFrScanner.c @@ -22,6 +22,13 @@ bool gScanPauseMode; uint32_t gScanRangeStart; uint32_t gScanRangeStop; +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE +DCS_CodeType_t gScanRangeCssType = CODE_TYPE_OFF; +uint8_t gScanRangeCssCode = 0xFF; +static uint8_t scanRangeCssCandidate = 0xFF; +static uint8_t scanRangeCssHitCount = 0; +#endif + #define SCAN_RANGE_SKIP_MAX 32 #if (SCAN_RANGE_SKIP_MAX & (SCAN_RANGE_SKIP_MAX - 1)) != 0 #error SCAN_RANGE_SKIP_MAX must be a power of two @@ -183,6 +190,58 @@ bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t la return false; } + +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE +void CHFRSCANNER_UpdateCssDetection(void) +{ + if (!gScanRangeStart || !FUNCTION_IsRx()) + { + gScanRangeCssType = CODE_TYPE_OFF; + gScanRangeCssCode = 0xFF; + scanRangeCssCandidate = 0xFF; + scanRangeCssHitCount = 0; + return; + } + + uint32_t cdcssFreq; + uint16_t ctcssFreq; + const BK4819_CssScanResult_t result = BK4819_GetCxCSSScanResult(&cdcssFreq, &ctcssFreq); + + if (result == BK4819_CSS_RESULT_CDCSS) + { + const uint8_t Code = DCS_GetCdcssCode(cdcssFreq); + if (Code != 0xFF && Code != gScanRangeCssCode) + { + gScanRangeCssType = CODE_TYPE_DIGITAL; + gScanRangeCssCode = Code; + scanRangeCssCandidate = 0xFF; + scanRangeCssHitCount = 0; + gUpdateDisplay = true; + } + } + else if (result == BK4819_CSS_RESULT_CTCSS) + { + const uint8_t Code = DCS_GetCtcssCode(ctcssFreq); + if (Code != 0xFF) + { + if (Code == scanRangeCssCandidate) + { + if (++scanRangeCssHitCount >= 2 && Code != gScanRangeCssCode) + { + gScanRangeCssType = CODE_TYPE_CONTINUOUS_TONE; + gScanRangeCssCode = Code; + gUpdateDisplay = true; + } + } + else + { + scanRangeCssCandidate = Code; + scanRangeCssHitCount = 1; + } + } + } +} +#endif #endif static void CHFRSCANNER_AbortActiveReception(void) diff --git a/App/app/chFrScanner.h b/App/app/chFrScanner.h index 057a5dc9..c1e33e50 100644 --- a/App/app/chFrScanner.h +++ b/App/app/chFrScanner.h @@ -15,8 +15,16 @@ extern bool gScanPauseMode; #ifdef ENABLE_SCAN_RANGES extern uint32_t gScanRangeStart; extern uint32_t gScanRangeStop; +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE +extern DCS_CodeType_t gScanRangeCssType; +extern uint8_t gScanRangeCssCode; +#endif + bool CHFRSCANNER_ExcludeCurrentScanRange(void); bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal); +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE +void CHFRSCANNER_UpdateCssDetection(void); +#endif #endif void CHFRSCANNER_Found(void); diff --git a/App/ui/main.c b/App/ui/main.c index 02c1631e..b5c860ac 100644 --- a/App/ui/main.c +++ b/App/ui/main.c @@ -1233,6 +1233,26 @@ static void UI_FormatFrequency(uint32_t freq, char *buffer) { sprintf(buffer, "%3u.%05u", freq / 100000, freq % 100000); } +#if defined(ENABLE_SCAN_RANGES) && defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE +static void UI_PrintScanRangeCss(char *String, uint8_t LabelX, uint8_t ValueX, uint8_t Line) +{ + if (gScanRangeCssType == CODE_TYPE_CONTINUOUS_TONE) + { + strcpy(String, "CTCSS"); + UI_PrintStringSmallNormalInverse(String, LabelX, 0, Line); + sprintf(String, "%u.%uHz", CTCSS_Options[gScanRangeCssCode] / 10, CTCSS_Options[gScanRangeCssCode] % 10); + } + else + { + strcpy(String, "DCS"); + UI_PrintStringSmallNormalInverse(String, LabelX, 0, Line); + sprintf(String, "D%03o%c", DCS_Options[gScanRangeCssCode], gScanRangeCssType == CODE_TYPE_REVERSE_DIGITAL ? 'I' : 'N'); + } + + UI_PrintStringSmallNormal(String, ValueX, 0, Line); +} +#endif + void UI_DisplayMain(void) { char String[22]; @@ -1331,6 +1351,11 @@ void UI_DisplayMain(void) UI_FormatFrequency(gScanRangeStop, String); UI_PrintStringSmallNormal(String, 56, 0, line + shift + 1); +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE + if (!isMainOnly() && gScanRangeCssCode != 0xFF) + UI_PrintScanRangeCss(String, 6, 48, line + 2); +#endif + if (!isMainOnly()) continue; } @@ -1344,6 +1369,12 @@ void UI_DisplayMain(void) UI_PrintStringSmallNormal(String, 56, 0, line); UI_FormatFrequency(gScanRangeStop, String); UI_PrintStringSmallNormal(String, 56, 0, line + 1); + +#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE + if (gScanRangeCssCode != 0xFF) + UI_PrintScanRangeCss(String, 2, 44, line + 2); +#endif + continue; #endif } @@ -2172,6 +2203,11 @@ void UI_DisplayMain(void) UI_MAIN_PrintAGC(false); #endif +#if defined(ENABLE_SCAN_RANGES) && defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE + if (isMainOnly() && gScanRangeStart && gScanRangeCssCode != 0xFF) + UI_PrintScanRangeCss(String, 2, 46, 6); +#endif + if (center_line == CENTER_LINE_NONE) { // we're free to use the middle line diff --git a/CMakePresets.json b/CMakePresets.json index d4430ae4..08df6767 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -71,6 +71,7 @@ "ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false, "ENABLE_FEAT_F4HWN_SCAN_FASTER": false, "ENABLE_FEAT_F4HWN_SCAN_RSSI": false, + "ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": false, "ENABLE_FEAT_F4HWN_BEAM": false, "ENABLE_FEAT_F4HWN_LOGO": false, "ENABLE_FEAT_F4HWN_LOGO_SAV": false, @@ -201,6 +202,7 @@ "ENABLE_FEAT_F4HWN_SCAN_PROGRESS": true, "ENABLE_FEAT_F4HWN_SCAN_FASTER": true, "ENABLE_FEAT_F4HWN_SCAN_RSSI": true, + "ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true, "ENABLE_FEAT_F4HWN_BEAM": true, "ENABLE_FEAT_F4HWN_QRCODE": true, "ENABLE_FEAT_F4HWN_MEM": true,