From cc2281d3cb181f01431087e47b622ffd6f51bcd9 Mon Sep 17 00:00:00 2001 From: Armel FAUVEAU Date: Sat, 19 Sep 2026 05:05:38 +0200 Subject: [PATCH] Add MIX scan list mode --- App/app/app.c | 1 + App/app/chFrScanner.c | 6 +- App/app/main.c | 44 ++++++------ App/app/menu.c | 140 ++++++++++++++++++++++++++++++++++++- App/app/menu.h | 4 +- App/driver/eeprom_compat.c | 3 +- App/misc.h | 4 ++ App/radio.c | 77 ++++++++++++++++---- App/radio.h | 2 + App/settings.c | 28 +++++++- App/settings.h | 1 + App/ui/main.c | 18 ++--- App/ui/menu.c | 41 ++++++++++- App/ui/status.c | 7 +- 14 files changed, 317 insertions(+), 59 deletions(-) diff --git a/App/app/app.c b/App/app/app.c index 0fa96526..601257f7 100644 --- a/App/app/app.c +++ b/App/app/app.c @@ -2097,6 +2097,7 @@ void APP_TimeSlice500ms(void) if (exit_menu) { gMenuCountdown = 0; + gScanMixEditorActive = false; const int m = UI_MENU_GetCurrentMenuId(); diff --git a/App/app/chFrScanner.c b/App/app/chFrScanner.c index d663049d..73c5a5f2 100644 --- a/App/app/chFrScanner.c +++ b/App/app/chFrScanner.c @@ -1045,9 +1045,9 @@ static void NextFreqChannel(void) static void NextMemChannel(void) { static uint16_t prev_mr_chan = 0; - const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1) ? gEeprom.SCAN_LIST_ENABLED : true; - const int16_t chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[0] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[0] : -1; - const int16_t chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[1] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[1] : -1; + const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= SCAN_LIST_MODE_MIX) ? gEeprom.SCAN_LIST_ENABLED : true; + const int16_t chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= SCAN_LIST_MODE_MIX && gEeprom.SCANLIST_PRIORITY_CH[0] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[0] : -1; + const int16_t chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= SCAN_LIST_MODE_MIX && gEeprom.SCANLIST_PRIORITY_CH[1] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[1] : -1; const uint16_t prev_chan = gNextMrChannel; uint16_t chan = 0; diff --git a/App/app/main.c b/App/app/main.c index 29fc3c5d..e7059513 100644 --- a/App/app/main.c +++ b/App/app/main.c @@ -25,6 +25,7 @@ #endif #include "app/generic.h" #include "app/main.h" +#include "app/menu.h" #include "app/scanner.h" #ifdef ENABLE_SPECTRUM @@ -104,7 +105,7 @@ static void toggle_chan_scanlist(void) scanlist++; - if (scanlist > MR_CHANNELS_LIST + 1) + if (scanlist > SCAN_LIST_MODE_ALL) scanlist = 0; gTxVfo->SCANLIST_PARTICIPATION = scanlist; @@ -491,26 +492,6 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) if (!gWasFKeyPressed) { // F-key wasn't pressed if (gScanStateDir != SCAN_OFF){ - /* - switch(Key) { - case KEY_0: - gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1; - #ifdef ENABLE_FEAT_F4HWN_RESUME_STATE - SETTINGS_WriteCurrentState(); - #endif - break; - case KEY_1...KEY_9: - gEeprom.SCAN_LIST_DEFAULT = Key; - #ifdef ENABLE_FEAT_F4HWN_RESUME_STATE - SETTINGS_WriteCurrentState(); - #endif - break; - default: - break; - } - return; - */ - INPUTBOX_Append(Key); /* Wait until exactly two digits are entered */ @@ -525,7 +506,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) /* 00 = ALL scan lists */ if (value == 0) { - gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1; + gEeprom.SCAN_LIST_DEFAULT = SCAN_LIST_MODE_ALL; UI_MAIN_NotifyScanListChanged(); #ifdef ENABLE_FEAT_F4HWN_RESUME_STATE SETTINGS_WriteCurrentState(); @@ -533,6 +514,24 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) return; } + /* 25 = saved MIX selection */ + if (value == SCAN_LIST_MIX_SHORTCUT) + { + gEeprom.SCAN_LIST_DEFAULT = SCAN_LIST_MODE_MIX; + + if (!RADIO_CheckValidList(SCAN_LIST_MODE_MIX)) + { + gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; + RADIO_NextValidList(1); + } + UI_MAIN_NotifyScanListChanged(); + + #ifdef ENABLE_FEAT_F4HWN_RESUME_STATE + SETTINGS_WriteCurrentState(); + #endif + return; + } + /* 01 .. MR_CHANNELS_LIST */ if (value <= MR_CHANNELS_LIST) { @@ -854,6 +853,7 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld) #endif gFlagRefreshSetting = true; + gScanMixEditorActive = false; gRequestDisplayScreen = DISPLAY_MENU; #ifdef ENABLE_FEAT_F4HWN_MENU_CAT gMenuLevel = MENU_LEVEL_CAT; diff --git a/App/app/menu.c b/App/app/menu.c index 6e1e461a..3c6f4c36 100644 --- a/App/app/menu.c +++ b/App/app/menu.c @@ -37,6 +37,7 @@ #include "frequencies.h" #include "helper/battery.h" #include "misc.h" +#include "radio.h" #include "settings.h" #include "../driver/st7565.h" #if defined(ENABLE_OVERLAY) @@ -48,6 +49,41 @@ uint8_t gUnlockAllTxConfCnt; +bool gScanMixEditorActive; +uint8_t gScanMixEditorCursor; +uint32_t gScanMixEditorMask; + +static void MENU_OpenScanMixEditor(void) +{ + gScanMixEditorMask = gEeprom.SCAN_LIST_MIX_MASK & SCAN_LIST_MIX_MASK_ALL; + if (gScanMixEditorMask == 0) + gScanMixEditorMask = SCAN_LIST_MIX_MASK_ALL; + + gScanMixEditorCursor = 0; + while (gScanMixEditorCursor < MR_CHANNELS_LIST && + (gScanMixEditorMask & (1u << gScanMixEditorCursor)) == 0) + gScanMixEditorCursor++; + if (gScanMixEditorCursor >= MR_CHANNELS_LIST) + gScanMixEditorCursor = 0; + + gInputBoxIndex = 0; + gScanMixEditorActive = true; + gRequestDisplayScreen = DISPLAY_MENU; + gUpdateDisplay = true; +} + +static void MENU_SaveScanMixEditor(void) +{ + gEeprom.SCAN_LIST_MIX_MASK = gScanMixEditorMask & SCAN_LIST_MIX_MASK_ALL; + gEeprom.SCAN_LIST_DEFAULT = SCAN_LIST_MODE_MIX; + gSubMenuSelection = SCAN_LIST_MODE_MIX; + gScanMixEditorActive = false; + gIsInSubMenu = false; + gInputBoxIndex = 0; + gFlagRefreshSetting = true; + gRequestSaveSettings = true; + gRequestDisplayScreen = DISPLAY_MENU; +} #ifdef ENABLE_F_CAL_MENU void writeXtalFreqCal(const int32_t value, const bool update_eeprom) @@ -319,12 +355,12 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax) case MENU_LIST_CH: //*pMin = 0; - *pMax = MR_CHANNELS_LIST + 1; + *pMax = SCAN_LIST_MODE_ALL; break; case MENU_S_LIST: *pMin = 1; - *pMax = MR_CHANNELS_LIST + 1; + *pMax = SCAN_LIST_MODE_MIX; break; #ifdef ENABLE_DTMF_CALLING @@ -1040,6 +1076,12 @@ static void MENU_ClampSelection(int8_t Direction) int32_t Min; int32_t Max; + if (UI_MENU_GetCurrentMenuId() == MENU_S_LIST) + { + gSubMenuSelection = RADIO_GetAdjacentScanList(gSubMenuSelection, Direction); + return; + } + if (!MENU_GetLimits(UI_MENU_GetCurrentMenuId(), &Min, &Max)) { int32_t Selection = gSubMenuSelection; @@ -1528,6 +1570,26 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; + if (gScanMixEditorActive) + { + if (bKeyHeld) + return; + + INPUTBOX_Append(Key); + if (gInputBoxIndex < 2) + return; + + gInputBoxIndex = 0; + Value = (gInputBox[0] * 10) + gInputBox[1]; + if (Value >= 1 && Value <= MR_CHANNELS_LIST) { + gScanMixEditorCursor = (uint8_t)(Value - 1); + gRequestDisplayScreen = DISPLAY_MENU; + } else { + gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; + } + return; + } + if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0) { // currently editing the channel name if (edit_index >= 10) @@ -1705,6 +1767,25 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) return; } + if (m == MENU_S_LIST) + { + if (gInputBoxIndex < 2) + return; + + gInputBoxIndex = 0; + Value = (gInputBox[0] * 10) + gInputBox[1]; + + if (Value == 0) + gSubMenuSelection = SCAN_LIST_MODE_ALL; + else if (Value == SCAN_LIST_MIX_SHORTCUT) + gSubMenuSelection = SCAN_LIST_MODE_MIX; + else if (Value <= MR_CHANNELS_LIST) + gSubMenuSelection = Value; + else + gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; + return; + } + if (MENU_GetLimits(UI_MENU_GetCurrentMenuId(), &Min, &Max)) { gInputBoxIndex = 0; @@ -1747,6 +1828,16 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld) { + if (gScanMixEditorActive) + { + if (bKeyHeld || !bKeyPressed) + return; + + gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; + MENU_SaveScanMixEditor(); + return; + } + if (MENU_IsEditingName()) { if (!bKeyPressed) @@ -1863,6 +1954,24 @@ Skip: static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld) { + if (gScanMixEditorActive) + { + if (!bKeyPressed || bKeyHeld) + return; + + const uint32_t bit = 1u << gScanMixEditorCursor; + if ((gScanMixEditorMask & bit) != 0 && (gScanMixEditorMask & ~bit) == 0) { + gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; + return; + } + + gScanMixEditorMask ^= bit; + gInputBoxIndex = 0; + gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; + gRequestDisplayScreen = DISPLAY_MENU; + return; + } + if (!bKeyPressed || (bKeyHeld && (!MENU_IsEditingName() || gAskForConfirmation))) return; @@ -1966,6 +2075,13 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld) } } + if (UI_MENU_GetCurrentMenuId() == MENU_S_LIST && + gSubMenuSelection == SCAN_LIST_MODE_MIX) + { + MENU_OpenScanMixEditor(); + return; + } + // exiting the sub menu if (gIsInSubMenu) @@ -2122,6 +2238,26 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction) if (!bKeyPressed) return; + if (gScanMixEditorActive) + { + if (!bKeyHeld) { + gInputBoxIndex = 0; + gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; + } +#ifdef ENABLE_FEAT_F4HWN + if (!gEeprom.SET_NAV) + Direction = -Direction; +#else + Direction = -Direction; +#endif + gScanMixEditorCursor = NUMBER_AddWithWraparound(gScanMixEditorCursor, + Direction, + 0, + MR_CHANNELS_LIST - 1); + gRequestDisplayScreen = DISPLAY_MENU; + return; + } + if (!bKeyHeld) { gInputBoxIndex = 0; gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; diff --git a/App/app/menu.h b/App/app/menu.h index fbca3bfb..22c853eb 100644 --- a/App/app/menu.h +++ b/App/app/menu.h @@ -24,6 +24,9 @@ #endif extern uint8_t gUnlockAllTxConfCnt; +extern bool gScanMixEditorActive; +extern uint8_t gScanMixEditorCursor; +extern uint32_t gScanMixEditorMask; int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax); void MENU_AcceptSetting(void); @@ -35,4 +38,3 @@ void MENU_StopCssScan(void); void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld); #endif - diff --git a/App/driver/eeprom_compat.c b/App/driver/eeprom_compat.c index a7b751a1..108575f2 100644 --- a/App/driver/eeprom_compat.c +++ b/App/driver/eeprom_compat.c @@ -58,7 +58,7 @@ static const AddrMapping_t ADDR_MAPPINGS[] = { // (written directly by app/foxhunt.c; concatenated // here so aircopy clones them with the VFOs) - _MK_MAPPING(0x00A000, 0x00A000, 0x00A170), // Settings * 16 Bytes (ex 0x004000) 0x00A000 -> 0x00A010 + _MK_MAPPING(0x00A000, 0x00A000, 0x00A178), // Settings * 16 Bytes (ex 0x004000) 0x00A000 -> 0x00A010 // Settings * 16 Bytes (ex 0x005000) 0x00A010 -> 0x00A020 // Settings FM * 8 Bytes (0x006000) 0x00A020 -> 0x00A028 // MR FM * 128 Bytes (0x003000) 0x00A028 -> 0x00A0A8 @@ -70,6 +70,7 @@ static const AddrMapping_t ADDR_MAPPINGS[] = { // Settings * 8 Bytes (0x00B000) 0x00A150 -> 0x00A158 // Settings F4HWN * 8 Bytes (0x00C000) 0x00A158 -> 0x00A160 // Settings Version * 16 Bytes 0x00A160 -> 0x00A170 + // Settings Mixed Scan Lists * 8 Bytes 0x00A170 -> 0x00A178 _MK_MAPPING(0x010000, 0x00B000, 0x00B200), // Calibration 512 Bytes!!! diff --git a/App/misc.h b/App/misc.h index dd5d6e3a..c90b92b6 100644 --- a/App/misc.h +++ b/App/misc.h @@ -39,6 +39,10 @@ #define FM_CHANNELS_MAX 48 #define MR_CHANNELS_MAX 1024 #define MR_CHANNELS_LIST 24 +#define SCAN_LIST_MODE_ALL (MR_CHANNELS_LIST + 1u) +#define SCAN_LIST_MODE_MIX (MR_CHANNELS_LIST + 2u) +#define SCAN_LIST_MIX_SHORTCUT 25u +#define SCAN_LIST_MIX_MASK_ALL ((1u << MR_CHANNELS_LIST) - 1u) // CACHE-BASED OPTIMIZATION: Only keep active channels in RAM // Full array stays in EEPROM, cache holds ~10 most-used channels #define MR_CHANNELS_CACHE_SIZE 10 diff --git a/App/radio.c b/App/radio.c index fb84c2d9..e86ce69c 100644 --- a/App/radio.c +++ b/App/radio.c @@ -120,14 +120,38 @@ const char gModulationStr[MODULATION_UKNOWN][4] = { } #endif +bool RADIO_IsChannelInScanList(uint8_t channelScanList, uint8_t scanList) +{ + if (scanList < 1 || scanList > SCAN_LIST_MODE_MIX) + return false; + + if (channelScanList == 0) + return false; + + if (channelScanList == SCAN_LIST_MODE_ALL) + return true; + + if (scanList == SCAN_LIST_MODE_ALL) + return true; + + if (scanList == SCAN_LIST_MODE_MIX) { + if (channelScanList > MR_CHANNELS_LIST) + return false; + return (gEeprom.SCAN_LIST_MIX_MASK & (1u << (channelScanList - 1u))) != 0; + } + + return scanList >= 1 && scanList <= MR_CHANNELS_LIST && channelScanList == scanList; +} + bool RADIO_CheckValidList(uint8_t scanList) { - if(scanList == MR_CHANNELS_LIST + 1) + if (scanList == SCAN_LIST_MODE_ALL) return true; for (uint16_t i = 0; IS_MR_CHANNEL(i); i++) { const ChannelAttributes_t* att = MR_GetChannelAttributes(i); - if(att->scanlist == scanList && att->exclude == false) + if (att != NULL && !att->exclude && att->band <= BAND7_470MHz && + RADIO_IsChannelInScanList(att->scanlist, scanList)) { return true; } @@ -135,20 +159,43 @@ bool RADIO_CheckValidList(uint8_t scanList) return false; } +uint8_t RADIO_GetAdjacentScanList(uint8_t scanList, int8_t direction) +{ + if (scanList < 1 || scanList > SCAN_LIST_MODE_MIX) + scanList = 1; + + if (direction > 0) { + if (scanList == MR_CHANNELS_LIST) + return SCAN_LIST_MODE_MIX; + if (scanList == SCAN_LIST_MODE_MIX) + return SCAN_LIST_MODE_ALL; + if (scanList == SCAN_LIST_MODE_ALL) + return 1; + return scanList + 1; + } + + if (direction < 0) { + if (scanList == 1) + return SCAN_LIST_MODE_ALL; + if (scanList == SCAN_LIST_MODE_ALL) + return SCAN_LIST_MODE_MIX; + if (scanList == SCAN_LIST_MODE_MIX) + return MR_CHANNELS_LIST; + return scanList - 1; + } + + return scanList; +} + void RADIO_NextValidList(int8_t direction) { uint8_t startList = gEeprom.SCAN_LIST_DEFAULT; uint8_t attempts = 0; - const uint8_t MAX_VALUE = MR_CHANNELS_LIST + 1; // 25 (1-24 lists + ALL) + const uint8_t MAX_VALUE = SCAN_LIST_MODE_MIX; do { - if (direction > 0) { - // Forward: 1 → 2 → ... → 25 → 1 - gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT % MAX_VALUE) + 1; - } else { - // Backward: 25 → 24 → ... → 1 → 25 - gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT - 2 + MAX_VALUE) % MAX_VALUE) + 1; - } + gEeprom.SCAN_LIST_DEFAULT = RADIO_GetAdjacentScanList(gEeprom.SCAN_LIST_DEFAULT, + direction); attempts++; if (RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) @@ -158,7 +205,7 @@ void RADIO_NextValidList(int8_t direction) // Safety fallback: switch to ALL mode if (!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) { - gEeprom.SCAN_LIST_DEFAULT = MAX_VALUE; // ALL (25) + gEeprom.SCAN_LIST_DEFAULT = SCAN_LIST_MODE_ALL; } } @@ -169,13 +216,15 @@ bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanL // return true if the channel appears valid if (!IS_MR_CHANNEL(channel)) return false; + if (att == NULL) + return false; if (checkScanList && att->exclude == true) return false; if (att->band > BAND7_470MHz) return false; - if (!checkScanList || (scanList > MR_CHANNELS_LIST && att->scanlist != 0) || (scanList > 0 && att->scanlist == MR_CHANNELS_LIST + 1)) + if (!checkScanList) return true; - if ((scanList == 0) || (scanList != att->scanlist)) { + if (!RADIO_IsChannelInScanList(att->scanlist, scanList)) { return false; } @@ -322,7 +371,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure } else { band = channel - FREQ_CHANNEL_FIRST; - bParticipation = MR_CHANNELS_LIST + 1; + bParticipation = SCAN_LIST_MODE_ALL; } pVfo->Band = band; diff --git a/App/radio.h b/App/radio.h index cb61f582..ce55bbfe 100644 --- a/App/radio.h +++ b/App/radio.h @@ -152,7 +152,9 @@ extern VfoState_t VfoState[2]; extern const char *const VfoStateStr[]; bool RADIO_CheckValidList(uint8_t scanList); +uint8_t RADIO_GetAdjacentScanList(uint8_t scanList, int8_t direction); void RADIO_NextValidList(int8_t direction); +bool RADIO_IsChannelInScanList(uint8_t channelScanList, uint8_t scanList); bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList); uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum); void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency); diff --git a/App/settings.c b/App/settings.c index b6d58cc9..105c2af8 100644 --- a/App/settings.c +++ b/App/settings.c @@ -28,6 +28,8 @@ #include "settings.h" #include "ui/menu.h" +#define SETTINGS_SCAN_MIX_ADDR 0x00A170u + EEPROM_Config_t gEeprom = { 0 }; // Load a DTMF code from EEPROM, falling back to default_val if invalid. @@ -333,7 +335,7 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE PY25Q16_ReadBuffer(0x00A130, Data, 8); gEeprom.SCAN_LIST_DEFAULT = - (((Data[0] & 0x7F) >= 1) && ((Data[0] & 0x7F) <= (MR_CHANNELS_LIST + 1))) + (((Data[0] & 0x7F) >= 1) && ((Data[0] & 0x7F) <= SCAN_LIST_MODE_MIX)) ? (Data[0] & 0x7F) : 1; gEeprom.SCAN_LIST_ENABLED = (Data[0] >> 7) & 0x01; @@ -350,6 +352,19 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE (uint16_t)Data[5] | ((uint16_t)Data[6] << 8); + // 0F58..0F5F + PY25Q16_ReadBuffer(SETTINGS_SCAN_MIX_ADDR, Data, 8); + if (Data[3] == 'M' && Data[4] == 'I' && Data[5] == 'X' && Data[6] == 1) { + gEeprom.SCAN_LIST_MIX_MASK = + (uint32_t)Data[0] | + ((uint32_t)Data[1] << 8) | + ((uint32_t)Data[2] << 16); + if (gEeprom.SCAN_LIST_MIX_MASK == 0) + gEeprom.SCAN_LIST_MIX_MASK = SCAN_LIST_MIX_MASK_ALL; + } else { + gEeprom.SCAN_LIST_MIX_MASK = SCAN_LIST_MIX_MASK_ALL; + } + // 0F40..0F47 PY25Q16_ReadBuffer(0x00A150, Data, 8); gSetting_F_LOCK = (Data[0] < F_LOCK_LEN) ? Data[0] : F_LOCK_DEF; @@ -1150,6 +1165,17 @@ void SETTINGS_SaveSettings(void) #ifdef ENABLE_FEAT_F4HWN_VOL SETTINGS_WriteCurrentVol(); #endif + + // 0F58..0F5F + PY25Q16_ReadBuffer(SETTINGS_SCAN_MIX_ADDR, SecBuf, 8); + SecBuf[0] = (uint8_t)(gEeprom.SCAN_LIST_MIX_MASK & 0xFFu); + SecBuf[1] = (uint8_t)((gEeprom.SCAN_LIST_MIX_MASK >> 8) & 0xFFu); + SecBuf[2] = (uint8_t)((gEeprom.SCAN_LIST_MIX_MASK >> 16) & 0xFFu); + SecBuf[3] = 'M'; + SecBuf[4] = 'I'; + SecBuf[5] = 'X'; + SecBuf[6] = 1; + PY25Q16_WriteBuffer(SETTINGS_SCAN_MIX_ADDR, SecBuf, 8, false); } void SETTINGS_SaveChannel(uint16_t Channel, uint8_t VFO, const VFO_Info_t *pVFO, uint8_t Mode) diff --git a/App/settings.h b/App/settings.h index 3aa2d734..fe1bc98d 100644 --- a/App/settings.h +++ b/App/settings.h @@ -213,6 +213,7 @@ typedef struct { uint8_t BACKLIGHT_TIME; uint8_t SCAN_RESUME_MODE; uint8_t SCAN_LIST_DEFAULT; + uint32_t SCAN_LIST_MIX_MASK; bool SCAN_LIST_ENABLED; uint16_t SCANLIST_PRIORITY_CH[6]; //#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE // Fix me !!! What the hell is this? diff --git a/App/ui/main.c b/App/ui/main.c index 7849b067..54f36780 100644 --- a/App/ui/main.c +++ b/App/ui/main.c @@ -268,7 +268,7 @@ static inline bool ScanProgress_GetBit(const uint8_t *map, uint16_t ch) static uint8_t ScanProgress_GetActiveScanList(void) { - const uint8_t max_scan_list = MR_CHANNELS_LIST + 1; + const uint8_t max_scan_list = SCAN_LIST_MODE_MIX; uint8_t scan_list = gEeprom.SCAN_LIST_DEFAULT; if (scan_list == 0 || scan_list > max_scan_list) @@ -286,7 +286,9 @@ static void UI_MAIN_DrawScanListName(void) strcpy(text, "SCAN LIST "); char *p = text + 10; // sizeof("SCAN LIST ") - 1 - if (scan_list > MR_CHANNELS_LIST) { + if (scan_list == SCAN_LIST_MODE_MIX) { + *p++ = 'M'; *p++ = 'I'; *p++ = 'X'; + } else if (scan_list == SCAN_LIST_MODE_ALL) { *p++ = 'A'; *p++ = 'L'; *p++ = 'L'; } else { const char *name = gListName[scan_list - 1]; @@ -312,13 +314,7 @@ static bool ScanProgress_ChannelBelongsToList(uint16_t channel, const ChannelAtt if (att->band > BAND7_470MHz) return false; - if (scan_list > MR_CHANNELS_LIST && att->scanlist != 0) - return true; - - if (scan_list > 0 && att->scanlist == (MR_CHANNELS_LIST + 1)) - return true; - - if (scan_list == 0 || scan_list != att->scanlist) + if (!RADIO_IsChannelInScanList(att->scanlist, scan_list)) return false; if (gEeprom.SCAN_LIST_ENABLED) { @@ -1788,11 +1784,11 @@ void UI_DisplayMain(void) { // show the scan list assigment symbols uint8_t countList = att->scanlist; - if(countList > MR_CHANNELS_LIST + 1) { + if(countList > SCAN_LIST_MODE_ALL) { countList = 0; } - if (countList == MR_CHANNELS_LIST + 1) { + if (countList == SCAN_LIST_MODE_ALL) { displayStr = "ALL"; } else if (countList == 0) { diff --git a/App/ui/menu.c b/App/ui/menu.c index 4a6b77c7..7dc42b9f 100644 --- a/App/ui/menu.c +++ b/App/ui/menu.c @@ -825,7 +825,6 @@ static void UI_MENU_DrawTopRightRoundedBadge(const char *text, const uint8_t lin UI_PrintStringSmallNormalInverse(text, text_x, 0, line); } -#ifdef ENABLE_FEAT_F4HWN_MULTIBOOT /* Draw `text` (3x5 font) centred inside a fixed-width rounded inverse capsule: * left edge `cap_left`, inclusive width `cap_w`, on framebuffer page `line`. Same * capsule pattern as GUI_DisplaySmallestInverse (0x3E rounded ends, 0x7F body) but @@ -845,7 +844,34 @@ static void UI_MENU_DrawFixedCapsule(const char *text, uint8_t cap_left, gFrameBuffer[line][x] ^= 0x7Fu; gFrameBuffer[line][cap_right] ^= 0x3Eu; } -#endif + +static void UI_MENU_DrawScanMixEditor(void) +{ + char text[9]; + + // Keep the cursor centred, with two neighbouring lists above and below. + for (int8_t row = -2; row <= 2; row++) { + const uint8_t index = (uint8_t)((gScanMixEditorCursor + row + + MR_CHANNELS_LIST) % MR_CHANNELS_LIST); + const uint8_t line = (uint8_t)(3 + row); + const char *name = gListName[index]; + + if (IsEmptyName(name, sizeof(gListName[0]))) + sprintf(text, "%02u", (unsigned)(index + 1)); + else + sprintf(text, "%02u (%.3s)", (unsigned)(index + 1), name); + + // The longest label ends at x=109; the compact ON capsule stays right-aligned. + if (row == 0) { + UI_PrintStringSmallBold(text, 54, 0, line); + } else { + UI_PrintStringSmallNormal(text, 54, 0, line); + } + + if (gScanMixEditorMask & (1u << index)) + UI_MENU_DrawFixedCapsule("ON", 115, 11, line); + } +} void UI_DisplayMenu(void) { @@ -972,6 +998,13 @@ void UI_DisplayMenu(void) BACKLIGHT_TurnOn(); + if (gScanMixEditorActive) + { + UI_MENU_DrawScanMixEditor(); + ST7565_BlitFullScreen(); + return; + } + //#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO) uint8_t gaugeLine = 0; uint8_t gaugeMin = 0; @@ -1330,7 +1363,9 @@ void UI_DisplayMenu(void) case MENU_LIST_CH: case MENU_S_LIST: - if (gSubMenuSelection == MR_CHANNELS_LIST + 1) + if (gSubMenuSelection == SCAN_LIST_MODE_MIX && m == MENU_S_LIST) + strcpy(String, "MIX"); + else if (gSubMenuSelection == SCAN_LIST_MODE_ALL) strcpy(String, "ALL"); else if (gSubMenuSelection == 0 && m == MENU_LIST_CH) strcpy(String, "OFF"); diff --git a/App/ui/status.c b/App/ui/status.c index 789deee3..a4a7d362 100644 --- a/App/ui/status.c +++ b/App/ui/status.c @@ -157,7 +157,12 @@ void UI_DisplayStatus() if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode uint8_t end = 0; - if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1) + if (gEeprom.SCAN_LIST_DEFAULT == SCAN_LIST_MODE_MIX) + { + strcpy(str, "MIX"); + end = 14; + } + else if(gEeprom.SCAN_LIST_DEFAULT == SCAN_LIST_MODE_ALL) { strcpy(str, "ALL"); end = 14;