mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Decouple FoxHunt and Beacon actions
This commit is contained in:
1 parent
4c3847ef34
commit
0850420fdf
18 files changed
+246
-135
No files matched your search
+5
-3
@@ -234,9 +234,11 @@ enable_feature(ENABLE_FEAT_F4HWN_BEAM
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
app/rxtx_log.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
app/foxhunt.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_FOXHUNT)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_BEACON)
|
||||
if(ENABLE_FEAT_F4HWN_FOXHUNT OR ENABLE_FEAT_F4HWN_BEACON)
|
||||
target_sources(App INTERFACE app/foxhunt.c)
|
||||
endif()
|
||||
enable_feature(ENABLE_FEAT_F4HWN_ACTION_PICKER)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_ACTION_PICKER AND NOT ENABLE_FEAT_F4HWN)
|
||||
|
||||
+59
-4
@@ -51,7 +51,7 @@
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
#include "app/foxhunt.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
@@ -119,9 +119,12 @@ void (*const action_opt_table[ACTION_OPT_LEN])(void) = {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
[ACTION_OPT_RXTX_LOG] = &ACTION_RxTxLog,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
[ACTION_OPT_FOXHUNT] = &ACTION_FoxHunt,
|
||||
#endif
|
||||
#if defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
[ACTION_OPT_BEACON] = &ACTION_Beacon,
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(action_opt_table) == ACTION_OPT_LEN);
|
||||
@@ -130,10 +133,33 @@ static_assert(ACTION_OPT_BEAM == 19);
|
||||
static_assert(ACTION_OPT_POWER_HIGH == 20);
|
||||
static_assert(ACTION_OPT_REMOVE_OFFSET == 21);
|
||||
static_assert(ACTION_OPT_FOXHUNT == 22);
|
||||
static_assert(ACTION_OPT_BEACON == 23);
|
||||
|
||||
bool ACTION_IsAvailable(uint8_t action)
|
||||
{
|
||||
return action < ACTION_OPT_LEN && action_opt_table[action] != NULL;
|
||||
if (action >= ACTION_OPT_LEN || action_opt_table[action] == NULL)
|
||||
return false;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_OVERLAY_APPS
|
||||
switch (action) {
|
||||
#ifdef ENABLE_FMRADIO
|
||||
case ACTION_OPT_FM:
|
||||
return (APP_OverlayShortcutMask() & APP_SHORTCUT_FM) != 0;
|
||||
#endif
|
||||
#ifndef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
case ACTION_OPT_FOXHUNT:
|
||||
return (APP_OverlayShortcutMask() & APP_SHORTCUT_FOXHUNT) != 0;
|
||||
#endif
|
||||
#ifndef ENABLE_FEAT_F4HWN_BEACON
|
||||
case ACTION_OPT_BEACON:
|
||||
return (APP_OverlayShortcutMask() & APP_SHORTCUT_BEACON) != 0;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ACTION_Power(void)
|
||||
@@ -315,8 +341,11 @@ inline static bool ACTION_IsBlockedInFM(uint8_t action)
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
case ACTION_OPT_BEAM:
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
case ACTION_OPT_FOXHUNT:
|
||||
#endif
|
||||
#if defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
case ACTION_OPT_BEACON:
|
||||
#endif
|
||||
return true;
|
||||
|
||||
@@ -466,6 +495,32 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
ACTION_Execute(func);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
void ACTION_FoxHunt(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
APP_RunFoxHunt();
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
#else
|
||||
if (APP_LaunchOverlayByName("FoxHunt") != APP_OK)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
void ACTION_Beacon(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEACON
|
||||
APP_RunBeacon();
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
#else
|
||||
if (APP_LaunchOverlayByName("Beacon") != APP_OK)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
void ACTION_FM(void)
|
||||
|
||||
+1
-1
@@ -1985,7 +1985,7 @@ void APP_TimeSlice500ms(void)
|
||||
|
||||
if ((gBatteryCheckCounter & 1) == 0)
|
||||
{
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT)
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
BATTERY_Sample(true);
|
||||
#else
|
||||
BOARD_ADC_GetBatteryInfo(&gBatteryVoltages[gBatteryVoltageIndex++], &gBatteryCurrent);
|
||||
|
||||
+88
-110
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "app/foxhunt.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
|
||||
#if defined(ENABLE_UART) || defined(ENABLE_USB)
|
||||
#include "app/uart.h"
|
||||
@@ -86,7 +86,7 @@
|
||||
#define FOXHUNT_AUDIO_BEEP 1
|
||||
#define FOXHUNT_AUDIO_STATION 2
|
||||
|
||||
// --- Beacon (fox) sub-mode ---------------------------------------------------
|
||||
// --- Beacon (fox) app --------------------------------------------------------
|
||||
// Turns the radio into the hidden transmitter: each cycle keys up on the TX VFO and
|
||||
// repeats the CW fox identifier (MOE..MO5, or "<call> MOE") in Morse for the TX window,
|
||||
// then stays silent for the (adjustable) idle gap. The carrier stays up during the
|
||||
@@ -201,8 +201,7 @@ static const uint8_t FOXHUNT_MORSE_DIGIT[10] = {
|
||||
// Trailing character of each fox id: MOE / MOI / MOS / MOH / MO5 (index = FOXHUNT_FOX_*).
|
||||
static const char FOXHUNT_FOX_TAIL[5] = {'E', 'I', 'S', 'H', '5'};
|
||||
|
||||
// Beacon sub-mode state.
|
||||
static bool foxBeacon; // false = hunt (RX), true = beacon (TX)
|
||||
// Beacon state.
|
||||
static bool beaconPhaseTx; // true = transmit this tick, false = idle gap
|
||||
static uint8_t beaconIdle; // configured silence between IDs (s)
|
||||
static uint8_t beaconIdleLeft; // seconds left in the current silence
|
||||
@@ -782,11 +781,6 @@ static void FOXHUNT_HandleKeys(void)
|
||||
minDbm = curDbm;
|
||||
trendRef = curDbm;
|
||||
break;
|
||||
case KEY_SIDE1:
|
||||
case KEY_SIDE2:
|
||||
// Same shortcut that opened Fox Hunt now toggles to the beacon.
|
||||
FOXHUNT_EnterBeacon();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -794,22 +788,18 @@ static void FOXHUNT_HandleKeys(void)
|
||||
gWasFKeyPressed = false; // any non-F key consumes (or cancels) the reverse arm
|
||||
}
|
||||
|
||||
// (Re)enter the hunt (RX) sub-mode: fixed front-end gain, attenuator and audio mode
|
||||
// re-applied, peak/trend reset. Called at start-up and when leaving the beacon. The
|
||||
// audio mode is a hunt setting, so it is (re)applied here, not forced off — it must
|
||||
// survive a trip through the beacon.
|
||||
// Enter Fox Hunt with fixed front-end gain, the persisted attenuator/audio mode,
|
||||
// and freshly rebased level tracking.
|
||||
static void FOXHUNT_EnterHunt(void)
|
||||
{
|
||||
foxBeacon = false;
|
||||
|
||||
BK4819_SetAGC(false);
|
||||
FOXHUNT_ApplyAtt();
|
||||
FOXHUNT_SetAudio(); // (re)apply the current audio mode: off / beep / station
|
||||
|
||||
// Let the RSSI settle to the gain just applied, then reset the peak/min hold, trend
|
||||
// reference and signal history onto a fresh reading (also re-primes the sparkline on
|
||||
// return from the beacon). Same settle as FOXHUNT_AttCycle: without it a restored
|
||||
// BYP/BYP+ step at start-up or on beacon return would rebase onto the old-gain reading.
|
||||
// reference and signal history onto a fresh reading. Same settle as
|
||||
// FOXHUNT_AttCycle: without it a restored
|
||||
// BYP/BYP+ step at start-up would rebase onto the old-gain reading.
|
||||
// SetAudio already waits when audio is on, but not in the default audio-off case.
|
||||
SYSTEM_DelayMs(FOXHUNT_ATT_SETTLE_MS);
|
||||
FOXHUNT_RebaseMeasurements();
|
||||
@@ -836,9 +826,8 @@ static VfoState_t FOXHUNT_TxState(void)
|
||||
|
||||
// Refuse feedback shown when a burst is barred: reuse the beacon screen layout and
|
||||
// the radio's own state label (VfoStateStr, e.g. "TX DISABLE" / "BAT LOW" / "VOLT
|
||||
// HIGH"), same font as the main screen, for a beat; the caller then falls back to
|
||||
// the hunt. The RX front-end and audio path are left untouched so the hunt keeps
|
||||
// reading cleanly.
|
||||
// HIGH"), same font as the main screen, for a beat; the caller then waits through
|
||||
// a fresh idle gap before checking again.
|
||||
static void FOXHUNT_TxDeniedNotice(VfoState_t state)
|
||||
{
|
||||
// Frame (clear + BEACON tag + battery + barred TX frequency).
|
||||
@@ -855,12 +844,10 @@ static void FOXHUNT_TxDeniedNotice(VfoState_t state)
|
||||
FOXHUNT_TickDelay();
|
||||
}
|
||||
|
||||
// Enter the beacon (fox) sub-mode. The TX gates are enforced per burst in
|
||||
// FOXHUNT_BeaconTick (which runs immediately), so entry just arms the state.
|
||||
// Enter the Beacon app. TX gates are enforced before each burst.
|
||||
static void FOXHUNT_EnterBeacon(void)
|
||||
{
|
||||
foxBeacon = true;
|
||||
AUDIO_AudioPathOff(); // no RX audio while beaconing; foxAudioMode kept for the hunt
|
||||
AUDIO_AudioPathOff();
|
||||
gCurrentVfo = gTxVfo; // the VFO RADIO_SetTxParameters keys up
|
||||
beaconPhaseTx = true;
|
||||
}
|
||||
@@ -879,8 +866,8 @@ static void FOXHUNT_DrawTxSeconds(void)
|
||||
}
|
||||
|
||||
// Sleep in short slices while watching the keypad, so a burst stays interactive and the
|
||||
// TX-window countdown (beaconTxMsLeft) drains in real time. EXIT leaves Fox Hunt and a
|
||||
// side key aborts back to the hunt (both return true). The 1/2/3 keys edit the beacon
|
||||
// TX-window countdown (beaconTxMsLeft) drains in real time. EXIT leaves Beacon. The
|
||||
// 1/2/3 keys edit the beacon
|
||||
// settings live, exactly as in the idle phase: the TX window (1) and idle gap (2) take
|
||||
// effect at the upcoming window / gap, and a FOX change (3) at the next repeat (the
|
||||
// message is rebuilt there, never under the running loop).
|
||||
@@ -926,17 +913,12 @@ static bool FOXHUNT_TxDelay(uint16_t ms)
|
||||
continue;
|
||||
|
||||
if (kbd.current == KEY_EXIT) {
|
||||
foxRunning = false; // abort the burst and leave Fox Hunt
|
||||
return true;
|
||||
}
|
||||
if (kbd.current == KEY_SIDE1 || kbd.current == KEY_SIDE2) {
|
||||
foxBeacon = false; // abort the burst and switch back to hunt
|
||||
foxRunning = false; // abort the burst and leave Beacon
|
||||
return true;
|
||||
}
|
||||
if (kbd.current == KEY_MENU) {
|
||||
// Cut the transmission short but stay in Beacon: aborting the burst without
|
||||
// clearing foxBeacon/foxRunning drops the loop straight into a fresh idle
|
||||
// gap of the configured IDLE length.
|
||||
// Cut the transmission short but stay in Beacon: the loop drops straight
|
||||
// into a fresh idle gap of the configured IDLE length.
|
||||
return true;
|
||||
}
|
||||
if (kbd.current == KEY_F) { // arm / disarm the reverse step
|
||||
@@ -1051,8 +1033,8 @@ static void FOXHUNT_BeaconTransmit(void)
|
||||
stop = FOXHUNT_TxDelay(FOXHUNT_MORSE_UNIT_MS * 7);
|
||||
}
|
||||
|
||||
// Key down: mute, drop the PA and restore RX for the silent gap. If the burst
|
||||
// was interrupted, FOXHUNT_TxDelay already set the target mode (quit or hunt).
|
||||
// Key down: mute, drop the PA and restore RX for the silent gap. If EXIT
|
||||
// interrupted the burst, FOXHUNT_TxDelay has already stopped the app.
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
RADIO_SetupRegisters(true);
|
||||
@@ -1208,11 +1190,7 @@ static void FOXHUNT_BeaconKeys(void)
|
||||
|
||||
switch (kbd.current) {
|
||||
case KEY_EXIT:
|
||||
foxRunning = false; // leave Fox Hunt entirely
|
||||
break;
|
||||
case KEY_SIDE1:
|
||||
case KEY_SIDE2:
|
||||
foxBeacon = false; // toggle back to the hunt
|
||||
foxRunning = false;
|
||||
break;
|
||||
case KEY_MENU:
|
||||
// Restart a full idle interval now (the TX-phase M drops here too).
|
||||
@@ -1237,28 +1215,26 @@ static void FOXHUNT_BeaconTick(void)
|
||||
// LOCK / TX LOCK and modulation are fixed for the session, but the battery
|
||||
// is re-sampled over the idle gaps (FOXHUNT_IdleHousekeeping), so a beacon
|
||||
// left running stops keying up once the pack falls low or over-voltage
|
||||
// instead of transmitting blind. Refuse, notify, and drop back to hunt.
|
||||
// instead of transmitting blind. Refuse, notify, then retry after an idle gap.
|
||||
VfoState_t state = FOXHUNT_TxState();
|
||||
if (state != VFO_STATE_NORMAL) {
|
||||
FOXHUNT_TxDeniedNotice(state);
|
||||
foxBeacon = false;
|
||||
beaconPhaseTx = false;
|
||||
FOXHUNT_EnterHunt();
|
||||
beaconIdleLeft = beaconIdle;
|
||||
beaconIdleTick = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// The burst repeats the ID for the whole TX window, redrawing the screen
|
||||
// itself each repeat (remaining-window countdown); it may clear foxBeacon
|
||||
// (side-key abort) or foxRunning (EXIT).
|
||||
// itself each repeat (remaining-window countdown); EXIT may clear
|
||||
// foxRunning and abort it.
|
||||
FOXHUNT_BeaconTransmit();
|
||||
|
||||
beaconPhaseTx = false;
|
||||
beaconIdleLeft = beaconIdle;
|
||||
beaconIdleTick = 0;
|
||||
|
||||
if (foxRunning && !foxBeacon) // side-key switched to hunt
|
||||
FOXHUNT_EnterHunt();
|
||||
return; // EXIT (quit) falls through
|
||||
return;
|
||||
}
|
||||
|
||||
// Idle (silent) phase: responsive, editable countdown.
|
||||
@@ -1267,12 +1243,8 @@ static void FOXHUNT_BeaconTick(void)
|
||||
#endif
|
||||
FOXHUNT_BeaconKeys();
|
||||
|
||||
if (!foxRunning) // EXIT: leave Fox Hunt
|
||||
if (!foxRunning)
|
||||
return;
|
||||
if (!foxBeacon) { // side-key: back to the hunt
|
||||
FOXHUNT_EnterHunt();
|
||||
return;
|
||||
}
|
||||
|
||||
FOXHUNT_BeaconDraw(false, beaconIdleLeft);
|
||||
FOXHUNT_BlitScreen();
|
||||
@@ -1340,7 +1312,7 @@ static void FOXHUNT_LoadConfig(void)
|
||||
|
||||
// Write the settings to flash, but only when they changed since the last write.
|
||||
// Called on the 500 ms tick (so any change persists within ~0.5 s and survives a
|
||||
// power-off, not just a clean EXIT) and once more on leaving Fox Hunt. The RAM
|
||||
// power-off, not just a clean EXIT) and once more on leaving either app. The RAM
|
||||
// compare avoids re-reading the 4 KB sector on every quiet tick.
|
||||
static void FOXHUNT_SaveConfig(void)
|
||||
{
|
||||
@@ -1354,31 +1326,46 @@ static void FOXHUNT_SaveConfig(void)
|
||||
memcpy(foxCfgSaved, cfg, sizeof(cfg));
|
||||
}
|
||||
|
||||
void APP_RunFoxHunt(void)
|
||||
static void FOXHUNT_Begin(void)
|
||||
{
|
||||
// Finish any pending backlight fade, then start with the screen on and a full
|
||||
// BLTime window (BACKLIGHT_TurnOn also re-arms the sleep countdown).
|
||||
BACKLIGHT_UpdateTickless();
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
// Hunt on the user-selected VFO: dual-watch / cross-band may have left the
|
||||
// radio listening on the other VFO, so force RX on the selected one and retune.
|
||||
// Both apps operate on the user-selected VFO. Dual watch may have left the
|
||||
// receiver on the other one, so establish a deterministic starting point.
|
||||
gEeprom.RX_VFO = gEeprom.TX_VFO;
|
||||
gRxVfo = gTxVfo;
|
||||
gCurrentVfo = gTxVfo;
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
// Beacon defaults (LoadConfig may override fox / tx / idle below).
|
||||
// Initialise the complete legacy seven-byte configuration before loading it.
|
||||
// Keeping the layout unchanged preserves existing FieldOps settings and lets
|
||||
// either independently compiled app update its fields without a migration.
|
||||
attStep = 0;
|
||||
foxGraphMode = FOXHUNT_GRAPH_BAR;
|
||||
foxAudioMode = FOXHUNT_AUDIO_OFF;
|
||||
beaconIdle = FOXHUNT_BEACON_IDLE_DEF;
|
||||
beaconTx = FOXHUNT_BEACON_TX_DEF;
|
||||
foxFox = FOXHUNT_FOX_CALL; // identified beacon by default (legal on ham)
|
||||
foxFox = FOXHUNT_FOX_CALL;
|
||||
beaconPhaseTx = false;
|
||||
FOXHUNT_LoadConfig();
|
||||
|
||||
// Ignore the key that launched the modal app until it has been released.
|
||||
kbd.prev = kbd.current = KEYBOARD_GetKey();
|
||||
gWasFKeyPressed = false;
|
||||
foxLocked = false;
|
||||
fHoldMs = 0;
|
||||
fLongDone = false;
|
||||
foxRunning = true;
|
||||
}
|
||||
|
||||
static void FOXHUNT_LoadCallsign(void)
|
||||
{
|
||||
char call[FOXHUNT_CALLSIGN_MAX + 1];
|
||||
uint8_t n = 0;
|
||||
|
||||
// Read the callsign from the boot message (line 1), sanitised to what Morse can
|
||||
// send; empty/erased leaves foxCall empty (the CALL id then falls back to bare MOE).
|
||||
{
|
||||
char call[FOXHUNT_CALLSIGN_MAX + 1];
|
||||
uint8_t n = 0;
|
||||
PY25Q16_ReadBuffer(FOXHUNT_CALLSIGN_ADDR, call, FOXHUNT_CALLSIGN_MAX);
|
||||
call[FOXHUNT_CALLSIGN_MAX] = '\0';
|
||||
for (uint8_t i = 0; i < FOXHUNT_CALLSIGN_MAX; i++) {
|
||||
@@ -1391,46 +1378,38 @@ void APP_RunFoxHunt(void)
|
||||
foxCall[n++] = c;
|
||||
}
|
||||
foxCall[n] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// Start in the hunt (RX) sub-mode, S-meter staircase gauge by default, then restore
|
||||
// any persisted settings (attenuator, gauge, audio, beacon idle / fox / tx).
|
||||
attStep = 0;
|
||||
foxGraphMode = FOXHUNT_GRAPH_BAR;
|
||||
foxAudioMode = FOXHUNT_AUDIO_OFF;
|
||||
FOXHUNT_LoadConfig(); // may override att / gauge / audio / idle / fox / tx
|
||||
FOXHUNT_EnterHunt(); // applies the restored attStep and audio mode
|
||||
// beaconMsg is (re)built by FOXHUNT_BeaconTransmit before every burst, and nothing
|
||||
// reads it before the first burst, so no need to assemble it here.
|
||||
static void FOXHUNT_End(void)
|
||||
{
|
||||
FOXHUNT_SaveConfig();
|
||||
gWasFKeyPressed = false;
|
||||
AUDIO_AudioPathOff();
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
|
||||
// Prime the key state with whatever is held right now, so the side key that
|
||||
// launched Fox Hunt (still down on a long-press assignment) is not taken for
|
||||
// a fresh press and does not immediately toggle to the beacon.
|
||||
kbd.prev = kbd.current = KEYBOARD_GetKey();
|
||||
gWasFKeyPressed = false; // start with the reverse-step arm cleared
|
||||
/* Both entry points stay in this translation unit so their small modal services
|
||||
* can be shared when both features are enabled. The action table references only
|
||||
* the enabled entry point(s); --gc-sections then drops the complete unused RX or
|
||||
* Beacon call graph, including its private constants and bitmaps. */
|
||||
void APP_RunFoxHunt(void)
|
||||
{
|
||||
FOXHUNT_Begin();
|
||||
|
||||
// Always enter unlocked: the keypad lock is a transient safety toggle, not a
|
||||
// persisted setting, so a previous session must not leave the pad locked.
|
||||
foxLocked = false;
|
||||
fHoldMs = 0;
|
||||
fLongDone = false;
|
||||
|
||||
foxRunning = true;
|
||||
FOXHUNT_EnterHunt();
|
||||
while (foxRunning) {
|
||||
#if defined(ENABLE_UART) || defined(ENABLE_USB)
|
||||
UART_ServiceCommands();
|
||||
#endif
|
||||
if (foxBeacon) {
|
||||
FOXHUNT_BeaconTick();
|
||||
continue;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
// Keep the K5Viewer link alive and pick up any remote key.
|
||||
K5VIEWER_ParseInput();
|
||||
#endif
|
||||
FOXHUNT_HandleKeys();
|
||||
if (foxBeacon) // just switched to beacon: skip the RX work this tick
|
||||
continue;
|
||||
if (!foxRunning)
|
||||
break;
|
||||
|
||||
curDbm = FOXHUNT_ReadDbm();
|
||||
if (curDbm > peakDbm)
|
||||
@@ -1468,24 +1447,23 @@ void APP_RunFoxHunt(void)
|
||||
FOXHUNT_TickDelay(); // tick delay + smooth backlight fade
|
||||
}
|
||||
|
||||
// Persist the session's settings on the way out (EXIT clears foxRunning) so
|
||||
// they survive a later power cycle.
|
||||
FOXHUNT_SaveConfig();
|
||||
|
||||
gWasFKeyPressed = false; // don't leak a pending reverse-step arm to the main screen
|
||||
|
||||
// Mute any pending tone, drop the PA (safety), then restore the normal VFO
|
||||
// selection and RX config.
|
||||
AUDIO_AudioPathOff();
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
FOXHUNT_End();
|
||||
}
|
||||
|
||||
void ACTION_FoxHunt(void)
|
||||
void APP_RunBeacon(void)
|
||||
{
|
||||
APP_RunFoxHunt();
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
FOXHUNT_Begin();
|
||||
FOXHUNT_LoadCallsign();
|
||||
FOXHUNT_EnterBeacon();
|
||||
|
||||
while (foxRunning) {
|
||||
#if defined(ENABLE_UART) || defined(ENABLE_USB)
|
||||
UART_ServiceCommands();
|
||||
#endif
|
||||
FOXHUNT_BeaconTick();
|
||||
}
|
||||
|
||||
FOXHUNT_End();
|
||||
}
|
||||
|
||||
#endif // ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#endif // ENABLE_FEAT_F4HWN_FOXHUNT || ENABLE_FEAT_F4HWN_BEACON
|
||||
+12
-3
@@ -17,7 +17,7 @@
|
||||
#ifndef APP_FOXHUNT_H
|
||||
#define APP_FOXHUNT_H
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
|
||||
#include "keyboard_state.h"
|
||||
|
||||
@@ -46,12 +46,21 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
// Entry point wired as an assignable side-key action (see action.c).
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
// Assignable resident/overlay launchers (see action.c).
|
||||
void ACTION_FoxHunt(void);
|
||||
void ACTION_Beacon(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
// Self-contained modal loop that owns the screen and keypad until EXIT.
|
||||
void APP_RunFoxHunt(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEACON
|
||||
void APP_RunBeacon(void);
|
||||
#endif
|
||||
|
||||
#endif // ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#endif // APP_FOXHUNT_H
|
||||
@@ -633,8 +633,60 @@ uint8_t APP_ValidateSlot(uint8_t slot, app_header_t *out_header)
|
||||
return APP_OK;
|
||||
}
|
||||
|
||||
static bool app_shortcuts_cached;
|
||||
static uint8_t app_shortcut_mask;
|
||||
static uint8_t app_shortcut_slots[3];
|
||||
|
||||
static int8_t app_shortcut_index(const char *name)
|
||||
{
|
||||
if (strncmp(name, "Broadcast FM", APP_NAME_LEN) == 0) return 0;
|
||||
if (strncmp(name, "FoxHunt", APP_NAME_LEN) == 0) return 1;
|
||||
if (strncmp(name, "Beacon", APP_NAME_LEN) == 0) return 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void app_cache_shortcuts(void)
|
||||
{
|
||||
if (app_shortcuts_cached)
|
||||
return;
|
||||
|
||||
app_shortcut_mask = 0;
|
||||
const uint32_t overlay_vma = (uint32_t)PY25Q16_OverlayBuffer();
|
||||
|
||||
for (uint8_t slot = 0; slot < APP_SLOT_COUNT; slot++) {
|
||||
app_header_t h;
|
||||
if (APP_ValidateSlot(slot, &h) != APP_OK || h.link_vma != overlay_vma)
|
||||
continue;
|
||||
|
||||
const int8_t index = app_shortcut_index(h.name);
|
||||
if (index >= 0) {
|
||||
const uint8_t bit = (uint8_t)(1u << index);
|
||||
if (!(app_shortcut_mask & bit)) {
|
||||
app_shortcut_mask |= bit;
|
||||
app_shortcut_slots[index] = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_shortcuts_cached = true;
|
||||
}
|
||||
|
||||
uint8_t APP_OverlayShortcutMask(void)
|
||||
{
|
||||
app_cache_shortcuts();
|
||||
return app_shortcut_mask;
|
||||
}
|
||||
|
||||
uint8_t APP_LaunchOverlayByName(const char *name)
|
||||
{
|
||||
const int8_t index = app_shortcut_index(name);
|
||||
if (index >= 0) {
|
||||
app_cache_shortcuts();
|
||||
return (app_shortcut_mask & (1u << index))
|
||||
? APP_LaunchOverlay(app_shortcut_slots[index])
|
||||
: APP_ERR_MAGIC;
|
||||
}
|
||||
|
||||
app_header_t h;
|
||||
|
||||
for (uint8_t slot = 0; slot < APP_SLOT_COUNT; slot++) {
|
||||
@@ -840,6 +892,7 @@ uint8_t APP_SlotErase(uint8_t slot)
|
||||
for (uint32_t off = 0; off < APP_SLOT_STRIDE; off += APP_SECTOR_SIZE)
|
||||
PY25Q16_SectorErase(base + off);
|
||||
PY25Q16_InvalidateCache();
|
||||
app_shortcuts_cached = false;
|
||||
return APP_OK;
|
||||
}
|
||||
|
||||
@@ -851,6 +904,7 @@ uint8_t APP_SlotWrite(uint8_t slot, uint32_t offset, const uint8_t *data, uint32
|
||||
return APP_ERR_SIZE;
|
||||
PY25Q16_WriteBuffer(APP_SLOT_BASE(slot) + offset, data, len, false);
|
||||
PY25Q16_InvalidateCache();
|
||||
app_shortcuts_cached = false;
|
||||
return APP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@
|
||||
#define APP_NAME_LEN 16
|
||||
#define APP_VERSION_LEN 16
|
||||
|
||||
#define APP_SHORTCUT_FM (1u << 0)
|
||||
#define APP_SHORTCUT_FOXHUNT (1u << 1)
|
||||
#define APP_SHORTCUT_BEACON (1u << 2)
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t magic; /* APP_MAGIC */
|
||||
uint16_t hdr_version; /* APP_HDR_VERSION */
|
||||
@@ -106,6 +110,9 @@ uint8_t APP_LaunchOverlay(uint8_t slot);
|
||||
/* Find the first valid app with this exact header name and launch it. */
|
||||
uint8_t APP_LaunchOverlayByName(const char *name);
|
||||
|
||||
/* Cached availability of apps exposed as resident quick actions. */
|
||||
uint8_t APP_OverlayShortcutMask(void);
|
||||
|
||||
/* Host-tool slot management (external flash only, never brick-critical). */
|
||||
uint8_t APP_SlotInfo(uint8_t slot, app_header_t *out_header);
|
||||
uint8_t APP_SlotErase(uint8_t slot);
|
||||
|
||||
+1
-1
@@ -301,7 +301,7 @@ const uint8_t BITMAP_NOAA[12] =
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
const uint8_t BITMAP_FoxHuntSignal[10] =
|
||||
{ // point source + the speaker bitmap's two sound waves, oriented horizontally
|
||||
0b00001000,
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ extern const uint8_t BITMAP_compand[6];
|
||||
|
||||
extern const uint8_t BITMAP_NOAA[12];
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
extern const uint8_t BITMAP_FoxHuntSignal[10];
|
||||
extern const uint8_t BITMAP_FoxHuntSpeaker[10];
|
||||
extern const uint8_t BITMAP_FoxHuntUp[11];
|
||||
|
||||
@@ -54,10 +54,9 @@ static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
_MK_MAPPING(0x009000, 0x009000, 0x0090E7), // 14 VFO * 16 Bytes = 0x9000 -> 0x90E0 (the old
|
||||
// 0x90D6 bound was 10 B short: it truncated the
|
||||
// 470 MHz VFO1 record at 0x90D0 -> 0x90DF)
|
||||
// Fox Hunt settings * 7 Bytes 0x90E0 -> 0x90E7
|
||||
// (ENABLE_FEAT_F4HWN_FOXHUNT, written directly by
|
||||
// app/foxhunt.c; concatenated here so aircopy clones
|
||||
// them together with the VFOs)
|
||||
// Fox Hunt / Beacon settings * 7 Bytes 0x90E0 -> 0x90E7
|
||||
// (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
|
||||
// Settings * 16 Bytes (ex 0x005000) 0x00A010 -> 0x00A020
|
||||
|
||||
@@ -130,7 +130,7 @@ unsigned int BATTERY_VoltsToPercent(const unsigned int voltage_10mV)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT)
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
void BATTERY_Sample(const bool bDisplayBatteryLevel)
|
||||
{
|
||||
BOARD_ADC_GetBatteryInfo(&gBatteryVoltages[gBatteryVoltageIndex++], &gBatteryCurrent);
|
||||
|
||||
@@ -45,7 +45,7 @@ typedef enum {
|
||||
|
||||
|
||||
unsigned int BATTERY_VoltsToPercent(unsigned int voltage_10mV);
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT)
|
||||
#if defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS) || defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON)
|
||||
void BATTERY_Sample(bool bDisplayBatteryLevel);
|
||||
#endif
|
||||
void BATTERY_GetReadings(bool bDisplayBatteryLevel);
|
||||
|
||||
+2
-1
@@ -126,8 +126,9 @@ enum ACTION_OPT_t {
|
||||
ACTION_OPT_POWER_HIGH = 20,
|
||||
ACTION_OPT_REMOVE_OFFSET = 21,
|
||||
ACTION_OPT_FOXHUNT = 22,
|
||||
ACTION_OPT_BEACON = 23,
|
||||
|
||||
ACTION_OPT_LEN = 23
|
||||
ACTION_OPT_LEN = 24
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
+2
-1
@@ -495,7 +495,8 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{"BEAM", ACTION_OPT_BEAM},
|
||||
{"POWER\nHIGH", ACTION_OPT_POWER_HIGH},
|
||||
{"REMOVE\nOFFSET", ACTION_OPT_REMOVE_OFFSET},
|
||||
{"FOX HUNT\nBEACON", ACTION_OPT_FOXHUNT},
|
||||
{"FOX HUNT", ACTION_OPT_FOXHUNT},
|
||||
{"BEACON", ACTION_OPT_BEACON},
|
||||
};
|
||||
|
||||
const uint8_t gSubMenu_SIDEFUNCTIONS_size = ARRAY_SIZE(gSubMenu_SIDEFUNCTIONS);
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ static void convertTime(uint8_t *line, uint8_t type)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
void UI_DrawStatusBattery(uint8_t *line, char *str)
|
||||
#else
|
||||
static __attribute__((always_inline)) inline void UI_DrawStatusBattery(uint8_t *line, char *str)
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
#if defined(ENABLE_FEAT_F4HWN_FOXHUNT) || defined(ENABLE_FEAT_F4HWN_BEACON) || defined(ENABLE_FEAT_F4HWN_OVERLAY_APPS)
|
||||
void UI_DrawStatusBattery(uint8_t *line, char *str);
|
||||
#endif
|
||||
void UI_DisplayStatus();
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": false,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": false,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
|
||||
"ENABLE_FEAT_F4HWN_BEACON": false,
|
||||
"ENABLE_FEAT_F4HWN_ACTION_PICKER": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
|
||||
@@ -132,6 +133,7 @@
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": false,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
|
||||
"ENABLE_FEAT_F4HWN_BEACON": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG": true,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": true,
|
||||
@@ -155,6 +157,7 @@
|
||||
"cacheVariables": {
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
|
||||
"ENABLE_FEAT_F4HWN_BEACON": true,
|
||||
"EDITION_STRING": "FieldOps",
|
||||
"TARGET": "f4hwn.fieldops"
|
||||
}
|
||||
@@ -175,6 +178,7 @@
|
||||
"cacheVariables": {
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
|
||||
"ENABLE_FEAT_F4HWN_BEACON": false,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
|
||||
@@ -187,7 +187,7 @@ Specialized presets extend Fusion for specific uses:
|
||||
- selectable RF attenuation,
|
||||
- silent, Geiger-style and received-audio modes,
|
||||
- long-press `F` keypad lock (attenuation stays adjustable with the arrow keys).
|
||||
- Integrated Morse Beacon transmitter with:
|
||||
- Independent Morse Beacon transmitter with:
|
||||
- `MOE`, `MOI`, `MOS`, `MOH`, `MO5` and `MO` identifiers,
|
||||
- optional callsign identification,
|
||||
- configurable TX and idle periods,
|
||||
@@ -264,7 +264,8 @@ Specialized presets extend Fusion for specific uses:
|
||||
- offset removal,
|
||||
- BEAM,
|
||||
- RF Log,
|
||||
- Fox Hunt / Beacon.
|
||||
- Fox Hunt,
|
||||
- Beacon.
|
||||
|
||||
### Reliability and optimization
|
||||
|
||||
|
||||
Reference in new issue
Block a user