Compare commits

...
Author SHA1 Message Date
Armel FAUVEAU 5b4b3e2a71 Merge pull request #423 from armel/feature_update_v5
Feature update v5
2026-06-21 19:02:08 +02:00
Armel FAUVEAU 65a5c9f4ef Prepare new version v5.6.1 2026-06-21 19:01:14 +02:00
Armel FAUVEAU a2936402c2 Fix screenshot state updates without display redraw 2026-06-21 18:07:10 +02:00
Armel FAUVEAU f069ed935f Fix screensaver state during deep sleep 2026-06-21 04:28:58 +02:00
Armel FAUVEAU dcbbd744ac Add screenshot state flags 2026-06-21 03:21:16 +02:00
Armel FAUVEAU bca06e3382 Fix NAR+ FM demod boost 2026-06-21 00:17:50 +02:00
Armel FAUVEAU 8723863fb0 Fix F+4 verify VHF harmonic before accepting frequency 2026-06-20 13:41:14 +02:00
Armel FAUVEAU c775fa8b2f Update donors 2026-06-18 02:29:34 +02:00
Armel FAUVEAU 80e7d4b3d1 Fix F+4 frequency scan sensitivity 2026-06-17 03:25:24 +02:00
Armel FAUVEAU e6a727eeda Fix this fucking and stupid roger beep (Issue #416) 2026-06-17 02:32:23 +02:00
Armel FAUVEAU 887e18efe6 Fix reset defaults (Issue #413) 2026-06-16 16:00:56 +02:00
Armel FAUVEAU 20b87feb1d Merge pull request #407 from mrkusypl/feature_update_v5_2
Code refactoring (4 B)
2026-06-15 22:20:52 +02:00
mrkusypl e2e9c9ba60 Code refactoring (4 B) 2026-06-15 20:40:56 +02:00
Armel FAUVEAU 337eb0cb12 Update donors 2026-06-15 16:10:31 +02:00
Armel FAUVEAU 504a2b0fb0 Update .gitignore 2026-06-15 03:15:45 +02:00
Armel FAUVEAU 16b07c6374 Merge pull request #405 from armel/feature_update_v5
Feature update v5
2026-06-15 03:10:46 +02:00
Armel FAUVEAU f5b20d4f80 Prepare v5.6.0 2026-06-15 03:09:32 +02:00
Armel FAUVEAU ae14bf5df3 Hollow out backlight bulb icon when manual light is off 2026-06-13 14:33:42 +02:00
Armel FAUVEAU 9dc85852ba Widen screenshot chunk fingerprint to 16 bits to fix stale chunks 2026-06-13 13:20:10 +02:00
Armel FAUVEAU f7d29f6a14 Save 128 bytes of SRAM in backlight PWM while preserving distinct brightness steps 2026-06-13 03:58:43 +02:00
Armel FAUVEAU 1184618731 Reduce screenshot peak stack usage with on-demand chunk computation 2026-06-13 03:30:22 +02:00
Armel FAUVEAU 3d15d59ce0 Remove dead screenshot bit packing flush 2026-06-12 18:06:44 +02:00
Armel FAUVEAU 15fccc31d6 Fix AM to FM dual-watch RX reconfiguration (issue #389) 2026-06-11 16:49:06 +02:00
Armel FAUVEAU 48f753caaf Reduce screenshot RAM usage with per-chunk frame hashing 2026-06-10 13:46:18 +02:00
Armel FAUVEAU edc4673983 Add subaudible detection for scan ranges 2026-06-09 04:57:15 +02:00
Armel FAUVEAU 9e03f02296 Refine VFO lock icon placement during scan 2026-06-08 04:11:36 +02:00
Armel FAUVEAU 25ea1f827b Add scan RSSI sparkline indicator 2026-06-08 01:50:28 +02:00
Armel FAUVEAU 8f11a32c9d Merge pull request #392 from mrkusypl/feature_update_v5_2
Code refactoring (32 B)
2026-06-06 18:23:54 +02:00
mrkusypl 576b8c552f Code refactoring (32 B) 2026-06-06 17:56:33 +02:00
Armel FAUVEAU 85db5a2394 Update .gitignore 2026-06-03 04:06:57 +02:00
Armel FAUVEAU 98989d4f0a Fix bandscope 8.33 kHz frequency rounding (issue #380) 2026-05-28 00:26:46 +02:00
Armel FAUVEAU 8c976cfffd Update donors 2026-05-25 12:24:23 +02:00
Armel FAUVEAU 0ce5422db3 Fix ENABLE_FEAT_F4HWN_RX_TX_TIMER guard with ENABLE_FEAT_F4HWN_DEBUG 2026-05-25 01:41:27 +02:00
Armel FAUVEAU d939009949 Merge pull request #370 from mrkusypl/feature_update_v5_2
Code refactoring (104 B)
2026-05-25 01:35:25 +02:00
mrkusypl 50efade5c4 Code refactoring (104 B) 2026-05-25 00:46:34 +02:00
Armel FAUVEAU 682a59fc0e Merge pull request #365 from mrkusypl/feature_update_v5_2
Prevent screensaver during M-SCAN; Code refactoring (20 B)
2026-05-23 14:40:01 +02:00
mrkusypl 3d3f370b86 Minor correction for checking gFM_ChannelPosition 2026-05-23 10:48:59 +02:00
mrkusypl eebeba09db Prevent screensaver during M-SCAN; Code refactoring (20 B) 2026-05-23 02:21:11 +02:00
30 changed files with 783 additions and 265 deletions

No files matched your search

+1
View File
@@ -15,3 +15,4 @@ k5_eeprom.raw
__pycache__/
.DS_Store
AGENTS.md
+2
View File
@@ -197,6 +197,8 @@ enable_feature(ENABLE_FEAT_F4HWN_INV)
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)
+17 -12
View File
@@ -135,20 +135,13 @@ static uint8_t ScreenSaverRandom(void)
static void ScreenSaverSetPixel(uint8_t x, uint8_t y, bool fill)
{
const uint8_t pattern = 1u << (y & 7u);
uint8_t *target = (y < 8) ? &gStatusLine[x] : &gFrameBuffer[(y >> 3) - 1][x];
if (y < 8) {
if (fill)
gStatusLine[x] |= pattern;
else
gStatusLine[x] &= (uint8_t)~pattern;
return;
}
y -= 8;
if (fill)
gFrameBuffer[y >> 3][x] |= pattern;
*target |= pattern;
else
gFrameBuffer[y >> 3][x] &= (uint8_t)~pattern;
*target &= (uint8_t)~pattern;
}
static void ScreenSaverDrawMatrixGlyph(uint8_t x, int16_t y, uint8_t glyph)
@@ -246,11 +239,14 @@ static bool ScreenSaverCanDisplay(void)
gEeprom.BACKLIGHT_TIME == 0 ||
gEeprom.BACKLIGHT_TIME >= 61 ||
gScreenSaverDisplayed ||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
gWakeUp ||
#endif
gCurrentFunction == FUNCTION_TRANSMIT ||
FUNCTION_IsRx() ||
gPttIsPressed
#ifdef ENABLE_FMRADIO
|| gFM_ScanState != FM_SCAN_OFF
|| (gFM_ScanState != FM_SCAN_OFF && !gFM_FoundFrequency)
#endif
#ifdef ENABLE_FEAT_F4HWN_BEAM
|| gBeamActive
@@ -1667,6 +1663,8 @@ void APP_TimeSlice10ms(void)
#endif
) {
SCREENSHOT_Update(false);
} else if (SCREENSHOT_HasPendingStateChange()) {
SCREENSHOT_Update(false);
}
#endif
@@ -1762,6 +1760,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()) {
@@ -1918,6 +1920,9 @@ void APP_TimeSlice500ms(void)
gBacklightCountdown_500ms = 0;
gPowerSave_10ms = 1;
gWakeUp = true;
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
ScreenSaverExit();
#endif
// TODO:
// PWM_PLUS0_CH0_COMP = 0;
BACKLIGHT_SetBrightness(0);
+134 -1
View File
@@ -1,5 +1,6 @@
#include <stddef.h>
#include <string.h>
#include "app/app.h"
#include "app/chFrScanner.h"
@@ -21,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
@@ -182,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)
@@ -232,6 +292,65 @@ static uint32_t scanFastPrevFrequency;
static bool scanFastLastFullTuneCandidate;
static VFO_Info_t scanFastDisplayVfo;
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)
{
@@ -538,6 +657,9 @@ static scan_fast_result_t ScanRangeFastPrecheck(void)
ScanFastTune(freq);
const uint16_t rssi = ScanFastReadCandidateRssi();
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklinePush(rssi);
#endif
if (ScanFastIsCandidate(rssi))
{
ScanRangeFastRefineCandidate(rssi);
@@ -573,7 +695,12 @@ static bool MemChannelFastPrecheck(uint16_t channel)
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
ScanFastTune(frequency);
if (ScanFastIsCandidate(ScanFastReadCandidateRssi()))
const uint16_t rssi = ScanFastReadCandidateRssi();
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklinePush(rssi);
#endif
if (ScanFastIsCandidate(rssi))
{
scanFastLastFullTuneCandidate = true;
return true; // signal detected: let the full tune path follow
@@ -637,6 +764,9 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
gScanStateDir = scan_direction;
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
ScanRssiSparklineReset();
#endif
ScanFastResetState();
#endif
@@ -820,6 +950,9 @@ void CHFRSCANNER_Stop(void)
}
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 bool channelChanged = chFr != initialFrqOrChan;
+13
View File
@@ -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);
@@ -26,6 +34,11 @@ 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);
#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
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
+1 -1
View File
@@ -474,7 +474,7 @@ void DTMF_Reply(void)
if (pString == NULL)
return;
Delay = (gEeprom.DTMF_PRELOAD_TIME < 200) ? 200 : gEeprom.DTMF_PRELOAD_TIME;
Delay = MAX(gEeprom.DTMF_PRELOAD_TIME, 200);
if (gEeprom.DTMF_SIDE_TONE)
{ // the user will also hear the transmitted tones
+19 -13
View File
@@ -94,6 +94,11 @@ int FM_ConfigureChannelState(void)
return 0;
}
void FM_SetFrequency(void)
{
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
}
void FM_TurnOff(void)
{
gFmRadioMode = false;
@@ -163,11 +168,10 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
gFM_ScanState = Step;
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
}
void FM_AudioPathOn(void) {
BACKLIGHT_TurnOn();
AUDIO_AudioPathOn();
gEnableSpeaker = true;
}
@@ -182,13 +186,14 @@ void FM_PlayAndUpdate(void)
}
FM_ConfigureChannelState();
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
SETTINGS_SaveFM();
gFmPlayCountdown_10ms = 0;
gScheduleFM = false;
gAskToSave = false;
BACKLIGHT_TurnOn();
FM_AudioPathOn();
}
@@ -291,7 +296,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
gAnotherVoiceID = (VOICE_ID_t)Key;
#endif
gEeprom.FM_FrequencyPlaying = gEeprom.FM_SelectedFrequency;
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
gRequestSaveFM = true;
return;
}
@@ -311,7 +316,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
#endif
gEeprom.FM_SelectedChannel = Channel;
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
gRequestSaveFM = true;
return;
}
@@ -366,7 +371,7 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
gEeprom.FM_IsMrMode = !gEeprom.FM_IsMrMode;
if (!FM_ConfigureChannelState()) {
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
gRequestSaveFM = true;
}
else
@@ -480,7 +485,7 @@ static void Key_MENU(uint8_t state)
gFM_Channels[gEeprom.FM_SelectedChannel] = 0xFFFF;
FM_ConfigureChannelState();
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
gRequestSaveFM = true;
}
@@ -558,7 +563,7 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
gRequestSaveFM = true;
Bail:
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
FM_SetFrequency();
gRequestDisplayScreen = DISPLAY_FM;
}
@@ -612,19 +617,19 @@ void FM_Play(void)
if (!gEeprom.FM_IsMrMode)
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
BACKLIGHT_TurnOn();
FM_AudioPathOn();
GUI_SelectNextDisplay(DISPLAY_FM);
return;
goto Display;
}
if (gFM_ChannelPosition < FM_CHANNELS_MAX)
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
if (gFM_ChannelPosition >= FM_CHANNELS_MAX) {
FM_PlayAndUpdate();
GUI_SelectNextDisplay(DISPLAY_FM);
return;
goto Display;
}
}
@@ -633,6 +638,7 @@ void FM_Play(void)
else
FM_Tune(gEeprom.FM_FrequencyPlaying, gFM_ScanState, false);
Display:
GUI_SelectNextDisplay(DISPLAY_FM);
}
+110 -14
View File
@@ -41,6 +41,101 @@ bool gScanUseCssResult;
STEP_Setting_t stepSetting;
uint8_t scanHitCount;
typedef enum
{
SCAN_FREQ_VERIFY_OFF,
SCAN_FREQ_VERIFY_FUNDAMENTAL,
SCAN_FREQ_VERIFY_HARMONIC
} SCAN_FrequencyVerifyState_t;
static SCAN_FrequencyVerifyState_t scanFreqVerifyState;
static uint32_t scanVerifyFundamental;
static uint32_t scanVerifyHarmonic;
static uint16_t scanVerifyFundamentalRssi;
static bool SCANNER_ShouldVerifyVhfSecondHarmonic(const uint32_t frequency)
{
const uint32_t fundamental = frequency / 2;
return frequency >= (frequencyBandTable[BAND3_137MHz].lower * 2) &&
frequency < (frequencyBandTable[BAND4_174MHz].lower * 2) &&
fundamental >= frequencyBandTable[BAND3_137MHz].lower &&
fundamental < frequencyBandTable[BAND4_174MHz].lower;
}
static void SCANNER_StartCssScanAtFrequency(const uint32_t frequency)
{
gScanFrequency = frequency;
gScanCssResultCode = 0xFF;
gScanCssResultType = 0xFF;
scanHitCount = 0;
gScanUseCssResult = false;
gScanProgressIndicator = 0;
gScanCssState = SCAN_CSS_STATE_SCANNING;
gScanDelay_10ms = scan_delay_10ms;
BK4819_SetScanFrequency(gScanFrequency);
if (!gCssBackgroundScan)
GUI_SelectNextDisplay(DISPLAY_SCANNER);
gUpdateStatus = true;
}
static void SCANNER_TuneForFrequencyVerification(const uint32_t frequency)
{
BK4819_SetFrequency(frequency);
BK4819_PickRXFilterPathBasedOnFrequency(frequency);
BK4819_RX_TurnOn();
}
static uint16_t SCANNER_ReadVerificationRssi(void)
{
(void)BK4819_GetRSSI();
return BK4819_GetRSSI();
}
static void SCANNER_StartFrequencyVerification(const uint32_t harmonic)
{
scanVerifyFundamental = harmonic / 2;
scanVerifyHarmonic = harmonic;
scanFreqVerifyState = SCAN_FREQ_VERIFY_FUNDAMENTAL;
SCANNER_TuneForFrequencyVerification(scanVerifyFundamental);
gScanDelay_10ms = 2;
}
static bool SCANNER_HandleFrequencyVerification(void)
{
switch (scanFreqVerifyState) {
case SCAN_FREQ_VERIFY_FUNDAMENTAL:
scanVerifyFundamentalRssi = SCANNER_ReadVerificationRssi();
scanFreqVerifyState = SCAN_FREQ_VERIFY_HARMONIC;
SCANNER_TuneForFrequencyVerification(scanVerifyHarmonic);
gScanDelay_10ms = 2;
return true;
case SCAN_FREQ_VERIFY_HARMONIC: {
const uint16_t harmonicRssi = SCANNER_ReadVerificationRssi();
const uint16_t margin = 4;
uint32_t verifiedFrequency = scanVerifyHarmonic;
if (scanVerifyFundamentalRssi > harmonicRssi &&
scanVerifyFundamentalRssi - harmonicRssi >= margin)
{
verifiedFrequency = scanVerifyFundamental;
}
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
SCANNER_StartCssScanAtFrequency(verifiedFrequency);
return true;
}
default:
return false;
}
}
static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
if (!bKeyHeld && bKeyPressed)
@@ -341,7 +436,8 @@ void SCANNER_Start(bool singleFreq)
gScanCssState = SCAN_CSS_STATE_OFF;
gScanFrequency = 0xFFFFFFFF;
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
// Keep the RX input path selected by RADIO_SetupRegisters().
// 0xFFFFFFFF means "unknown frequency" here and would turn both LNAs off.
BK4819_SetFrequencyScan(true);
gUpdateStatus = true;
@@ -366,6 +462,7 @@ void SCANNER_Start(bool singleFreq)
g_SquelchLost = false;
gScannerSaveState = SCAN_SAVE_NO_PROMPT;
gScanProgressIndicator = 0;
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
}
void SCANNER_Stop(void)
@@ -377,6 +474,7 @@ void SCANNER_Stop(void)
gUpdateStatus = true;
gCssBackgroundScan = false;
gScanUseCssResult = false;
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
#ifdef ENABLE_VOICE
gAnotherVoiceID = VOICE_ID_CANCEL;
#endif
@@ -398,6 +496,10 @@ void SCANNER_TimeSlice10ms(void)
return;
}
if (SCANNER_HandleFrequencyVerification()) {
return;
}
switch (gScanCssState) {
case SCAN_CSS_STATE_OFF: {
// must be RF frequency scanning if we're here ?
@@ -420,22 +522,16 @@ void SCANNER_TimeSlice10ms(void)
if (scanHitCount < 3) {
BK4819_SetFrequencyScan(true);
}
else if (SCANNER_ShouldVerifyVhfSecondHarmonic(gScanFrequency)) {
SCANNER_StartFrequencyVerification(gScanFrequency);
}
else {
BK4819_SetScanFrequency(gScanFrequency);
gScanCssResultCode = 0xFF;
gScanCssResultType = 0xFF;
scanHitCount = 0;
gScanUseCssResult = false;
gScanProgressIndicator = 0;
gScanCssState = SCAN_CSS_STATE_SCANNING;
if(!gCssBackgroundScan)
GUI_SelectNextDisplay(DISPLAY_SCANNER);
gUpdateStatus = true;
SCANNER_StartCssScanAtFrequency(gScanFrequency);
}
gScanDelay_10ms = scan_delay_10ms;
if (scanFreqVerifyState == SCAN_FREQ_VERIFY_OFF) {
gScanDelay_10ms = scan_delay_10ms;
}
//gScanDelay_10ms = 1; // 10ms
break;
}
+12 -1
View File
@@ -433,8 +433,15 @@ static void ToggleAFDAC(bool on)
BK4819_WriteRegister(BK4819_REG_30, Reg);
}
static uint32_t NormalizeScanFrequency(uint32_t f)
{
const uint16_t step = scanStepValues[settings.scanStepIndex];
return (step == 833) ? FREQUENCY_RoundToStep(f, step) : f;
}
static void SetF(uint32_t f)
{
f = NormalizeScanFrequency(f);
fMeasure = f;
BK4819_SetFrequency(fMeasure);
@@ -451,6 +458,8 @@ static void SetF(uint32_t f)
// cutting the SPI bus activity that causes SPI-induced audio interference.
static void SetFScan(uint32_t f)
{
f = NormalizeScanFrequency(f);
// Refresh RF path only when crossing the VHF/UHF boundary (280 MHz)
if ((f < 28000000) != (fMeasure < 28000000))
BK4819_PickRXFilterPathBasedOnFrequency(f);
@@ -756,7 +765,7 @@ static void UpdateScanInfo()
if (scanInfo.rssi > scanInfo.rssiMax)
{
scanInfo.rssiMax = scanInfo.rssi;
scanInfo.fPeak = scanInfo.f;
scanInfo.fPeak = NormalizeScanFrequency(scanInfo.f);
scanInfo.iPeak = scanInfo.i;
}
@@ -1593,6 +1602,7 @@ static void ShowChannelName(uint32_t f)
{
static uint32_t channelF = 0;
static char channelName[12];
f = NormalizeScanFrequency(f);
// Channel name starts at x=43 (fixed), leaving room for the dBm
// string on the left (max ~40 px) and battery indicator at x=116.
@@ -1632,6 +1642,7 @@ static void FormatFrequency(uint32_t freq, char *buffer) {
static void DrawF(uint32_t f)
{
f = NormalizeScanFrequency(f);
FormatFrequency(f, String);
// Align frequency with channel name in status bar (both at x=43).
// Left-aligned (End == Start = 43) so it does not collide with BW at x=108.
+15
View File
@@ -57,6 +57,21 @@ const uint8_t gFontLight[9] =
0b00001100,
};
// Same bulb outline as gFontLight with the inner filament removed,
// used when the manual backlight is currently off
const uint8_t gFontLightOff[9] =
{
0b00001100,
0b00010010,
0b00100001,
0b01100001,
0b01100001,
0b01100001,
0b00100001,
0b00010010,
0b00001100,
};
const uint8_t gFontMute[12] =
{
0b00011100,
+1
View File
@@ -12,6 +12,7 @@ extern const uint8_t gFontS[6];
extern const uint8_t gFontKeyLock[9];
extern const uint8_t gFontLight[9];
extern const uint8_t gFontLightOff[9];
extern const uint8_t gFontMute[12];
extern const uint8_t gFontXB[2][6];
+5 -3
View File
@@ -32,7 +32,9 @@
#endif
#define PWM_FREQ 4000
#define DUTY_CYCLE_LEVELS 64
// 32 levels keep every step of the value[] table distinct after
// quantization while halving the DMA duty cycle buffer (128 bytes saved)
#define DUTY_CYCLE_LEVELS 32
#define DUTY_CYCLE_ON_VALUE GPIO_PIN_MASK(GPIO_PIN_BACKLIGHT)
#define DUTY_CYCLE_OFF_VALUE (DUTY_CYCLE_ON_VALUE << 16)
@@ -56,8 +58,8 @@ static int16_t fadeStep = 0;
const uint8_t value[] = {
0, // 0 off
8, // 1 visible in the dark
14, // 2
22, // 3
16, // 2
24, // 3
32, // 4
48, // 5
72, // 6
+21 -5
View File
@@ -411,6 +411,11 @@ void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet)
BK4819_WriteRegister(BK4819_REG_33, gBK4819_GpioOutState);
}
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin)
{
return (gBK4819_GpioOutState & (0x40u >> Pin)) != 0;
}
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord)
{
// REG_51
@@ -1719,7 +1724,7 @@ void BK4819_PrepareFSKReceive(void)
BK4819_WriteRegister(BK4819_REG_59, 0x3068);
}
static void BK4819_PlayRogerNormal(void)
static void BK4819_PlayRogerNormal(BK4819_FilterBandwidth_t Bandwidth)
{
#if 0
const uint32_t tone1_Hz = 500;
@@ -1734,12 +1739,19 @@ static void BK4819_PlayRogerNormal(void)
BK4819_EnterTxMute();
BK4819_SetAF(BK4819_AF_MUTE);
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (66u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
const uint8_t rogerToneGain = (Bandwidth == BK4819_FILTER_BW_WIDE) ? 32u : 66u;
if (Bandwidth == BK4819_FILTER_BW_WIDE)
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_NARROW, true);
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
BK4819_EnableTXLink();
SYSTEM_DelayMs(50);
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
BK4819_WriteRegister(BK4819_REG_70,
BK4819_REG_70_ENABLE_TONE1 |
(rogerToneGain << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
BK4819_ExitTxMute();
SYSTEM_DelayMs(80);
@@ -1752,6 +1764,10 @@ static void BK4819_PlayRogerNormal(void)
BK4819_EnterTxMute();
BK4819_WriteRegister(BK4819_REG_70, 0x0000);
if (Bandwidth == BK4819_FILTER_BW_WIDE)
BK4819_SetFilterBandwidth(Bandwidth, true);
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); // 1 1 0000 0 1 1111 1 1 1 0
}
@@ -1804,10 +1820,10 @@ void BK4819_PlayRogerMDC(void)
BK4819_WriteRegister(BK4819_REG_58, 0x0000);
}
void BK4819_PlayRoger(void)
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth)
{
if (gEeprom.ROGER == ROGER_MODE_ROGER) {
BK4819_PlayRogerNormal();
BK4819_PlayRogerNormal(Bandwidth);
} else if (gEeprom.ROGER == ROGER_MODE_MDC) {
BK4819_PlayRogerMDC();
}
+2 -1
View File
@@ -77,6 +77,7 @@ void BK4819_SetAGC(bool enable);
void BK4819_InitAGC(bool amModulation);
void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet);
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin);
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord);
void BK4819_SetCTCSSFrequency(uint32_t BaudRate);
@@ -169,7 +170,7 @@ uint8_t BK4819_GetCTCType(void);
void BK4819_SendFSKData(uint16_t *pData);
void BK4819_PrepareFSKReceive(void);
void BK4819_PlayRoger(void);
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth);
void BK4819_Enable_AfDac_DiscMode_TxDsp(void);
+30 -11
View File
@@ -441,6 +441,11 @@ void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet)
BK4819_WriteRegister(BK4819_REG_33, gBK4819_GpioOutState);
}
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin)
{
return (gBK4819_GpioOutState & (0x40u >> Pin)) != 0;
}
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord)
{
// REG_51
@@ -696,11 +701,11 @@ void BK4819_SetFilterBandwidth(const BK4819_FilterBandwidth_t Bandwidth, const b
break;
case BK4819_FILTER_BW_NARROWER: // 6.25kHz
// 0x205C = (0b010 << 12) | (0b000 << 9) | (0b001 << 6) |
// (0b01 << 4) | (1 << 3) | (1 << 2)
// 0x2058 = (0b010 << 12) | (0b000 << 9) | (0b001 << 6) |
// (0b01 << 4) | (1 << 3)
// = RF 3.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
// 6.25k mode, FM gain +6 dB.
val = 0x205C;
// 6.25k mode, FM gain 0 dB.
val = 0x2058;
break;
case BK4819_FILTER_BW_AM: // Stock AM preset
@@ -1754,7 +1759,7 @@ void BK4819_PrepareFSKReceive(void)
BK4819_WriteRegister(BK4819_REG_59, 0x3068);
}
static void BK4819_PlayRogerNormal(void)
static void BK4819_PlayRogerNormal(BK4819_FilterBandwidth_t Bandwidth)
{
#if 0
const uint32_t tone1_Hz = 500;
@@ -1769,17 +1774,31 @@ static void BK4819_PlayRogerNormal(void)
BK4819_EnterTxMute();
BK4819_SetAF(BK4819_AF_MUTE);
BK4819_WriteRegister(BK4819_REG_70, // BK4819_REG_70_ENABLE_TONE1 | (66u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
0xC300
);
const uint8_t rogerToneGain = (Bandwidth == BK4819_FILTER_BW_WIDE) ? 32u : 67u;
if (Bandwidth == BK4819_FILTER_BW_WIDE)
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_NARROW, true);
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
BK4819_EnableTXLink();
SYSTEM_DelayMs(50);
BK4819_PlayToneRaw(tone1_Hz, 80);
BK4819_WriteRegister(BK4819_REG_70,
BK4819_REG_70_ENABLE_TONE1 |
(rogerToneGain << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
BK4819_ExitTxMute();
SYSTEM_DelayMs(80);
BK4819_EnterTxMute();
BK4819_PlayToneRaw(tone2_Hz, 80);
BK4819_WriteRegister(BK4819_REG_70, 0x0000);
if (Bandwidth == BK4819_FILTER_BW_WIDE)
BK4819_SetFilterBandwidth(Bandwidth, true);
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); // 1 1 0000 0 1 1111 1 1 1 0
}
@@ -1832,10 +1851,10 @@ void BK4819_PlayRogerMDC(void)
BK4819_WriteRegister(BK4819_REG_58, 0x0000);
}
void BK4819_PlayRoger(void)
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth)
{
if (gEeprom.ROGER == ROGER_MODE_ROGER) {
BK4819_PlayRogerNormal();
BK4819_PlayRogerNormal(Bandwidth);
} else if (gEeprom.ROGER == ROGER_MODE_MDC) {
BK4819_PlayRogerMDC();
}
+6 -6
View File
@@ -73,12 +73,14 @@ BOOT_Mode_t BOOT_GetMode(void)
void BOOT_ProcessMode(BOOT_Mode_t Mode)
{
GUI_DisplayType_t display = DISPLAY_MAIN;
if (Mode == BOOT_MODE_F_LOCK)
{
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
#endif
GUI_SelectNextDisplay(DISPLAY_MENU);
display = DISPLAY_MENU;
}
#ifdef ENABLE_AIRCOPY
else
@@ -119,11 +121,9 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode)
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
#endif
GUI_SelectNextDisplay(DISPLAY_AIRCOPY);
display = DISPLAY_AIRCOPY;
}
#endif
else
{
GUI_SelectNextDisplay(DISPLAY_MAIN);
}
GUI_SelectNextDisplay(display);
}
+54 -60
View File
@@ -510,17 +510,20 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
}
else
{ // squelch >= 1
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
// VHF UHF
PY25Q16_ReadBuffer(Base + 0x00, &pInfo->SquelchOpenRSSIThresh, 1); // 50 10
PY25Q16_ReadBuffer(Base + 0x10, &pInfo->SquelchCloseRSSIThresh, 1); // 40 5
PY25Q16_ReadBuffer(Base + 0x20, &pInfo->SquelchOpenNoiseThresh, 1); // 65 90
PY25Q16_ReadBuffer(Base + 0x30, &pInfo->SquelchCloseNoiseThresh, 1); // 70 100
PY25Q16_ReadBuffer(Base + 0x40, &pInfo->SquelchCloseGlitchThresh, 1); // 90 90
PY25Q16_ReadBuffer(Base + 0x50, &pInfo->SquelchOpenGlitchThresh, 1); // 100 100
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
// VHF UHF
uint8_t *sq_ptrs[6] = {
&pInfo->SquelchOpenRSSIThresh, // 50 10
&pInfo->SquelchCloseRSSIThresh, // 40 5
&pInfo->SquelchOpenNoiseThresh, // 65 90
&pInfo->SquelchCloseNoiseThresh, // 70 100
&pInfo->SquelchCloseGlitchThresh, // 90 90
&pInfo->SquelchOpenGlitchThresh // 100 100
};
for(uint8_t i = 0; i < 6; i++) {
PY25Q16_ReadBuffer(Base + (i * 0x10), sq_ptrs[i], 1);
}
uint16_t noise_open = pInfo->SquelchOpenNoiseThresh;
uint16_t noise_close = pInfo->SquelchCloseNoiseThresh;
@@ -544,14 +547,14 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
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->SquelchOpenGlitchThresh = (glitch_open > 255) ? 255 : glitch_open;
pInfo->SquelchCloseGlitchThresh = (glitch_close > 255) ? 255 : glitch_close;
pInfo->SquelchOpenRSSIThresh = MIN(rssi_open, 255);
pInfo->SquelchCloseRSSIThresh = MIN(rssi_close, 255);
pInfo->SquelchOpenGlitchThresh = MIN(glitch_open, 255);
pInfo->SquelchCloseGlitchThresh = MIN(glitch_close, 255);
#endif
pInfo->SquelchOpenNoiseThresh = (noise_open > 127) ? 127 : noise_open;
pInfo->SquelchCloseNoiseThresh = (noise_close > 127) ? 127 : noise_close;
pInfo->SquelchOpenNoiseThresh = MIN(noise_open, 127);
pInfo->SquelchCloseNoiseThresh = MIN(noise_close, 127);
}
// *******************************
@@ -698,18 +701,11 @@ 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 (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_ADD)
Frequency += pInfo->TX_OFFSET_FREQUENCY;
else if (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_SUB)
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
pInfo->freq_config_TX.Frequency = Frequency;
}
@@ -809,6 +805,9 @@ void RADIO_SetupRegisters(bool switchToForeground)
#endif
BK4819_SetFrequency(Frequency);
// Keep the demodulator in sync when retuning without entering RX audio.
RADIO_SetModulation(gRxVfo->Modulation);
BK4819_SetupSquelch(
gRxVfo->SquelchOpenRSSIThresh, gRxVfo->SquelchCloseRSSIThresh,
gRxVfo->SquelchOpenNoiseThresh, gRxVfo->SquelchCloseNoiseThresh,
@@ -841,7 +840,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
BK4819_SetCTCSSFrequency(SQL_TONE);
BK4819_SetTailDetection(SQL_TONE); // Default 550 = QS's 55Hz tone method
InterruptMask = BK4819_REG_3F_CxCSS_TAIL | BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL;
break;
case CODE_TYPE_CONTINUOUS_TONE:
@@ -853,24 +852,17 @@ void RADIO_SetupRegisters(bool switchToForeground)
// 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;
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
| BK4819_REG_3F_CTCSS_FOUND
| BK4819_REG_3F_CTCSS_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;
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
| BK4819_REG_3F_CDCSS_FOUND
| BK4819_REG_3F_CDCSS_LOST;
break;
}
@@ -888,11 +880,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
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;
InterruptMask |= BK4819_REG_3F_CTCSS_FOUND
| BK4819_REG_3F_CTCSS_LOST;
}
#endif
@@ -1099,11 +1088,12 @@ void RADIO_SetModulation(ModulationMode_t modulation)
// So for now we just keep it as is to maintain compatibility.
//
uint16_t uVar1 = BK4819_ReadRegister(0x31);
switch (modulation)
{
case MODULATION_AM:
{
uint16_t uVar1 = BK4819_ReadRegister(0x31);
BK4819_WriteRegister(0x31, uVar1 | 1); // AM Demodulation Enable
BK4819_WriteRegister(0x42, 0x6f5c);
BK4819_WriteRegister(0x2a, 0x7434);
@@ -1115,7 +1105,7 @@ void RADIO_SetModulation(ModulationMode_t modulation)
BK4819_WriteRegister(0x55, 0x31a9);
#endif
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gCurrentVfo), true);
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
break;
}
@@ -1123,7 +1113,6 @@ void RADIO_SetModulation(ModulationMode_t modulation)
case MODULATION_FM:
default:
{
uint16_t uVar1 = BK4819_ReadRegister(0x31);
BK4819_WriteRegister(0x31, uVar1 & 0xfffe); // AM Demodulation Disable
BK4819_WriteRegister(0x42, 0x6b5a);
BK4819_WriteRegister(0x2a, 0x7400);
@@ -1213,16 +1202,12 @@ void RADIO_PrepareTX(void)
RADIO_SelectCurrentVfo();
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
#ifdef ENABLE_FEAT_F4HWN
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0 && gCurrentVfo->TX_LOCK == true
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
&& gAlarmState != ALARM_STATE_SITE_ALARM
#endif
#else
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
&& gAlarmState != ALARM_STATE_SITE_ALARM
#endif
&& gCurrentVfo->TX_LOCK == true
#endif
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
&& gAlarmState != ALARM_STATE_SITE_ALARM
#endif
){
// TX frequency not allowed
@@ -1343,7 +1328,16 @@ void RADIO_SendCssTail(void)
void RADIO_SendEndOfTransmission(void)
{
BK4819_PlayRoger();
BK4819_FilterBandwidth_t Bandwidth = gCurrentVfo->CHANNEL_BANDWIDTH;
#ifdef ENABLE_FEAT_F4HWN_NARROWER
if(Bandwidth == BK4819_FILTER_BW_NARROW && gSetting_set_nfm == 1)
{
Bandwidth = BK4819_FILTER_BW_NARROWER;
}
#endif
BK4819_PlayRoger(Bandwidth);
DTMF_SendEndOfTransmission();
// send the CTCSS/DCS tail tone - allows the receivers to mute the usual FM squelch tail/crash
+113 -61
View File
@@ -20,13 +20,37 @@
#include "misc.h"
#include "driver/vcp.h"
#include "driver/keyboard.h"
#include "driver/bk4819.h"
// SRAM optimization: minimize static allocations
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
// - No currentFrame or deltaFrame static buffers
static uint8_t previousFrame[1024] = {0};
// - previousHash: one fingerprint per 8-byte chunk instead of a full
// 1024-byte copy of the previous frame (chunks are only compared,
// never retransmitted from history). A 16-bit fingerprint keeps the
// collision odds at ~1/65536 per chunk, so a stale chunk slipping
// through is rare; the forcedBlock rotation repairs that residual
// within at most 128 frames.
// Stack optimization: chunks are computed on demand straight from
// gStatusLine/gFrameBuffer instead of building the whole 1024-byte
// frame on the stack. Changed chunks are tracked in a 16-byte bitmap.
// Peak stack drops from ~1.3 KB to ~40 bytes, at the cost of
// transforming each transmitted chunk twice (negligible next to the
// blocking UART transfer).
static uint16_t previousHash[128];
static uint8_t previousStateFlags = 0xFF;
static uint8_t forcedBlock = 0;
static uint8_t keepAlive = 3;
static bool hasConnectionPing = false;
static bool wasConnected = false;
// FNV-1a over one 8-byte chunk, folded to 16 bits
static uint16_t SCREENSHOT_Hash(const uint8_t *data)
{
uint32_t h = 2166136261u;
for (uint8_t i = 0; i < 8; i++) {
h = (h ^ data[i]) * 16777619u;
}
return (uint16_t)(h ^ (h >> 16));
}
void SCREENSHOT_ParseInput(void)
{
@@ -35,10 +59,12 @@ void SCREENSHOT_ParseInput(void)
if (UART_IsCableConnected()) {
keepAlive = 15;
hasConnectionPing = true;
gUSB_ScreenshotEnabled = false;
}
else if (VCP_ScreenshotPing()) {
keepAlive = 15;
hasConnectionPing = true;
gUSB_ScreenshotEnabled = true;
}
}
@@ -52,28 +78,65 @@ static void SCREENSHOT_Send(const uint8_t *buf, uint16_t len)
}
}
void SCREENSHOT_Line(uint8_t *src, uint8_t *dest, uint16_t *idx) {
for (uint8_t b = 0; b < 8; b++) {
for (uint8_t i = 0; i < 128; i += 8) {
uint8_t acc = 0;
for (uint8_t k = 0; k < 8; k++) {
if (src[i + k] & (1 << b)) acc |= (1 << k);
}
dest[(*idx)++] = gSetting_set_inv ? ~acc : acc;
enum {
SCREENSHOT_CHUNK_SIZE = 8,
SCREENSHOT_CHUNKS_PER_LINE = 16,
SCREENSHOT_HALF_LINE_COLUMNS = LCD_WIDTH / 2,
SCREENSHOT_MARKER_BASE = 0xF0,
SCREENSHOT_FLAG_DEEP_SLEEP = 1 << 0,
SCREENSHOT_FLAG_LED_RED = 1 << 1,
SCREENSHOT_FLAG_LED_GREEN = 1 << 2,
};
static uint8_t SCREENSHOT_StateFlags(void)
{
uint8_t flags = 0;
#ifdef ENABLE_FEAT_F4HWN_SLEEP
if (gWakeUp)
flags |= SCREENSHOT_FLAG_DEEP_SLEEP;
#endif
if (BK4819_IsGpioOutSet(BK4819_GPIO5_PIN1_RED))
flags |= SCREENSHOT_FLAG_LED_RED;
if (BK4819_IsGpioOutSet(BK4819_GPIO6_PIN2_GREEN))
flags |= SCREENSHOT_FLAG_LED_GREEN;
return flags;
}
bool SCREENSHOT_HasPendingStateChange(void)
{
if (gUART_LockScreenshot > 0 || keepAlive == 0 || !hasConnectionPing)
return false;
return !wasConnected || SCREENSHOT_StateFlags() != previousStateFlags;
}
// Compute one 8-byte output chunk directly from the display buffers.
// Frame layout: per line (status + 7 frame lines), 8 bit layers of
// 16 bytes each. Each bit layer is split into two 64-column chunks.
static void SCREENSHOT_Chunk(uint8_t chunkIdx, uint8_t *dest)
{
const uint8_t chunkInLine = chunkIdx % SCREENSHOT_CHUNKS_PER_LINE;
const uint8_t line = chunkIdx / SCREENSHOT_CHUNKS_PER_LINE;
const uint8_t bit = chunkInLine / 2;
const uint8_t columnBase = (chunkInLine % 2) * SCREENSHOT_HALF_LINE_COLUMNS;
const uint8_t *src = (line == 0 ? gStatusLine : gFrameBuffer[line - 1])
+ columnBase;
for (uint8_t j = 0; j < SCREENSHOT_CHUNK_SIZE; j++) {
uint8_t acc = 0;
for (uint8_t k = 0; k < SCREENSHOT_CHUNK_SIZE; k++) {
if (src[j * SCREENSHOT_CHUNK_SIZE + k] & (1 << bit)) acc |= (1 << k);
}
dest[j] = gSetting_set_inv ? ~acc : acc;
}
}
void SCREENSHOT_Update(bool force)
{
// Build frame in a temporary stack buffer
// This is 1024 bytes but it's temporary and gets freed after the function
uint8_t frameBuffer[1024];
uint16_t index = 0;
uint8_t acc = 0;
uint8_t bitCount = 0;
static bool wasConnected = false;
if (SCREENSHOT_IsLocked())
return;
@@ -81,6 +144,8 @@ void SCREENSHOT_Update(bool force)
if (--keepAlive == 0) {
// Connection just lost → reset state for next reconnection
wasConnected = false;
hasConnectionPing = false;
previousStateFlags = 0xFF;
return;
}
} else {
@@ -90,46 +155,31 @@ void SCREENSHOT_Update(bool force)
// Connection is alive — detect reconnection and force full frame
if (!wasConnected) {
force = true;
wasConnected = true;
}
// ==== BUILD FRAME ONCE ====
// Status line: 8 bit layers × 128 columns
SCREENSHOT_Line(gStatusLine, frameBuffer, &index);
// Frame buffer: 7 lines × 8 bit layers × 128 columns
for (uint8_t l = 0; l < 7; l++) {
SCREENSHOT_Line(gFrameBuffer[l], frameBuffer, &index);
}
if (bitCount > 0)
frameBuffer[index++] = acc;
if (index != 1024)
return;
const uint8_t stateFlags = SCREENSHOT_StateFlags();
const bool stateChanged = (stateFlags != previousStateFlags);
// ==== FIRST PASS: Count changed chunks ====
uint16_t deltaLen = 0;
uint8_t changedChunks[128]; // List of changed chunk indices
uint8_t changedCount = 0;
uint8_t changedBitmap[16] = {0}; // 1 bit per chunk
uint8_t chunk[9]; // [0] = index, [1..8] = payload
for (uint8_t chunk = 0; chunk < 128; chunk++) {
uint8_t *cur = &frameBuffer[chunk * 8];
uint8_t *prev = &previousFrame[chunk * 8];
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
bool changed = memcmp(cur, prev, 8) != 0;
bool isForced = (chunk == forcedBlock);
bool fullUpdate = force;
bool changed = SCREENSHOT_Hash(&chunk[1]) != previousHash[chunkIdx];
bool isForced = (chunkIdx == forcedBlock);
if (changed || isForced || fullUpdate) {
changedChunks[changedCount++] = chunk;
if (changed || isForced || force) {
changedBitmap[chunkIdx >> 3] |= 1 << (chunkIdx & 7);
deltaLen += 9;
}
}
forcedBlock = (forcedBlock + 1) % 128;
if (deltaLen == 0)
if (deltaLen == 0 && !stateChanged)
return;
// Skip transmission if a key is currently pressed
@@ -137,10 +187,9 @@ void SCREENSHOT_Update(bool force)
if (gKeyReading0 != KEY_INVALID)
return;
// ==== Send version marker (for backward compatibility detection) ====
// New format: sends 0xFF before header
// Old format: doesn't exist, so viewers can differentiate
uint8_t versionMarker = 0xFF;
// ==== Send version marker and state flags ====
// 0xF0 keeps a resync-safe marker before the standard AA 55 header.
uint8_t versionMarker = SCREENSHOT_MARKER_BASE | stateFlags;
SCREENSHOT_Send(&versionMarker, 1);
// ==== Send header ====
@@ -153,22 +202,25 @@ void SCREENSHOT_Update(bool force)
SCREENSHOT_Send(header, 5);
// ==== SECOND PASS: Send only changed chunks ====
uint8_t chunk[9];
for (uint8_t i = 0; i < changedCount; i++) {
uint8_t chunkIdx = changedChunks[i];
uint8_t *cur = &frameBuffer[chunkIdx * 8];
uint8_t *prev = &previousFrame[chunkIdx * 8];
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
if (!(changedBitmap[chunkIdx >> 3] & (1 << (chunkIdx & 7))))
continue;
chunk[0] = chunkIdx;
memcpy(&chunk[1], cur, 8);
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
SCREENSHOT_Send(chunk, 9);
// Update previousFrame for next comparison
memcpy(prev, cur, 8);
// Update the fingerprint only once the chunk is actually sent,
// so chunks skipped by an early return stay marked as changed.
// Hashing the recomputed payload also keeps the fingerprint in
// sync if the display buffer changed between the two passes.
previousHash[chunkIdx] = SCREENSHOT_Hash(&chunk[1]);
}
uint8_t end = 0x0A;
SCREENSHOT_Send(&end, 1);
}
previousStateFlags = stateFlags;
wasConnected = true;
}
+2 -1
View File
@@ -19,5 +19,6 @@
void SCREENSHOT_Update(bool force);
void SCREENSHOT_ParseInput(void);
bool SCREENSHOT_HasPendingStateChange(void);
#endif
#endif
+4 -3
View File
@@ -455,15 +455,16 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
// TODO: address TBD
PY25Q16_ReadBuffer(0x00A158, Data, 8);
const uint8_t set_ptt_scn_sav = Data[7] & 0x0F;
const bool set_ptt_scn_sav_erased = Data[7] == 0xFF;
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < (SET_SAV_LEN << 2);
const bool set_ptt_scn_sav_valid = !set_ptt_scn_sav_erased && set_ptt_scn_sav < (SET_SAV_LEN << 2);
#else
const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < 4;
const bool set_ptt_scn_sav_valid = !set_ptt_scn_sav_erased && set_ptt_scn_sav < 4;
#endif
gSetting_set_pwr = (((Data[7] & 0xF0) >> 4) < 7) ? ((Data[7] & 0xF0) >> 4) : 0;
gSetting_set_ptt = set_ptt_scn_sav_valid ? (set_ptt_scn_sav & 0x01) : 0;
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
gSetting_set_scn = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav & 0x02) == 0) : 1;
gSetting_set_scn = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav & 0x02) == 0) : 0;
#endif
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
gSetting_set_sav = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav >> 2) & 0x03) : SET_SAV_OFF;
+17
View File
@@ -325,6 +325,23 @@ static void sort(int16_t *a, int16_t *b)
}
}
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line,
bool statusbar, bool fill, uint8_t end)
{
// First draw the string normally
GUI_DisplaySmallest(pString, x, (Line * 8) + 1, statusbar, fill);
// Now invert the framebuffer/statusline bits for the rendered area
uint8_t start = (x - 2);
uint8_t *buffer = statusbar ? gStatusLine : gFrameBuffer[Line];
buffer[start] ^= 0x3E;
for (uint8_t i = start + 1; i < end; i++) {
buffer[i] ^= 0x7F;
}
buffer[end] ^= 0x3E;
}
void UI_DisplayUnlockKeyboard(uint8_t shift) {
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
{ // tell user how to unlock the keyboard
+1
View File
@@ -38,6 +38,7 @@ void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black
void PutPixel(uint8_t x, uint8_t y, bool fill);
void PutPixelStatus(uint8_t x, uint8_t y, bool fill);
void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y, bool statusbar, bool fill);
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line, bool statusbar, bool fill, uint8_t endX);
void UI_DisplayUnlockKeyboard(uint8_t shift);
bool IsEmptyName(const char *name, uint8_t len);
#endif
+119 -5
View File
@@ -140,6 +140,71 @@ const char *VfoStateStr[] = {
[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
static void ScanProgress_ResetSession(void)
{
@@ -287,8 +352,7 @@ static void ScanProgress_DrawGaugeLine(uint8_t line, uint32_t current_index, uin
current_index = total;
head_col = (total <= 1) ? (fill_cols - 1) : ((current_index - 1) * (fill_cols - 1)) / (total - 1);
if (head_col >= fill_cols)
head_col = fill_cols - 1;
head_col = MIN(head_col, fill_cols - 1);
gFrameBuffer[line][gauge_left] = 0x0c;
gFrameBuffer[line][gauge_left + 1] = 0x12;
@@ -1169,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];
@@ -1267,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;
}
@@ -1280,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
}
@@ -1443,10 +1538,15 @@ void UI_DisplayMain(void)
#endif
}
if(TX_freq_check(frequency) != 0 && gEeprom.VfoInfo[vfo_num].TX_LOCK == true)
#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((gScanStateDir == SCAN_OFF || vfo_num != gEeprom.RX_VFO) && TX_freq_check(frequency) != 0 && gEeprom.VfoInfo[vfo_num].TX_LOCK == true)
{
if (!FUNCTION_IsRx() || RxOnVfofrequency != frequency)
memcpy(p_line0 + 25, BITMAP_VFO_Lock, sizeof(BITMAP_VFO_Lock));
memcpy(p_line0 + 24, BITMAP_VFO_Lock, sizeof(BITMAP_VFO_Lock));
}
if (IS_MR_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
@@ -1613,6 +1713,9 @@ void UI_DisplayMain(void)
xStart = 117;
}
#ifdef ENABLE_FEAT_F4HWN
GUI_DisplaySmallestInverse(displayStr, xStart + 2, line, false, true, 127);
#else
GUI_DisplaySmallest(displayStr, xStart + 2, line == 0 ? 1 : 33, false, true);
gFrameBuffer[line][xStart] ^= 0x3E;
@@ -1620,6 +1723,7 @@ void UI_DisplayMain(void)
gFrameBuffer[line][x] ^= 0x7F;
}
gFrameBuffer[line][127] ^= 0x3E;
#endif
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
{
@@ -2099,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
@@ -2237,13 +2346,18 @@ void UI_DisplayMain(void)
if (isMainOnly() && !gDTMF_InputMode)
{
sprintf(String, "VFO %s", activeTxVFO ? "B" : "A");
GUI_DisplaySmallest(String, 107, 50, false, true);
#ifdef ENABLE_FEAT_F4HWN
GUI_DisplaySmallestInverse(String, 107, 6, false, true, 127);
#else
GUI_DisplaySmallest(String, 107, 49, false, true);
gFrameBuffer[6][105] ^= 0x7C;
for (uint8_t x = 106; x < 127; x++) {
gFrameBuffer[6][x] ^= 0xFE;
}
gFrameBuffer[6][127] ^= 0x7C;
#endif
/*
UI_PrintStringSmallBold(String, 92, 0, 6);
+78 -65
View File
@@ -44,8 +44,6 @@ static void convertTime(uint8_t *line, uint8_t type)
uint8_t m = t / 60;
uint8_t s = t - (m * 60); // Replace modulo with subtraction for efficiency
gStatusLine[0] = gStatusLine[7] = gStatusLine[14] = 0x00; // Quick fix on display (on scanning I, II, etc.)
char str[6];
sprintf(str, "%02u:%02u", m, s);
UI_PrintStringSmallBufferNormal(str, line);
@@ -64,9 +62,18 @@ void UI_DisplayStatus()
uint8_t *line = gStatusLine;
unsigned int x = 0;
unsigned int x1 = 0;
char str[8] = "";
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
convertTime(line, !isTransmit);
x += 39;
} else {
#endif
#ifdef ENABLE_NOAA
// NOAA indicator
if (!(gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) && gIsNoaaMode) { // NOASS SCAN indicator
@@ -85,7 +92,7 @@ void UI_DisplayStatus()
x += 8;
#endif
unsigned int x1 = x;
x1 = x;
#ifdef ENABLE_DTMF_CALLING
if (gSetting_KILLED) {
@@ -97,7 +104,6 @@ void UI_DisplayStatus()
{ // SCAN indicator
if (gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) {
if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode
uint8_t end = 0;
if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1)
@@ -124,6 +130,9 @@ void UI_DisplayStatus()
end += 4;
}
#ifdef ENABLE_FEAT_F4HWN
GUI_DisplaySmallestInverse(str, 2, 0, true, true, end);
#else
GUI_DisplaySmallest(str, 2, 1, true, true);
gStatusLine[0] ^= 0x3E;
@@ -132,6 +141,7 @@ void UI_DisplayStatus()
gStatusLine[x] ^= 0x7F;
}
gStatusLine[end] ^= 0x3E;
#endif
}
else { // frequency mode
memcpy(line + x + 1, gFontS, sizeof(gFontS));
@@ -161,54 +171,49 @@ void UI_DisplayStatus()
#endif
if(!SCANNER_IsScanning()) {
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
convertTime(line, !isTransmit);
}
else
#endif
{
if(!gAirCopyBootMode) {
const void *src = NULL; // Pointer to the font/bitmap to copy
size_t sSize = 0; // Size of the font/bitmap
uint8_t sOff = 2; // Offset relative to the reference position
if(!gAirCopyBootMode) {
const void *src = NULL; // Pointer to the font/bitmap to copy
size_t sSize = 0; // Size of the font/bitmap
uint8_t sOff = 2; // Offset relative to the reference position
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if (gEeprom.MENU_LOCK) {
src = gFontRO;
sSize = sizeof(gFontRO);
} else
#endif
{
uint8_t xb = (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if (gEeprom.MENU_LOCK) {
src = gFontRO;
sSize = sizeof(gFontRO);
} else
#endif
{
uint8_t xb = (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) {
if (gDualWatchActive) { // DWR - dual watch + respond
src = gFontDWR;
sOff = xb ? 2 : 0;
sSize = sizeof(gFontDWR) - (xb ? 5 : 0);
} else {
src = gFontHold;
sOff = 3;
sSize = sizeof(gFontHold);
}
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) {
if (gDualWatchActive) { // DWR - dual watch + respond
src = gFontDWR;
sOff = xb ? 2 : 0;
sSize = sizeof(gFontDWR) - (xb ? 5 : 0);
} else {
src = xb ? gFontXB : gFontMO; // XB - crossband
sSize = xb ? sizeof(gFontXB) : sizeof(gFontMO); // MO - main only
src = gFontHold;
sOff = 3;
sSize = sizeof(gFontHold);
}
} else {
src = xb ? gFontXB : gFontMO; // XB - crossband
sSize = xb ? sizeof(gFontXB) : sizeof(gFontMO); // MO - main only
}
}
// Perform the memcpy if a source was selected
if (src) {
memcpy(line + x + sOff, src, sSize);
}
// Perform the memcpy if a source was selected
if (src) {
memcpy(line + x + sOff, src, sSize);
}
}
}
x += sizeof(gFontDWR) + 3;
#endif
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
}
#endif
#ifdef ENABLE_VOX
// VOX indicator
if (gEeprom.VOX_SWITCH) {
@@ -255,8 +260,19 @@ void UI_DisplayStatus()
}
#endif
else if (gBackLight) {
src = gFontLight;
size = sizeof(gFontLight);
// Distinguish the manual backlight sub-state: BACKLIGHT_TIME == 0
// means the light is currently off (hollow bulb), otherwise it is
// forced on (bulb with filament).
if (gEeprom.BACKLIGHT_TIME == 0) {
src = gFontLightOff;
size = sizeof(gFontLightOff);
}
else
{
src = gFontLight;
size = sizeof(gFontLight);
}
}
#ifdef ENABLE_FEAT_F4HWN_CHARGING_C
else if (gChargingWithTypeC) {
@@ -275,38 +291,35 @@ void UI_DisplayStatus()
UI_DrawBattery(line + x2, gBatteryDisplayLevel, gLowBatteryBlink);
bool BatTxt = true;
switch (gSetting_battery_text) {
default:
case 0:
BatTxt = false;
break;
case 1: // voltage
const uint16_t voltage = (gBatteryVoltageAverage <= 999) ? gBatteryVoltageAverage : 999; // limit to 9.99V
sprintf(str, "%u.%02u", voltage / 100, voltage % 100);
break;
case 1: // voltage
case 2: // percentage
//gBatteryVoltageAverage = 999;
sprintf(str, "%02u%%", BATTERY_VoltsToPercent(gBatteryVoltageAverage));
if (gSetting_battery_text == 1) {
const uint16_t voltage = MIN(gBatteryVoltageAverage, 999); // limit to 9.99V
sprintf(str, "%u.%02u", voltage / 100, voltage % 100);
} else {
//gBatteryVoltageAverage = 999;
sprintf(str, "%02u%%", BATTERY_VoltsToPercent(gBatteryVoltageAverage));
}
x2 -= (7 * strlen(str));
UI_PrintStringSmallBufferNormal(str, line + x2);
/*
uint8_t shift = (strlen(str) < 5) ? 92 : 88;
GUI_DisplaySmallest(str, shift, 1, true, true);
for (uint8_t i = shift - 2; i < 110; i++) {
gStatusLine[i] ^= 0x7F; // invert
}
*/
break;
}
if (BatTxt) {
x2 -= (7 * strlen(str));
UI_PrintStringSmallBufferNormal(str, line + x2);
/*
uint8_t shift = (strlen(str) < 5) ? 92 : 88;
GUI_DisplaySmallest(str, shift, 1, true, true);
for (uint8_t i = shift - 2; i < 110; i++) {
gStatusLine[i] ^= 0x7F; // invert
}
*/
}
// **************
ST7565_BlitStatusLine();
+5 -1
View File
@@ -70,6 +70,8 @@
"ENABLE_FEAT_F4HWN_MEM": false,
"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,
@@ -77,7 +79,7 @@
"ENABLE_UART_RW_BK_REGS": false,
"ENABLE_SWD": false,
"VERSION_STRING_1": "v0.22",
"VERSION_STRING_2": "v5.5.0"
"VERSION_STRING_2": "v5.6.1"
}
},
{
@@ -199,6 +201,8 @@
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
"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,
+1 -1
View File
@@ -42,7 +42,7 @@ Anyway, have fun.
# Donations
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, Victor TI2SYS, Tobi DG9LAY, Deaglan K4DFQ, Catherine PALMER, Brian WA6JFK, Stéphane Hintzy, Roger F1HCN and Marcin Kusaj for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C / F4WFS (3 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, Victor TI2SYS, Tobi DG9LAY, Deaglan K4DFQ, Catherine PALMER, Brian WA6JFK, Stéphane Hintzy, Roger F1HCN, Marcin Kusaj, Flavio Cottarelli and Bob N1MLZ for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
## Table of Contents
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.