Add scan RSSI sparkline indicator

This commit is contained in:
Armel FAUVEAU committed 2026-06-08 01:50:28 +02:00
1 parent 8f11a32c9d
commit 25ea1f827b
5 files changed
+153 -1

No files matched your search

+1
View File
@@ -197,6 +197,7 @@ enable_feature(ENABLE_FEAT_F4HWN_INV)
enable_feature(ENABLE_FEAT_F4HWN_CTR) enable_feature(ENABLE_FEAT_F4HWN_CTR)
enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS) enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS)
enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER) enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER)
enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI)
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS) enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
enable_feature(ENABLE_FEAT_F4HWN_VOL) enable_feature(ENABLE_FEAT_F4HWN_VOL)
enable_feature(ENABLE_FEAT_F4HWN_AUDIO) enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
+75 -1
View File
@@ -1,5 +1,6 @@
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include "app/app.h" #include "app/app.h"
#include "app/chFrScanner.h" #include "app/chFrScanner.h"
@@ -232,6 +233,65 @@ static uint32_t scanFastPrevFrequency;
static bool scanFastLastFullTuneCandidate; static bool scanFastLastFullTuneCandidate;
static VFO_Info_t scanFastDisplayVfo; static VFO_Info_t scanFastDisplayVfo;
static bool scanFastDisplayVfoValid; static bool scanFastDisplayVfoValid;
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
static uint16_t scanRssiSparkline[CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
static uint8_t scanRssiSparklineWrite;
static uint8_t scanRssiSparklineCount;
static void ScanRssiSparklineReset(void)
{
memset(scanRssiSparkline, 0, sizeof(scanRssiSparkline));
scanRssiSparklineWrite = 0;
scanRssiSparklineCount = 0;
}
static void ScanRssiSparklinePush(uint16_t rssi)
{
scanRssiSparkline[scanRssiSparklineWrite] = rssi;
scanRssiSparklineWrite++;
if (scanRssiSparklineWrite >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
scanRssiSparklineWrite = 0;
if (scanRssiSparklineCount < CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
scanRssiSparklineCount++;
}
bool CHFRSCANNER_HasScanRssiSparkline(void)
{
return gScanStateDir != SCAN_OFF && scanRssiSparklineCount > 1;
}
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index)
{
uint16_t minRssi = SCAN_FAST_RSSI_MAX;
uint16_t rssi = 0;
uint8_t oldest;
if (index >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH || scanRssiSparklineCount == 0)
return 0;
if (index < CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
return 0;
oldest = (uint8_t)((scanRssiSparklineWrite + CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
% CHFRSCANNER_RSSI_SPARKLINE_WIDTH);
index -= (uint8_t)(CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount);
for (uint8_t i = 0; i < scanRssiSparklineCount; i++)
{
const uint16_t sample = scanRssiSparkline[(oldest + i) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
if (sample != 0 && sample < minRssi)
minRssi = sample;
}
rssi = scanRssiSparkline[(oldest + index) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
if (rssi == 0 || minRssi == SCAN_FAST_RSSI_MAX || rssi <= minRssi + 2)
return 0;
// 48 RSSI units ~= 24 dB. This keeps quiet jitter low while strong hits pop.
return (uint8_t)MIN(((uint32_t)(rssi - minRssi) * 5u + 24u) / 48u, 5u);
}
#endif
static void ScanFastResetState(void) static void ScanFastResetState(void)
{ {
@@ -538,6 +598,9 @@ static scan_fast_result_t ScanRangeFastPrecheck(void)
ScanFastTune(freq); ScanFastTune(freq);
const uint16_t rssi = ScanFastReadCandidateRssi(); const uint16_t rssi = ScanFastReadCandidateRssi();
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklinePush(rssi);
#endif
if (ScanFastIsCandidate(rssi)) if (ScanFastIsCandidate(rssi))
{ {
ScanRangeFastRefineCandidate(rssi); ScanRangeFastRefineCandidate(rssi);
@@ -573,7 +636,12 @@ static bool MemChannelFastPrecheck(uint16_t channel)
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC; scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
ScanFastTune(frequency); ScanFastTune(frequency);
if (ScanFastIsCandidate(ScanFastReadCandidateRssi())) const uint16_t rssi = ScanFastReadCandidateRssi();
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklinePush(rssi);
#endif
if (ScanFastIsCandidate(rssi))
{ {
scanFastLastFullTuneCandidate = true; scanFastLastFullTuneCandidate = true;
return true; // signal detected: let the full tune path follow return true; // signal detected: let the full tune path follow
@@ -637,6 +705,9 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
currentScanList = SCAN_NEXT_CHAN_SCANLIST1; currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
gScanStateDir = scan_direction; gScanStateDir = scan_direction;
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER #ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklineReset();
#endif
ScanFastResetState(); ScanFastResetState();
#endif #endif
@@ -820,6 +891,9 @@ void CHFRSCANNER_Stop(void)
} }
gScanStateDir = SCAN_OFF; gScanStateDir = SCAN_OFF;
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_FEAT_F4HWN_SCAN_RSSI)
ScanRssiSparklineReset();
#endif
const uint32_t chFr = gScanKeepResult ? lastFoundFrqOrChan : initialFrqOrChan; const uint32_t chFr = gScanKeepResult ? lastFoundFrqOrChan : initialFrqOrChan;
const bool channelChanged = chFr != initialFrqOrChan; const bool channelChanged = chFr != initialFrqOrChan;
+5
View File
@@ -26,6 +26,11 @@ void CHFRSCANNER_ManualResume(const int8_t scan_direction);
void CHFRSCANNER_ContinueScanning(void); void CHFRSCANNER_ContinueScanning(void);
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER #ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void); const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void);
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
bool CHFRSCANNER_HasScanRssiSparkline(void);
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index);
#define CHFRSCANNER_RSSI_SPARKLINE_WIDTH 24u
#endif
#endif #endif
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES) #if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
+70
View File
@@ -140,6 +140,71 @@ const char *VfoStateStr[] = {
[VFO_STATE_VOLTAGE_HIGH]="VOLT HIGH" [VFO_STATE_VOLTAGE_HIGH]="VOLT HIGH"
}; };
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_FEAT_F4HWN_SCAN_RSSI)
static uint8_t UI_MAIN_GetScanRssiSparklineMask(uint8_t previousLevel, uint8_t level)
{
uint8_t mask = 0;
const uint8_t bottom = 5;
if (level == 0)
return (uint8_t)(1u << bottom);
const uint8_t top = bottom + 1 - level;
uint8_t bridgeTop = top;
uint8_t bridgeBottom = top;
if (previousLevel > 0)
{
const uint8_t previousTop = bottom + 1 - previousLevel;
if (previousTop < bridgeTop)
bridgeTop = previousTop;
else if (previousTop > bridgeBottom)
bridgeBottom = previousTop;
}
// Solid crest line with a small vertical bridge to avoid broken diagonals.
for (uint8_t y = bridgeTop; y <= bridgeBottom; y++)
mask |= (uint8_t)(1u << y);
return mask;
}
static void UI_MAIN_DrawScanRssiSparkline(uint8_t line)
{
uint8_t *p_line = gFrameBuffer[line];
const uint8_t x0 = 7;
uint8_t level[CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
if (!CHFRSCANNER_HasScanRssiSparkline())
return;
for (uint8_t i = 0; i < CHFRSCANNER_RSSI_SPARKLINE_WIDTH; i++)
level[i] = CHFRSCANNER_GetScanRssiSparklineLevel(i);
for (uint8_t i = 0; i < CHFRSCANNER_RSSI_SPARKLINE_WIDTH; i++)
{
const uint8_t previousRaw = (i > 0) ? level[i - 1] : 0;
const uint8_t nextRaw = (i + 1 < CHFRSCANNER_RSSI_SPARKLINE_WIDTH) ? level[i + 1] : 0;
uint8_t displayLevel = (uint8_t)((previousRaw + 2u * level[i] + nextRaw + 2u) >> 2);
uint8_t previousLevel = previousRaw;
if (level[i] == 0 && previousRaw == 0 && nextRaw == 0)
displayLevel = 0;
else if (displayLevel == 0)
displayLevel = 1;
if (i > 0)
{
const uint8_t previousPreviousRaw = (i > 1) ? level[i - 2] : 0;
previousLevel = (uint8_t)((previousPreviousRaw + 2u * previousRaw + level[i] + 2u) >> 2);
}
const uint8_t mask = UI_MAIN_GetScanRssiSparklineMask(previousLevel, displayLevel);
p_line[x0 + i] = (p_line[x0 + i] & 0x80) | mask;
}
}
#endif
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS #ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
static void ScanProgress_ResetSession(void) static void ScanProgress_ResetSession(void)
{ {
@@ -1442,6 +1507,11 @@ void UI_DisplayMain(void)
#endif #endif
} }
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_FEAT_F4HWN_SCAN_RSSI)
if (vfo_num == gEeprom.RX_VFO && gScanStateDir != SCAN_OFF && !FUNCTION_IsRx())
UI_MAIN_DrawScanRssiSparkline(line);
#endif
if(TX_freq_check(frequency) != 0 && gEeprom.VfoInfo[vfo_num].TX_LOCK == true) if(TX_freq_check(frequency) != 0 && gEeprom.VfoInfo[vfo_num].TX_LOCK == true)
{ {
if (!FUNCTION_IsRx() || RxOnVfofrequency != frequency) if (!FUNCTION_IsRx() || RxOnVfofrequency != frequency)
+2
View File
@@ -70,6 +70,7 @@
"ENABLE_FEAT_F4HWN_MEM": false, "ENABLE_FEAT_F4HWN_MEM": false,
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false, "ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false,
"ENABLE_FEAT_F4HWN_SCAN_FASTER": false, "ENABLE_FEAT_F4HWN_SCAN_FASTER": false,
"ENABLE_FEAT_F4HWN_SCAN_RSSI": false,
"ENABLE_FEAT_F4HWN_BEAM": false, "ENABLE_FEAT_F4HWN_BEAM": false,
"ENABLE_FEAT_F4HWN_LOGO": false, "ENABLE_FEAT_F4HWN_LOGO": false,
"ENABLE_FEAT_F4HWN_LOGO_SAV": false, "ENABLE_FEAT_F4HWN_LOGO_SAV": false,
@@ -199,6 +200,7 @@
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true, "ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": true, "ENABLE_FEAT_F4HWN_SCAN_PROGRESS": true,
"ENABLE_FEAT_F4HWN_SCAN_FASTER": true, "ENABLE_FEAT_F4HWN_SCAN_FASTER": true,
"ENABLE_FEAT_F4HWN_SCAN_RSSI": true,
"ENABLE_FEAT_F4HWN_BEAM": true, "ENABLE_FEAT_F4HWN_BEAM": true,
"ENABLE_FEAT_F4HWN_QRCODE": true, "ENABLE_FEAT_F4HWN_QRCODE": true,
"ENABLE_FEAT_F4HWN_MEM": true, "ENABLE_FEAT_F4HWN_MEM": true,