mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-04 12:07:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b4b3e2a71 | ||
|
|
65a5c9f4ef | ||
|
|
a2936402c2 | ||
|
|
f069ed935f | ||
|
|
dcbbd744ac | ||
|
|
bca06e3382 | ||
|
|
8723863fb0 | ||
|
|
c775fa8b2f | ||
|
|
80e7d4b3d1 | ||
|
|
e6a727eeda | ||
|
|
887e18efe6 | ||
|
|
20b87feb1d | ||
|
|
e2e9c9ba60 | ||
|
|
337eb0cb12 | ||
|
|
504a2b0fb0 | ||
|
|
16b07c6374 | ||
|
|
f5b20d4f80 | ||
|
|
ae14bf5df3 | ||
|
|
9dc85852ba | ||
|
|
f7d29f6a14 | ||
|
|
1184618731 | ||
|
|
3d15d59ce0 | ||
|
|
15fccc31d6 | ||
|
|
48f753caaf | ||
|
|
edc4673983 | ||
|
|
9e03f02296 | ||
|
|
25ea1f827b | ||
|
|
8f11a32c9d | ||
|
|
576b8c552f | ||
|
|
85db5a2394 | ||
|
|
98989d4f0a | ||
|
|
8c976cfffd | ||
|
|
0ce5422db3 | ||
|
|
d939009949 | ||
|
|
50efade5c4 | ||
|
|
682a59fc0e | ||
|
|
3d3f370b86 | ||
|
|
eebeba09db | ||
|
|
6cceb26814 | ||
|
|
a77b132fa5 | ||
|
|
86aa05f1ec | ||
|
|
953f579adb | ||
|
|
d429b3afe8 | ||
|
|
a65a4f601a | ||
|
|
546cd35681 | ||
|
|
8ecf976067 | ||
|
|
3e54dc1c00 | ||
|
|
9eb9a9da5c | ||
|
|
a74529b9ec | ||
|
|
7ccb53572a | ||
|
|
c515188a06 | ||
|
|
cdcb950ccd | ||
|
|
13ca3b8a39 | ||
|
|
34acff0fbf | ||
|
|
fa75ab4243 | ||
|
|
1589d8bdd7 | ||
|
|
a5597a1ab9 | ||
|
|
955b245e66 | ||
|
|
2c4bca4202 | ||
|
|
6c4c240e9a |
No files matched your search
@@ -15,3 +15,4 @@ k5_eeprom.raw
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
AGENTS.md
|
||||
@@ -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)
|
||||
@@ -215,6 +217,11 @@ if(ENABLE_FEAT_F4HWN_BEAM AND NOT ENABLE_AIRCOPY)
|
||||
endif()
|
||||
enable_feature(ENABLE_FEAT_F4HWN_QRCODE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_LOGO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_LOGO_SAV AND NOT ENABLE_FEAT_F4HWN_LOGO)
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_LOGO_SAV requires ENABLE_FEAT_F4HWN_LOGO.")
|
||||
endif()
|
||||
|
||||
# ---- DEBUGGING ----
|
||||
|
||||
|
||||
+311
-6
@@ -72,11 +72,13 @@
|
||||
#include "sram-overlay.h"
|
||||
#endif
|
||||
#include "ui/battery.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/status.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/welcome.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
@@ -86,6 +88,19 @@ static bool flagSaveVfo;
|
||||
static bool flagSaveSettings;
|
||||
static bool flagSaveChannel;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
static KEY_Code_t gSleepWakeKey = KEY_INVALID;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
static bool gScreenSaverDisplayed;
|
||||
static uint8_t gScreenSaverTick;
|
||||
static uint16_t gMatrixRandom = 0xACE1;
|
||||
static uint8_t gMatrixHeads[32];
|
||||
static uint8_t gMatrixSpeeds[32];
|
||||
static KEY_Code_t gScreenSaverWakeKey = KEY_INVALID;
|
||||
#endif
|
||||
|
||||
static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
|
||||
@@ -110,11 +125,195 @@ static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM-1);
|
||||
static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
static uint8_t ScreenSaverRandom(void)
|
||||
{
|
||||
gMatrixRandom = (uint16_t)(gMatrixRandom * 2053u + 13849u);
|
||||
return (uint8_t)(gMatrixRandom >> 8);
|
||||
}
|
||||
|
||||
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 (fill)
|
||||
*target |= pattern;
|
||||
else
|
||||
*target &= (uint8_t)~pattern;
|
||||
}
|
||||
|
||||
static void ScreenSaverDrawMatrixGlyph(uint8_t x, int16_t y, uint8_t glyph)
|
||||
{
|
||||
static const uint8_t glyphs[][3] = {
|
||||
{0x1F, 0x11, 0x1F},
|
||||
{0x15, 0x1F, 0x15},
|
||||
{0x1D, 0x15, 0x17},
|
||||
{0x17, 0x15, 0x1D},
|
||||
{0x1F, 0x04, 0x1F},
|
||||
{0x1B, 0x15, 0x1B},
|
||||
{0x0E, 0x11, 0x0E},
|
||||
{0x11, 0x0A, 0x04}
|
||||
};
|
||||
|
||||
for (uint8_t dx = 0; dx < 3; dx++) {
|
||||
uint8_t pixels = glyphs[glyph & 7u][dx];
|
||||
for (uint8_t dy = 0; dy < 5; dy++) {
|
||||
const int16_t py = y + dy;
|
||||
if ((pixels & (1u << dy)) && py >= 0 && py < LCD_HEIGHT)
|
||||
ScreenSaverSetPixel(x + dx, (uint8_t)py, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ScreenSaverRenderMatrix(bool reset)
|
||||
{
|
||||
if (reset) {
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) {
|
||||
gMatrixHeads[i] = ScreenSaverRandom() % 112u;
|
||||
gMatrixSpeeds[i] = 1u + (ScreenSaverRandom() % 3u);
|
||||
}
|
||||
} else {
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) {
|
||||
gMatrixHeads[i] += gMatrixSpeeds[i];
|
||||
if (gMatrixHeads[i] > 112u)
|
||||
gMatrixHeads[i] = ScreenSaverRandom() & 0x0Fu;
|
||||
}
|
||||
}
|
||||
|
||||
UI_StatusClear();
|
||||
UI_DisplayClear();
|
||||
|
||||
for (uint8_t col = 0; col < ARRAY_SIZE(gMatrixHeads); col++) {
|
||||
const uint8_t x = col * 4u;
|
||||
for (uint8_t trail = 0; trail < 8; trail++) {
|
||||
const int16_t y = (int16_t)gMatrixHeads[col] - ((int16_t)trail * 7);
|
||||
const uint8_t glyph = (uint8_t)(col + trail + gMatrixHeads[col]);
|
||||
|
||||
if (trail == 0) {
|
||||
ScreenSaverDrawMatrixGlyph(x, y, glyph);
|
||||
} else if ((trail < 4) || ((glyph & 1u) != 0)) {
|
||||
ScreenSaverDrawMatrixGlyph(x, y, glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void ScreenSaverScrollLogoLine(uint8_t *line)
|
||||
{
|
||||
const uint8_t first = line[0];
|
||||
|
||||
memmove(line, line + 1, LCD_WIDTH - 1);
|
||||
line[LCD_WIDTH - 1] = first;
|
||||
}
|
||||
|
||||
static void ScreenSaverRenderLogoPlus(bool reset)
|
||||
{
|
||||
if (reset) {
|
||||
UI_DisplayLogo();
|
||||
return;
|
||||
}
|
||||
|
||||
ScreenSaverScrollLogoLine(gStatusLine);
|
||||
for (uint8_t line = 0; line < FRAME_LINES; line++)
|
||||
ScreenSaverScrollLogoLine(gFrameBuffer[line]);
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void ScreenSaverUpdateViewer(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ScreenSaverCanDisplay(void)
|
||||
{
|
||||
if (gSetting_set_sav == SET_SAV_OFF ||
|
||||
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_FoundFrequency)
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
|| gBeamActive
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN)
|
||||
return true;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gScreenToDisplay == DISPLAY_FM)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ScreenSaverTryDisplay(void)
|
||||
{
|
||||
if (!ScreenSaverCanDisplay())
|
||||
return;
|
||||
|
||||
if (gSetting_set_sav == SET_SAV_LOGO)
|
||||
UI_DisplayLogo();
|
||||
else if (gSetting_set_sav == SET_SAV_LOGO_PLUS)
|
||||
ScreenSaverRenderLogoPlus(true);
|
||||
else if (gSetting_set_sav == SET_SAV_MATRIX)
|
||||
ScreenSaverRenderMatrix(true);
|
||||
|
||||
gScreenSaverDisplayed = true;
|
||||
gScreenSaverTick = 0;
|
||||
gUpdateDisplay = false;
|
||||
gUpdateStatus = false;
|
||||
ScreenSaverUpdateViewer();
|
||||
}
|
||||
|
||||
static void ScreenSaverExit(void)
|
||||
{
|
||||
if (gScreenSaverDisplayed) {
|
||||
gScreenSaverDisplayed = false;
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APP_IsScreenSaverDisplayed(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
return gScreenSaverDisplayed;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CheckForIncoming(void)
|
||||
{
|
||||
if (!g_SquelchLost)
|
||||
return; // squelch is closed
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
|
||||
// squelch is open
|
||||
|
||||
if (gScanStateDir == SCAN_OFF)
|
||||
@@ -495,6 +694,10 @@ void APP_StartListening(FUNCTION_Type_t function)
|
||||
{
|
||||
const unsigned int vfo = gEeprom.RX_VFO;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
gRxTimerCountdown_500ms = 7200;
|
||||
#endif
|
||||
@@ -1416,6 +1619,36 @@ void APP_TimeSlice10ms(void)
|
||||
|
||||
if (gUpdateDisplayCurrent) {
|
||||
gUpdateDisplay = false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
bool screenSaverRendered = false;
|
||||
|
||||
if (gScreenSaverDisplayed) {
|
||||
if (gUpdateDisplayCurrent) {
|
||||
gUpdateDisplayCurrent = false;
|
||||
} else if (gUpdateStatusCurrent) {
|
||||
gUpdateStatusCurrent = false;
|
||||
gUpdateStatus = false;
|
||||
}
|
||||
|
||||
if (gSetting_set_sav == SET_SAV_MATRIX) {
|
||||
if (++gScreenSaverTick >= 8u) {
|
||||
gScreenSaverTick = 0;
|
||||
ScreenSaverRenderMatrix(false);
|
||||
screenSaverRendered = true;
|
||||
}
|
||||
} else if (gSetting_set_sav == SET_SAV_LOGO_PLUS) {
|
||||
if (++gScreenSaverTick >= 16u) {
|
||||
gScreenSaverTick = 0;
|
||||
ScreenSaverRenderLogoPlus(false);
|
||||
screenSaverRendered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gUpdateDisplayCurrent) {
|
||||
GUI_DisplayScreen();
|
||||
}
|
||||
|
||||
@@ -1424,7 +1657,13 @@ void APP_TimeSlice10ms(void)
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
|
||||
if (gUpdateDisplayCurrent || gUpdateStatusCurrent
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
|| screenSaverRendered
|
||||
#endif
|
||||
) {
|
||||
SCREENSHOT_Update(false);
|
||||
} else if (SCREENSHOT_HasPendingStateChange()) {
|
||||
SCREENSHOT_Update(false);
|
||||
}
|
||||
#endif
|
||||
@@ -1521,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()) {
|
||||
@@ -1563,6 +1806,17 @@ void APP_TimeSlice500ms(void)
|
||||
if (--gKeypadLocked == 0)
|
||||
gUpdateDisplay = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
if (gSetting_set_tmr && (gCurrentFunction == FUNCTION_TRANSMIT || FUNCTION_IsRx())) {
|
||||
const uint16_t timerCountdown = (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
? gTxTimerCountdown_500ms
|
||||
: gRxTimerCountdown_500ms;
|
||||
|
||||
if ((timerCountdown & 1u) != 0u)
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gKeyInputCountdown > 0)
|
||||
{
|
||||
if (--gKeyInputCountdown == 0)
|
||||
@@ -1634,15 +1888,23 @@ void APP_TimeSlice500ms(void)
|
||||
&& gEeprom.BACKLIGHT_TIME < 61
|
||||
) {
|
||||
BACKLIGHT_TurnOff();
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverTryDisplay();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if (gSleepModeCountdown_500ms == gSetting_set_off * 120 && gWakeUp) {
|
||||
//ST7565_Init();
|
||||
ST7565_FixInterfGlitch();
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
|
||||
gWakeUp = false;
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx()
|
||||
@@ -1658,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);
|
||||
@@ -1815,6 +2080,17 @@ void APP_TimeSlice500ms(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
if (gBacklightCountdown_500ms == 0 &&
|
||||
gEeprom.BACKLIGHT_TIME > 0 &&
|
||||
gEeprom.BACKLIGHT_TIME < 61 &&
|
||||
!gAskToSave &&
|
||||
!gCssBackgroundScan)
|
||||
{
|
||||
ScreenSaverTryDisplay();
|
||||
}
|
||||
#endif
|
||||
|
||||
BATTERY_TimeSlice500ms();
|
||||
SCANNER_TimeSlice500ms();
|
||||
UI_MAIN_TimeSlice500ms();
|
||||
@@ -1876,24 +2152,53 @@ static void ALARM_Off(void)
|
||||
|
||||
static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if (gSleepWakeKey != KEY_INVALID) {
|
||||
if (Key == gSleepWakeKey && !bKeyPressed)
|
||||
gSleepWakeKey = KEY_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
if(gWakeUp)
|
||||
{
|
||||
if(!bKeyPressed || Key == KEY_PTT)
|
||||
if(bKeyPressed || Key == KEY_PTT)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
if(Key != KEY_PTT)
|
||||
{
|
||||
Key = KEY_INVALID;
|
||||
gSleepWakeKey = Key;
|
||||
gBeepToPlay = BEEP_NONE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if(Key != KEY_PTT)
|
||||
{
|
||||
Key = KEY_INVALID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
if (gScreenSaverWakeKey != KEY_INVALID) {
|
||||
if (Key == gScreenSaverWakeKey && !bKeyPressed)
|
||||
gScreenSaverWakeKey = KEY_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyPressed && gScreenSaverDisplayed) {
|
||||
ScreenSaverExit();
|
||||
BACKLIGHT_TurnOn();
|
||||
if (Key != KEY_PTT) {
|
||||
gScreenSaverWakeKey = Key;
|
||||
gBeepToPlay = BEEP_NONE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (Key == KEY_EXIT && !BACKLIGHT_IsOn() && gEeprom.BACKLIGHT_TIME > 0)
|
||||
{ // just turn the light on for now so the user can see what's what
|
||||
|
||||
+1
-1
@@ -31,6 +31,6 @@ uint32_t APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t direction);
|
||||
void APP_Update(void);
|
||||
void APP_TimeSlice10ms(void);
|
||||
void APP_TimeSlice500ms(void);
|
||||
bool APP_IsScreenSaverDisplayed(void);
|
||||
|
||||
#endif
|
||||
|
||||
+134
-1
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +222,13 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
*pMax = ARRAY_SIZE(gSubMenu_RX_TX) - 1;
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
//*pMin = 0;
|
||||
*pMax = SET_SAV_LEN - 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_AM_FIX
|
||||
case MENU_AM_FIX:
|
||||
@@ -1030,6 +1037,11 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSetting_set_tmr = gSubMenuSelection;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSetting_set_sav = gSubMenuSelection;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gTxVfo->TX_LOCK = gSubMenuSelection;
|
||||
gRequestSaveChannel = 1;
|
||||
@@ -1497,6 +1509,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSubMenuSelection = gSetting_set_tmr;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSubMenuSelection = gSetting_set_sav;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gSubMenuSelection = gTxVfo->TX_LOCK;
|
||||
break;
|
||||
|
||||
+113
-17
@@ -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;
|
||||
}
|
||||
@@ -506,8 +602,8 @@ void SCANNER_TimeSlice500ms(void)
|
||||
if (SCANNER_IsScanning() && gScannerSaveState == SCAN_SAVE_NO_PROMPT && gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
gScanProgressIndicator++;
|
||||
#ifndef ENABLE_NO_CODE_SCAN_TIMEOUT
|
||||
if (gScanProgressIndicator > 32) {
|
||||
if (gScanCssState == SCAN_CSS_STATE_SCANNING && !gScanSingleFrequency)
|
||||
if (gScanCssState == SCAN_CSS_STATE_SCANNING && gScanProgressIndicator > 32) {
|
||||
if (!gScanSingleFrequency)
|
||||
gScanCssState = SCAN_CSS_STATE_FOUND;
|
||||
else
|
||||
gScanCssState = SCAN_CSS_STATE_FAILED;
|
||||
@@ -525,4 +621,4 @@ void SCANNER_TimeSlice500ms(void)
|
||||
bool SCANNER_IsScanning(void)
|
||||
{
|
||||
return gCssBackgroundScan || (gScreenToDisplay == DISPLAY_SCANNER);
|
||||
}
|
||||
}
|
||||
+14
-12
@@ -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.
|
||||
@@ -1604,7 +1614,7 @@ static void ShowChannelName(uint32_t f)
|
||||
if (f != channelF) {
|
||||
channelF = f;
|
||||
unsigned int i;
|
||||
memset(channelName, 0, sizeof(channelName));
|
||||
channelName[0] = 0;
|
||||
for (i = 0; IS_MR_CHANNEL(i); i++)
|
||||
{
|
||||
if (RADIO_CheckValidChannel(i, false, 0))
|
||||
@@ -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.
|
||||
@@ -1715,16 +1726,7 @@ static bool SpectrumColumnAtOrAboveY(const uint8_t *topY, uint8_t x, uint8_t y)
|
||||
|
||||
static uint8_t GetScanStepTextWidth()
|
||||
{
|
||||
uint16_t whole = GetScanStep() / 100;
|
||||
uint8_t digits = 1;
|
||||
|
||||
while (whole >= 10)
|
||||
{
|
||||
whole /= 10;
|
||||
digits++;
|
||||
}
|
||||
|
||||
return (digits + 4) * 4; // "%u.%02uk", 4 px advance per char
|
||||
return (sprintf(NULL, "%u", GetScanStep() / 100) + 4) * 4; // "%u.%02uk", 4 px advance per char
|
||||
}
|
||||
|
||||
static void DrawRssiTriggerLevel(const uint8_t *topY)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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];
|
||||
|
||||
+11
-4
@@ -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
|
||||
@@ -107,7 +109,12 @@ void BACKLIGHT_InitHardware()
|
||||
|
||||
static void BACKLIGHT_Sound(void)
|
||||
{
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_ALL)
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND ||
|
||||
gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_ALL
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
|| gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO
|
||||
#endif
|
||||
)
|
||||
{
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_TRIPLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_TRIPLE_BEEP);
|
||||
|
||||
+21
-5
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -137,6 +137,9 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool gSetting_set_nfm = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
uint8_t gSetting_set_sav = SET_SAV_OFF;
|
||||
#endif
|
||||
bool gSetting_set_tmr = 0;
|
||||
bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
|
||||
+13
@@ -181,6 +181,16 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
enum SET_SAV_t {
|
||||
SET_SAV_OFF,
|
||||
SET_SAV_LOGO,
|
||||
SET_SAV_LOGO_PLUS,
|
||||
SET_SAV_MATRIX,
|
||||
SET_SAV_LEN
|
||||
};
|
||||
#endif
|
||||
|
||||
extern uint8_t gSetting_set_pwr;
|
||||
extern bool gSetting_set_ptt;
|
||||
extern uint8_t gSetting_set_tot;
|
||||
@@ -197,6 +207,9 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern bool gSetting_set_nfm;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern uint8_t gSetting_set_sav;
|
||||
#endif
|
||||
extern bool gSetting_set_tmr;
|
||||
extern bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
|
||||
+54
-60
@@ -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
@@ -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
@@ -19,5 +19,6 @@
|
||||
|
||||
void SCREENSHOT_Update(bool force);
|
||||
void SCREENSHOT_ParseInput(void);
|
||||
bool SCREENSHOT_HasPendingStateChange(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+18
-6
@@ -454,11 +454,20 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
// 1FF0..0x1FF7
|
||||
// TODO: address TBD
|
||||
PY25Q16_ReadBuffer(0x00A158, Data, 8);
|
||||
const uint8_t set_ptt_scn = Data[7] & 0x0F;
|
||||
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_erased && set_ptt_scn_sav < (SET_SAV_LEN << 2);
|
||||
#else
|
||||
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 < 4) ? (set_ptt_scn & 0x01) : 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 < 4) ? ((set_ptt_scn & 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;
|
||||
#endif
|
||||
|
||||
gSetting_set_tot = (((Data[6] & 0xF0) >> 4) < 4) ? ((Data[6] & 0xF0) >> 4) : 0;
|
||||
@@ -1131,13 +1140,16 @@ void SETTINGS_SaveSettings(void)
|
||||
|
||||
State[5] = ((tmp << 4) | (gSetting_set_ctr & 0x0F));
|
||||
State[6] = ((gSetting_set_tot << 4) | (gSetting_set_eot & 0x0F));
|
||||
uint8_t set_ptt_scn = gSetting_set_ptt & 0x01;
|
||||
uint8_t set_ptt_scn_sav = gSetting_set_ptt & 0x01;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (!gSetting_set_scn)
|
||||
set_ptt_scn |= 0x02;
|
||||
set_ptt_scn_sav |= 0x02;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
set_ptt_scn_sav |= (gSetting_set_sav & 0x03) << 2;
|
||||
#endif
|
||||
|
||||
State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn);
|
||||
State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn_sav);
|
||||
|
||||
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
|
||||
|
||||
|
||||
+45
-52
@@ -37,9 +37,8 @@ static int get_bit(uint8_t* array, int bit_index) {
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16] = { 0 };
|
||||
char *pPrintStr = { 0 };
|
||||
uint16_t percent;
|
||||
char String[16];
|
||||
char *pPrintStr;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -60,15 +59,13 @@ void UI_DisplayAircopy(void)
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 97, 0, 3);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
} else {
|
||||
const char *ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s", ascii, ascii + 3);
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
}
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
@@ -78,47 +75,44 @@ void UI_DisplayAircopy(void)
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
|
||||
percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else if (gAirCopyIsSendMode == 1) {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
|
||||
// Draw memory selection
|
||||
if(gAircopyState == AIRCOPY_READY)
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
memset(gFrameBuffer[5], 0, 128);
|
||||
memset(gFrameBuffer[6], 0, 128);
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
sprintf(String, "MEM %03u - %03u%", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(String, "Settings");
|
||||
sprintf(String, "MEM %03u - %03u", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
} else {
|
||||
strcpy(String, "Settings");
|
||||
}
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
}
|
||||
|
||||
if (doneBlocks > 0)
|
||||
@@ -131,19 +125,12 @@ void UI_DisplayAircopy(void)
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
|
||||
uint16_t total = currentMap->total_blocks;
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done > total) done = total;
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
/* Map column [0..BAR_WIDTH-1] to block [0..total-1] */
|
||||
uint16_t b = (uint16_t)((col * (uint32_t)total) / AIRCOPY_BAR_WIDTH);
|
||||
|
||||
bool processed = (b < done);
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
@@ -152,8 +139,14 @@ void UI_DisplayAircopy(void)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // error gap (intentional hole)
|
||||
else
|
||||
gFrameBuffer[4][col + 4] = 0xBD; // ok filled
|
||||
}
|
||||
|
||||
// DDA/Bresenham algorythm
|
||||
fraction_accumulator += currentMap->total_blocks;
|
||||
while (fraction_accumulator >= AIRCOPY_BAR_WIDTH) {
|
||||
fraction_accumulator -= AIRCOPY_BAR_WIDTH;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
+1
-2
@@ -31,7 +31,7 @@
|
||||
|
||||
void UI_DisplayFM(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -80,7 +80,6 @@ void UI_DisplayFM(void)
|
||||
|
||||
UI_PrintString(pPrintStr, 0, 127, 3, 10); // memory, vfo, scan
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAskToSave || (gEeprom.FM_IsMrMode && gInputBoxIndex > 0)) {
|
||||
UI_GenerateChannelString(String, gFM_ChannelPosition);
|
||||
} else if (gAskToDelete) {
|
||||
|
||||
@@ -40,6 +40,8 @@ void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
pString[2] = '-';
|
||||
for (i = 0; i < 2; i++)
|
||||
pString[i + 3] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
|
||||
pString[5] = 0;
|
||||
}
|
||||
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
|
||||
@@ -323,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
|
||||
|
||||
@@ -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
|
||||
|
||||
+141
-15
@@ -17,6 +17,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h> // abs()
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#include "app/dtmf.h"
|
||||
|
||||
@@ -139,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)
|
||||
{
|
||||
@@ -286,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;
|
||||
@@ -321,16 +386,9 @@ static void ScanProgress_DrawGaugeLine(uint8_t line, uint32_t current_index, uin
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_DecimalDigits(uint32_t value)
|
||||
static inline uint8_t ScanProgress_DecimalDigits(uint32_t value)
|
||||
{
|
||||
uint8_t digits = 1;
|
||||
|
||||
while (value >= 10u) {
|
||||
value /= 10u;
|
||||
digits++;
|
||||
}
|
||||
|
||||
return digits;
|
||||
return sprintf(NULL, "%u", value);
|
||||
}
|
||||
|
||||
static void ScanProgress_FormatIndex(char *out, size_t out_size, uint32_t current_index, uint32_t total, uint8_t width)
|
||||
@@ -853,6 +911,8 @@ void UI_DisplayAudioScope(void)
|
||||
void DisplayRSSIBar(const bool now)
|
||||
{
|
||||
#if defined(ENABLE_RSSI_BAR)
|
||||
if (APP_IsScreenSaverDisplayed())
|
||||
return;
|
||||
|
||||
const unsigned int txt_width = 7 * 8; // 8 text chars
|
||||
const unsigned int bar_x = 2 + txt_width + 4; // X coord of bar graph
|
||||
@@ -899,13 +959,17 @@ void DisplayRSSIBar(const bool now)
|
||||
p_line0[i] = (p_line0[i] & 0x80) | BITMAP_VFO_Empty[i];
|
||||
}
|
||||
|
||||
ST7565_BlitLine(RxLine);
|
||||
ST7565_DrawLine(0, RxLine + 1, p_line0, sizeof(BITMAP_VFO_Default));
|
||||
}
|
||||
|
||||
#else
|
||||
const unsigned int line = 3;
|
||||
#endif
|
||||
uint8_t *p_line = gFrameBuffer[line];
|
||||
char str[16];
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
uint8_t oldLine[LCD_WIDTH];
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
const char plus[] = {
|
||||
@@ -930,8 +994,15 @@ void DisplayRSSIBar(const bool now)
|
||||
)
|
||||
return; // display is in use
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (now) {
|
||||
memcpy(oldLine, p_line, LCD_WIDTH);
|
||||
memset(p_line, 0, LCD_WIDTH);
|
||||
}
|
||||
#else
|
||||
if (now)
|
||||
memset(p_line, 0, LCD_WIDTH);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
int16_t rssi_dBm =
|
||||
@@ -1021,8 +1092,13 @@ void DisplayRSSIBar(const bool now)
|
||||
UI_PrintStringSmallNormal(str, 2, 0, line);
|
||||
#endif
|
||||
DrawLevelBar(bar_x, line, s_level + overS9Bars, 13);
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (now && memcmp(oldLine, p_line, LCD_WIDTH) != 0)
|
||||
ST7565_BlitLine(line);
|
||||
#else
|
||||
if (now)
|
||||
ST7565_BlitLine(line);
|
||||
#endif
|
||||
#else
|
||||
int16_t rssi = BK4819_GetRSSI();
|
||||
uint8_t Level;
|
||||
@@ -1157,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];
|
||||
@@ -1255,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;
|
||||
}
|
||||
@@ -1268,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
|
||||
}
|
||||
@@ -1431,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]))
|
||||
@@ -1601,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;
|
||||
@@ -1608,6 +1723,7 @@ void UI_DisplayMain(void)
|
||||
gFrameBuffer[line][x] ^= 0x7F;
|
||||
}
|
||||
gFrameBuffer[line][127] ^= 0x3E;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
{
|
||||
@@ -2087,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
|
||||
|
||||
@@ -2225,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);
|
||||
|
||||
+113
-96
@@ -171,6 +171,9 @@ const t_menu_item MenuList[] =
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
{"SetScn", MENU_SET_SCN },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
{"SetSav", MENU_SET_SAV },
|
||||
#endif
|
||||
#endif
|
||||
// hidden menu items from here on
|
||||
// enabled if pressing both the PTT and upper side button at power-on
|
||||
@@ -197,7 +200,7 @@ const t_menu_item MenuList[] =
|
||||
|
||||
const uint8_t FIRST_HIDDEN_MENU_ITEM = MENU_F_LOCK;
|
||||
|
||||
const char gSubMenu_TXP[][6] =
|
||||
const char* const gSubMenu_TXP[] =
|
||||
{
|
||||
"USER",
|
||||
"LOW 1",
|
||||
@@ -209,29 +212,26 @@ const char gSubMenu_TXP[][6] =
|
||||
"HIGH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SFT_D[][4] =
|
||||
const char* const gSubMenu_SFT_D[] =
|
||||
{
|
||||
"OFF",
|
||||
"+",
|
||||
"-"
|
||||
};
|
||||
|
||||
const char gSubMenu_W_N[][7] =
|
||||
const char* const gSubMenu_W_N[] =
|
||||
{
|
||||
"WIDE",
|
||||
"NARROW"
|
||||
};
|
||||
|
||||
const char gSubMenu_OFF_ON[][4] =
|
||||
const char* const gSubMenu_OFF_ON[] =
|
||||
{
|
||||
"OFF",
|
||||
"ON"
|
||||
};
|
||||
|
||||
const char gSubMenu_NA[4] =
|
||||
{
|
||||
"N/A"
|
||||
};
|
||||
const char* gSubMenu_NA = "N/A";
|
||||
|
||||
const char* const gSubMenu_RXMode[] =
|
||||
{
|
||||
@@ -242,7 +242,7 @@ const char* const gSubMenu_RXMode[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
const char gSubMenu_VOICE[][4] =
|
||||
const char* const gSubMenu_VOICE[] =
|
||||
{
|
||||
"OFF",
|
||||
"CHI",
|
||||
@@ -259,7 +259,7 @@ const char* const gSubMenu_MDF[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
const char gSubMenu_AL_MOD[][5] =
|
||||
const char* const gSubMenu_AL_MOD[] =
|
||||
{
|
||||
"SITE",
|
||||
"TONE"
|
||||
@@ -267,7 +267,7 @@ const char* const gSubMenu_MDF[] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
const char gSubMenu_D_RSP[][11] =
|
||||
const char* const gSubMenu_D_RSP[] =
|
||||
{
|
||||
"DO\nNOTHING",
|
||||
"RING",
|
||||
@@ -285,7 +285,7 @@ const char* const gSubMenu_PTT_ID[] =
|
||||
"APOLLO\nQUINDAR"
|
||||
};
|
||||
|
||||
const char gSubMenu_PONMSG[][8] =
|
||||
const char* const gSubMenu_PONMSG[] =
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
"ALL",
|
||||
@@ -301,20 +301,30 @@ const char gSubMenu_PONMSG[][8] =
|
||||
"NONE"
|
||||
};
|
||||
|
||||
const char gSubMenu_ROGER[][6] =
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
const char* const gSubMenu_SET_SAV[] =
|
||||
{
|
||||
"OFF",
|
||||
"LOGO",
|
||||
"LOGO+",
|
||||
"MATRIX"
|
||||
};
|
||||
#endif
|
||||
|
||||
const char* const gSubMenu_ROGER[] =
|
||||
{
|
||||
"OFF",
|
||||
"ROGER",
|
||||
"MDC"
|
||||
};
|
||||
|
||||
const char gSubMenu_RESET[][4] =
|
||||
const char* const gSubMenu_RESET[] =
|
||||
{
|
||||
"VFO",
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char * const gSubMenu_F_LOCK[] =
|
||||
const char* const gSubMenu_F_LOCK[] =
|
||||
{
|
||||
"DEFAULT+\n137-174\n400-470",
|
||||
"FCC HAM\n144-148\n420-450",
|
||||
@@ -335,7 +345,7 @@ const char * const gSubMenu_F_LOCK[] =
|
||||
"UNLOCK\nALL",
|
||||
};
|
||||
|
||||
const char gSubMenu_RX_TX[][6] =
|
||||
const char* const gSubMenu_RX_TX[] =
|
||||
{
|
||||
"OFF",
|
||||
"TX",
|
||||
@@ -343,14 +353,14 @@ const char gSubMenu_RX_TX[][6] =
|
||||
"TX/RX"
|
||||
};
|
||||
|
||||
const char gSubMenu_BAT_TXT[][8] =
|
||||
const char* const gSubMenu_BAT_TXT[] =
|
||||
{
|
||||
"NONE",
|
||||
"VOLTAGE",
|
||||
"PERCENT"
|
||||
};
|
||||
|
||||
const char gSubMenu_BATTYP[][12] =
|
||||
const char* const gSubMenu_BATTYP[] =
|
||||
{
|
||||
"1600mAh K5",
|
||||
"2200mAh K5",
|
||||
@@ -359,14 +369,14 @@ const char gSubMenu_BATTYP[][12] =
|
||||
"2500mAh K1"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_NAV[][17] =
|
||||
const char* const gSubMenu_SET_NAV[] =
|
||||
{
|
||||
"LEFT\nRIGHT\nUV-K1",
|
||||
"UP\nDOWN\nUV-K5(8)",
|
||||
};
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SCRAMBLER[][7] =
|
||||
const char* const gSubMenu_SCRAMBLER[] =
|
||||
{
|
||||
"OFF",
|
||||
"2600Hz",
|
||||
@@ -383,7 +393,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SET_PWR[][6] =
|
||||
const char* const gSubMenu_SET_PWR[] =
|
||||
{
|
||||
"< 20m",
|
||||
"125m",
|
||||
@@ -394,13 +404,13 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"5"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_PTT[][8] =
|
||||
const char* const gSubMenu_SET_PTT[] =
|
||||
{
|
||||
"CLASSIC",
|
||||
"ONEPUSH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_TOT[][7] = // Use by SET_EOT too
|
||||
const char* const gSubMenu_SET_TOT[] =
|
||||
{
|
||||
"OFF",
|
||||
"SOUND",
|
||||
@@ -408,20 +418,20 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_LCK[][9] =
|
||||
const char* const gSubMenu_SET_LCK[] =
|
||||
{
|
||||
"KEYS",
|
||||
"KEYS+PTT"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_MET[][8] =
|
||||
const char* const gSubMenu_SET_MET[] =
|
||||
{
|
||||
"TINY",
|
||||
"CLASSIC"
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const char gSubMenu_SET_SCN[][7] =
|
||||
const char* const gSubMenu_SET_SCN[] =
|
||||
{
|
||||
"NORMAL",
|
||||
"FAST"
|
||||
@@ -429,7 +439,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
const char gSubMenu_SET_AUD_FM[][6] =
|
||||
const char* const gSubMenu_SET_AUD_FM[] =
|
||||
{
|
||||
"FLAT",
|
||||
"CLEAN",
|
||||
@@ -438,7 +448,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"MAX"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_AUD_AM[][6] =
|
||||
const char* const gSubMenu_SET_AUD_AM[] =
|
||||
{
|
||||
"SHARP",
|
||||
"STOCK",
|
||||
@@ -447,7 +457,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
const char gSubMenu_SET_NFM[][9] =
|
||||
const char* const gSubMenu_SET_NFM[] =
|
||||
{
|
||||
"NARROW",
|
||||
"NARROWER"
|
||||
@@ -455,7 +465,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
const char gSubMenu_SET_KEY[][9] =
|
||||
const char* const gSubMenu_SET_KEY[] =
|
||||
{
|
||||
"KEY_MENU",
|
||||
"KEY_UP",
|
||||
@@ -496,7 +506,7 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{"VFO\nMEM", ACTION_OPT_VFO_MR},
|
||||
{"MODE", ACTION_OPT_SWITCH_DEMODUL},
|
||||
#ifdef ENABLE_BLMIN_TMP_OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporay OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporary OFF
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
{"RX MODE", ACTION_OPT_RXMODE},
|
||||
@@ -658,58 +668,56 @@ void UI_DisplayMenu(void)
|
||||
#else
|
||||
{ // new menu layout .. experimental & unfinished
|
||||
const int menu_index = gMenuCursor; // current selected menu item
|
||||
i = 1;
|
||||
const int menu_count = (int)gMenuListCount;
|
||||
|
||||
if (!gIsInSubMenu) {
|
||||
while (i < 2)
|
||||
{ // leading menu items - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k < 0)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount + k].name, 0, 0, i); // wrap-a-round
|
||||
else if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
|
||||
i++;
|
||||
}
|
||||
if (menu_index >= 0 && menu_index < menu_count)
|
||||
{
|
||||
if (!gIsInSubMenu)
|
||||
{
|
||||
// leading menu items - small text
|
||||
int prev_index = menu_index - 1;
|
||||
if (prev_index < 0) {
|
||||
prev_index = menu_count - 1;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[prev_index].name, 0, 0, 1);
|
||||
|
||||
// current menu item - keep big n fat
|
||||
if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
// current menu item - keep big n fat
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 2, 8);
|
||||
i++;
|
||||
|
||||
while (i < 4)
|
||||
{ // trailing menu item - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, 1 + i);
|
||||
else if (k >= (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount - k].name, 0, 0, 1 + i); // wrap-a-round
|
||||
i++;
|
||||
// trailing menu item - small text
|
||||
int next_index = menu_index + 1;
|
||||
if (next_index >= menu_count) {
|
||||
next_index = 0;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[next_index].name, 0, 0, 4);
|
||||
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
{ // current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// **************
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
memset(top_right_badge, 0, sizeof(top_right_badge));
|
||||
String[0] = '\0';
|
||||
top_right_badge[0] = '\0';
|
||||
|
||||
bool already_printed = false;
|
||||
|
||||
@@ -923,6 +931,12 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
strcpy(String, gSubMenu_SET_SAV[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_MEM_CH:
|
||||
case MENU_1_CALL:
|
||||
case MENU_DEL_CH:
|
||||
@@ -1103,11 +1117,19 @@ void UI_DisplayMenu(void)
|
||||
break;
|
||||
#endif
|
||||
case MENU_UPCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
if (gEeprom.DTMF_UP_CODE[8] != '\0' && gEeprom.DTMF_UP_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_UP_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_DWCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
if (gEeprom.DTMF_DOWN_CODE[8] != '\0' && gEeprom.DTMF_DOWN_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_DOWN_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
@@ -1441,7 +1463,7 @@ void UI_DisplayMenu(void)
|
||||
unsigned int len = strlen(String);
|
||||
bool small = false;
|
||||
|
||||
if (len > 0)
|
||||
if (String[0] != '\0')
|
||||
{
|
||||
// count number of lines
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -1508,30 +1530,25 @@ void UI_DisplayMenu(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m == MENU_R_CTCS ||
|
||||
m == MENU_T_CTCS) {
|
||||
const uint8_t approved_index =
|
||||
(gSubMenuSelection > 0) ? DCS_GetCtcssApprovedIndex(gSubMenuSelection - 1) : 0xFF;
|
||||
const bool is_ctcs = (m == MENU_R_CTCS || m == MENU_T_CTCS);
|
||||
const bool is_dcs = (m == MENU_R_DCS || m == MENU_T_DCS);
|
||||
|
||||
if (gSubMenuSelection == 0)
|
||||
sprintf(top_right_badge, "00/00");
|
||||
else if (approved_index != 0xFF)
|
||||
sprintf(top_right_badge, "%02u/%02u", (unsigned)gSubMenuSelection, (unsigned)approved_index + 1);
|
||||
else
|
||||
sprintf(top_right_badge, "%02u/--", (unsigned)gSubMenuSelection);
|
||||
}
|
||||
|
||||
if (m == MENU_R_DCS ||
|
||||
m == MENU_T_DCS) {
|
||||
const uint8_t approved_index =
|
||||
(gSubMenuSelection > 0) ? DCS_GetDcsApprovedIndex(gSubMenuSelection - 1) : 0xFF;
|
||||
if (is_ctcs || is_dcs) {
|
||||
if (gSubMenuSelection == 0) {
|
||||
strcpy(top_right_badge, is_ctcs ? "00/00" : "000/00");
|
||||
} else {
|
||||
const uint8_t approved_index = is_ctcs ?
|
||||
DCS_GetCtcssApprovedIndex(gSubMenuSelection - 1) :
|
||||
DCS_GetDcsApprovedIndex(gSubMenuSelection - 1);
|
||||
|
||||
const uint8_t width = is_ctcs ? 2 : 3;
|
||||
|
||||
if (gSubMenuSelection == 0)
|
||||
sprintf(top_right_badge, "000/00");
|
||||
else if (approved_index != 0xFF)
|
||||
sprintf(top_right_badge, "%03u/%02u", (unsigned)gSubMenuSelection, (unsigned)approved_index + 1);
|
||||
else
|
||||
sprintf(top_right_badge, "%03u/--", (unsigned)gSubMenuSelection);
|
||||
if (approved_index != 0xFF) {
|
||||
sprintf(top_right_badge, "%0*u/%02u", width, (unsigned)gSubMenuSelection, (unsigned)approved_index + 1);
|
||||
} else {
|
||||
sprintf(top_right_badge, "%0*u/--", width, (unsigned)gSubMenuSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|
||||
+47
-35
@@ -23,7 +23,7 @@
|
||||
#include "audio.h" // VOICE_ID_t
|
||||
#include "settings.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct __attribute__((packed)) {
|
||||
const char name[7]; // menu display area only has room for 6 characters
|
||||
uint8_t menu_id;
|
||||
} t_menu_item;
|
||||
@@ -163,76 +163,88 @@ enum
|
||||
MENU_F2SHRT,
|
||||
MENU_F2LONG,
|
||||
MENU_MLONG,
|
||||
MENU_BATTYP
|
||||
MENU_BATTYP,
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
MENU_SET_SAV
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const uint8_t FIRST_HIDDEN_MENU_ITEM;
|
||||
extern const t_menu_item MenuList[];
|
||||
|
||||
extern const char gSubMenu_TXP[8][6];
|
||||
extern const char gSubMenu_SFT_D[3][4];
|
||||
extern const char gSubMenu_W_N[2][7];
|
||||
extern const char gSubMenu_OFF_ON[2][4];
|
||||
extern const char gSubMenu_NA[4];
|
||||
extern const char gSubMenu_TOT[11][7];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
extern const char* const gSubMenu_TXP[8];
|
||||
extern const char* const gSubMenu_SFT_D[3];
|
||||
extern const char* const gSubMenu_W_N[2];
|
||||
extern const char* const gSubMenu_OFF_ON[2];
|
||||
extern const char* gSubMenu_NA;
|
||||
extern const char* const gSubMenu_TOT[11];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
extern const char gSubMenu_VOICE[3][4];
|
||||
extern const char* const gSubMenu_VOICE[3];
|
||||
#endif
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
#ifdef ENABLE_ALARM
|
||||
extern const char gSubMenu_AL_MOD[2][5];
|
||||
extern const char* const gSubMenu_AL_MOD[2];
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
extern const char gSubMenu_D_RSP[4][11];
|
||||
extern const char* const gSubMenu_D_RSP[4];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SET_PWR[7][6];
|
||||
extern const char gSubMenu_SET_PTT[2][8];
|
||||
extern const char gSubMenu_SET_TOT[4][7];
|
||||
extern const char gSubMenu_SET_LCK[2][9];
|
||||
extern const char gSubMenu_SET_MET[2][8];
|
||||
extern const char* const gSubMenu_SET_PWR[7];
|
||||
extern const char* const gSubMenu_SET_PTT[2];
|
||||
extern const char* const gSubMenu_SET_TOT[4];
|
||||
extern const char* const gSubMenu_SET_LCK[2];
|
||||
extern const char* const gSubMenu_SET_MET[2];
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
extern const char gSubMenu_SET_SCN[2][7];
|
||||
extern const char* const gSubMenu_SET_SCN[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern const char gSubMenu_SET_NFM[2][9];
|
||||
extern const char* const gSubMenu_SET_NFM[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
extern const char gSubMenu_SET_KEY[][9];
|
||||
extern const char* const gSubMenu_SET_KEY[5];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
extern const char gSubMenu_SET_AUD_FM[5][6];
|
||||
extern const char gSubMenu_SET_AUD_AM[3][6];
|
||||
extern const char* const gSubMenu_SET_AUD_FM[5];
|
||||
extern const char* const gSubMenu_SET_AUD_AM[3];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern const char* const gSubMenu_SET_SAV[];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern const char* const gSubMenu_PTT_ID[5];
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
extern const char gSubMenu_PONMSG[6][8];
|
||||
extern const char* const gSubMenu_PONMSG[6];
|
||||
#else
|
||||
extern const char gSubMenu_PONMSG[5][8];
|
||||
extern const char* const gSubMenu_PONMSG[5];
|
||||
#endif
|
||||
#else
|
||||
extern const char gSubMenu_PONMSG[4][8];
|
||||
extern const char* const gSubMenu_PONMSG[4];
|
||||
#endif
|
||||
extern const char gSubMenu_ROGER[3][6];
|
||||
extern const char gSubMenu_RESET[2][4];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char gSubMenu_RX_TX[4][6];
|
||||
extern const char gSubMenu_BAT_TXT[3][8];
|
||||
extern const char gSubMenu_BATTYP[5][12];
|
||||
|
||||
extern const char* const gSubMenu_ROGER[3];
|
||||
extern const char* const gSubMenu_RESET[2];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char* const gSubMenu_RX_TX[4];
|
||||
extern const char* const gSubMenu_BAT_TXT[3];
|
||||
extern const char* const gSubMenu_BATTYP[5];
|
||||
extern const char* const gSubMenu_SET_NAV[2];
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SCRAMBLER[11][7];
|
||||
extern const char* const gSubMenu_SCRAMBLER[11];
|
||||
#endif
|
||||
|
||||
typedef struct {char* name; uint8_t id;} t_sidefunction;
|
||||
typedef struct /* __attribute__((packed)) */ {
|
||||
const char* name;
|
||||
uint8_t id;
|
||||
} t_sidefunction;
|
||||
|
||||
extern const uint8_t gSubMenu_SIDEFUNCTIONS_size;
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
|
||||
extern bool gIsInSubMenu;
|
||||
|
||||
|
||||
+34
-36
@@ -26,58 +26,56 @@
|
||||
|
||||
void UI_DisplayScanner(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
bool bCentered;
|
||||
uint8_t Start;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "FREQ:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
// 1st line
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "Save?";
|
||||
} else if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "Save:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if ((gScanCssState < SCAN_CSS_STATE_FOUND) && ((gScanProgressIndicator & 1u) != 0)) {
|
||||
pPrintStr = "";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_OFF) {
|
||||
pPrintStr = "Search Freq";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_SCANNING) {
|
||||
pPrintStr = "Search Tone";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Scan Complete";
|
||||
} else {
|
||||
pPrintStr = "FREQ:**.*****";
|
||||
pPrintStr = "Scan Failed";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 1, 8);
|
||||
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND || !gScanUseCssResult) {
|
||||
pPrintStr = "CTC:******";
|
||||
// 2nd line
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "Freq:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "Freq:---.-----";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
|
||||
// 3rd line
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Tone:---";
|
||||
} else if (!gScanUseCssResult) {
|
||||
pPrintStr = "Tone:None";
|
||||
} else if (gScanCssResultType == CODE_TYPE_CONTINUOUS_TONE) {
|
||||
sprintf(String, "CTC:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
sprintf(String, "CTCSS:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
sprintf(String, "DCS:D%03oN", DCS_Options[gScanCssResultCode]);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "SAVE?";
|
||||
Start = 0;
|
||||
bCentered = 1;
|
||||
} else {
|
||||
Start = 2;
|
||||
bCentered = 0;
|
||||
|
||||
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "SAVE:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
strcpy(String, "SCAN");
|
||||
memset(String + 4, '.', (gScanProgressIndicator & 7) + 1);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "SCAN CMP.";
|
||||
} else {
|
||||
pPrintStr = "SCAN FAIL.";
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, Start, bCentered ? 127 : 0, 5, 8);
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 5, 8);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
+97
-77
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
@@ -43,26 +44,35 @@ 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);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void UI_DisplayStatus()
|
||||
{
|
||||
char str[8] = "";
|
||||
|
||||
gUpdateStatus = false;
|
||||
|
||||
if (APP_IsScreenSaverDisplayed())
|
||||
return;
|
||||
|
||||
UI_StatusClear();
|
||||
|
||||
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
|
||||
@@ -82,7 +92,7 @@ void UI_DisplayStatus()
|
||||
x += 8;
|
||||
#endif
|
||||
|
||||
unsigned int x1 = x;
|
||||
x1 = x;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gSetting_KILLED) {
|
||||
@@ -94,13 +104,12 @@ 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)
|
||||
{
|
||||
sprintf(str, gEeprom.SCAN_LIST_ENABLED ? "%s+" : "%s", "ALL");
|
||||
end = gEeprom.SCAN_LIST_ENABLED ? 18 : 14;
|
||||
strcpy(str, "ALL");
|
||||
end = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -108,15 +117,22 @@ void UI_DisplayStatus()
|
||||
|
||||
// Check if name is valid
|
||||
if (!IsEmptyName(name, sizeof(gListName[0]))) {
|
||||
sprintf(str, "%.3s%s", name, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
|
||||
end = gEeprom.SCAN_LIST_ENABLED ? 18 : 14;
|
||||
}
|
||||
else {
|
||||
sprintf(str, "%02d%s", gEeprom.SCAN_LIST_DEFAULT, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
|
||||
end = gEeprom.SCAN_LIST_ENABLED ? 14 : 10;
|
||||
sprintf(str, "%.3s", name);
|
||||
end = 14;
|
||||
} else {
|
||||
sprintf(str, "%02d", gEeprom.SCAN_LIST_DEFAULT);
|
||||
end = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (gEeprom.SCAN_LIST_ENABLED) {
|
||||
strcat(str, "+");
|
||||
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;
|
||||
@@ -125,6 +141,7 @@ void UI_DisplayStatus()
|
||||
gStatusLine[x] ^= 0x7F;
|
||||
}
|
||||
gStatusLine[end] ^= 0x3E;
|
||||
#endif
|
||||
}
|
||||
else { // frequency mode
|
||||
memcpy(line + x + 1, gFontS, sizeof(gFontS));
|
||||
@@ -154,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) {
|
||||
@@ -248,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) {
|
||||
@@ -268,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();
|
||||
|
||||
+33
-29
@@ -44,6 +44,21 @@
|
||||
#define LOGO_FLASH_ADDR 0x011000
|
||||
#define LOGO_HEADER_SIZE 8
|
||||
#define LOGO_BITMAP_ADDR (LOGO_FLASH_ADDR + LOGO_HEADER_SIZE)
|
||||
|
||||
static void UI_LoadLogo(void)
|
||||
{
|
||||
// Skip 8-byte header, then read 128x64 bitmap (1024 B):
|
||||
// page 0 -> gStatusLine, pages 1..7 -> gFrameBuffer.
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR, gStatusLine, sizeof(gStatusLine));
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR + sizeof(gStatusLine), gFrameBuffer, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_DisplayLogo(void)
|
||||
{
|
||||
UI_LoadLogo();
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
@@ -217,11 +232,6 @@ void UI_DisplayReleaseKeys(void)
|
||||
|
||||
void UI_DisplayWelcome(void)
|
||||
{
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
UI_StatusClear();
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
@@ -229,36 +239,30 @@ void UI_DisplayWelcome(void)
|
||||
#endif
|
||||
UI_DisplayClear();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) {
|
||||
// Skip 8-byte header, then read 128x64 bitmap (1024 B):
|
||||
// page 0 -> gStatusLine, pages 1..7 -> gFrameBuffer.
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR, gStatusLine, sizeof(gStatusLine));
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR + sizeof(gStatusLine), gFrameBuffer, sizeof(gFrameBuffer));
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND) {
|
||||
ST7565_FillScreen(0x00);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_FULL_SCREEN) {
|
||||
ST7565_FillScreen(0xFF);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
else if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) {
|
||||
UI_LoadLogo();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
memset(WelcomeString0, 0, sizeof(WelcomeString0));
|
||||
memset(WelcomeString1, 0, sizeof(WelcomeString1));
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
// 0x0EB0
|
||||
PY25Q16_ReadBuffer(0x00A0C8, WelcomeString0, 16);
|
||||
@@ -346,12 +350,12 @@ void UI_DisplayWelcome(void)
|
||||
#else
|
||||
UI_PrintStringSmallNormal(Version, 0, 127, 6);
|
||||
#endif
|
||||
|
||||
//ST7565_BlitStatusLine(); // blank status line : I think it's useless
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
+4
-1
@@ -23,6 +23,10 @@
|
||||
void UI_DisplayReleaseKeys(void);
|
||||
void UI_DisplayWelcome(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
void UI_DisplayLogo(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Draw the embedded GitHub QR code (V4, 33x33) at (origin_x, origin_y).
|
||||
// `wiki` selects the wiki URL bitmap, otherwise the repo URL bitmap.
|
||||
@@ -35,4 +39,3 @@ void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+8
-2
@@ -34,7 +34,7 @@
|
||||
"ENABLE_BOOT_BEEPS": false,
|
||||
"ENABLE_SHOW_CHARGE_LEVEL": false,
|
||||
"ENABLE_REVERSE_BAT_SYMBOL": false,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": true,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": false,
|
||||
"ENABLE_SQUELCH_MORE_SENSITIVE": true,
|
||||
"ENABLE_FASTER_CHANNEL_SCAN": true,
|
||||
"ENABLE_RSSI_BAR": true,
|
||||
@@ -70,13 +70,16 @@
|
||||
"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,
|
||||
"ENABLE_AGC_SHOW_DATA": false,
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -198,10 +201,13 @@
|
||||
"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,
|
||||
"ENABLE_FEAT_F4HWN_LOGO": true,
|
||||
"ENABLE_FEAT_F4HWN_LOGO_SAV": true,
|
||||
"ENABLE_SWD": true,
|
||||
"EDITION_STRING": "Fusion",
|
||||
"TARGET": "f4hwn.fusion"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Reference in new issue
Block a user