mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Add optional CARR carrier-keying mode to beacon
This commit is contained in:
1 parent
07226822cd
commit
34ea3fd6af
5 files changed
+82
-36
No files matched your search
+54
-20
@@ -90,8 +90,12 @@
|
||||
// --- 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
|
||||
// window; only the tone modulation is keyed on/off (EnterTxMute/ExitTxMute) = MCW on FM.
|
||||
// then stays silent for the (adjustable) idle gap. Two keying modes (KEY_4):
|
||||
// TONE (default): carrier stays up the whole window, only the tone is keyed on/off
|
||||
// (EnterTxMute/ExitTxMute) = MCW on FM (F2A).
|
||||
// CARR: the carrier (PA_ENABLE) is keyed together with the tone, so between
|
||||
// elements the carrier itself is gone (carrier interruption, the ARDF
|
||||
// field pattern) — harder to home in on, at the cost of some key clicks.
|
||||
#define FOXHUNT_BEACON_TONE_HZ 1000 // CW tone pitch (Hz)
|
||||
#define FOXHUNT_MORSE_UNIT_MS 100 // one Morse time unit (~12 WPM, ARDF pace)
|
||||
#define FOXHUNT_BEACON_IDLE_DEF 30 // default silence between IDs (s)
|
||||
@@ -212,6 +216,7 @@ static uint8_t beaconTx; // TX window length (s): the ID repeats for th
|
||||
static uint16_t beaconTxMsLeft; // ms left in the current TX window (drained as it plays)
|
||||
static uint8_t beaconTxSecShown; // whole-second value last painted on the TX line
|
||||
static uint8_t foxFox; // selected fox identifier (FOXHUNT_FOX_*)
|
||||
static bool beaconCarrierKeyed; // false = TONE (F2A, keyed tone); true = CARR (keyed carrier)
|
||||
static char foxCall[FOXHUNT_CALLSIGN_MAX + 1]; // sanitised callsign for the CALL id
|
||||
|
||||
static void FOXHUNT_EnterHunt(void);
|
||||
@@ -638,14 +643,15 @@ static void FOXHUNT_IdleCycle(int8_t dir)
|
||||
}
|
||||
|
||||
// Apply a beacon number key in the given direction; returns true when it changed a
|
||||
// setting so the caller can refresh. Keys follow the on-screen layout: 1 = TX (top-left),
|
||||
// 2 = IDLE (below it), 3 = FOX (right). Shared by both beacon phases.
|
||||
// setting so the caller can refresh. Keys: 1 = TX, 2 = IDLE, 3 = FOX, 4 = keying mode
|
||||
// (TONE/CARR, a plain toggle so dir is moot). Shared by both beacon phases.
|
||||
static bool FOXHUNT_BeaconKey(KEY_Code_t key, int8_t dir)
|
||||
{
|
||||
switch (key) {
|
||||
case KEY_1: FOXHUNT_TxCycle(dir); return true;
|
||||
case KEY_2: FOXHUNT_IdleCycle(dir); return true;
|
||||
case KEY_3: FOXHUNT_FoxCycle(dir); return true;
|
||||
case KEY_4: beaconCarrierKeyed = !beaconCarrierKeyed; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -946,8 +952,27 @@ static uint8_t FOXHUNT_MorseByte(char c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send one character as modulated CW: key the running TX tone on per element,
|
||||
// muting the modulation (carrier stays up) between them. Returns true if aborted.
|
||||
// Beacon keying primitive. Both modes key the tone (Enter/ExitTxMute); CARR additionally
|
||||
// gates the PA in lockstep, so between elements the carrier itself is gone — not just the
|
||||
// tone. Muting the tone in CARR too is deliberate: gating PA_ENABLE does not perfectly
|
||||
// kill the carrier, and a tone left riding would bleed through that residual carrier as a
|
||||
// near-continuous note. The PLL stays locked, so the carrier gate is only a GPIO toggle.
|
||||
static void FOXHUNT_KeyOn(void)
|
||||
{
|
||||
BK4819_ExitTxMute();
|
||||
if (beaconCarrierKeyed)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, true);
|
||||
}
|
||||
static void FOXHUNT_KeyOff(void)
|
||||
{
|
||||
BK4819_EnterTxMute();
|
||||
if (beaconCarrierKeyed)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
}
|
||||
|
||||
// Send one character as Morse: key each element on (FOXHUNT_KeyOn) for its dit/dah
|
||||
// duration, off (FOXHUNT_KeyOff) in the gaps. What "key" means depends on the beacon
|
||||
// mode (tone vs carrier), see above. Returns true if aborted.
|
||||
static bool FOXHUNT_MorseChar(char c, uint8_t visibleChars)
|
||||
{
|
||||
uint8_t code = FOXHUNT_MorseByte(c);
|
||||
@@ -964,9 +989,9 @@ static bool FOXHUNT_MorseChar(char c, uint8_t visibleChars)
|
||||
for (bit >>= 1; bit; bit >>= 1) { // then walk the elements, MSB first
|
||||
const uint16_t on = (code & bit) ? (FOXHUNT_MORSE_UNIT_MS * 3) // dah
|
||||
: FOXHUNT_MORSE_UNIT_MS; // dit
|
||||
BK4819_ExitTxMute();
|
||||
if (FOXHUNT_TxDelay(on)) { BK4819_EnterTxMute(); return true; }
|
||||
BK4819_EnterTxMute();
|
||||
FOXHUNT_KeyOn();
|
||||
if (FOXHUNT_TxDelay(on)) { FOXHUNT_KeyOff(); return true; }
|
||||
FOXHUNT_KeyOff();
|
||||
if (FOXHUNT_TxDelay(FOXHUNT_MORSE_UNIT_MS)) return true; // intra gap
|
||||
}
|
||||
|
||||
@@ -985,11 +1010,14 @@ static bool FOXHUNT_MorseChar(char c, uint8_t visibleChars)
|
||||
// keys up for a fixed slot rather than sending a single one-shot.
|
||||
static void FOXHUNT_BeaconTransmit(void)
|
||||
{
|
||||
RADIO_SetTxParameters(); // key up: carrier + PA
|
||||
RADIO_SetTxParameters(); // key up: carrier + PA + PLL lock
|
||||
|
||||
// Prime the tone generator, then start silent before the first element.
|
||||
// Prime the tone generator, then open the window silent before the first element:
|
||||
// tone muted in both modes, and in CARR the carrier dropped too (PLL still locked).
|
||||
BK4819_TransmitTone(false, FOXHUNT_BEACON_TONE_HZ);
|
||||
BK4819_EnterTxMute();
|
||||
if (beaconCarrierKeyed)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
|
||||
beaconTxMsLeft = (uint16_t)beaconTx * 1000u; // drained inside FOXHUNT_TxDelay
|
||||
|
||||
@@ -1151,16 +1179,19 @@ static void FOXHUNT_BeaconDraw(bool txNow, uint8_t idleLeft)
|
||||
UI_PrintString(str, 0, 127, 3, 10);
|
||||
}
|
||||
|
||||
// Bottom-left column: 1 = TX window over 2 = IDLE gap. Bottom-right: 3 = FOX id over
|
||||
// the freq (drawn by FOXHUNT_BeaconChrome). All three are inverse tags; each has its
|
||||
// own number key, editable in either phase, so there is no cursor to show.
|
||||
// Settings tags: TX (line 5, left) over IDLE (line 6, left). On line 5, right of TX:
|
||||
// FOX shifted left with the keying mode (TONE/CARR) right-aligned beside it, so the pair
|
||||
// spans roughly the width of the TX frequency drawn just below on line 6
|
||||
// (FOXHUNT_BeaconChrome). Keys: 1 TX, 2 IDLE, 3 FOX, 4 mode; editable in either phase.
|
||||
sprintf(str, "TX %us", beaconTx);
|
||||
FOXHUNT_Tag(str, 4, 5);
|
||||
sprintf(str, "IDLE %us", beaconIdle);
|
||||
FOXHUNT_Tag(str, 4, 6);
|
||||
|
||||
FOXHUNT_FoxLabel(str);
|
||||
FOXHUNT_Tag(str, (uint8_t)(125 - strlen(str) * 4), 5);
|
||||
FOXHUNT_Tag(str, 66, 5);
|
||||
|
||||
const char *mode = beaconCarrierKeyed ? "CARR" : "TONE";
|
||||
FOXHUNT_Tag(mode, (uint8_t)(125 - strlen(mode) * 4), 5);
|
||||
}
|
||||
|
||||
static void FOXHUNT_BeaconKeys(void)
|
||||
@@ -1270,7 +1301,7 @@ static void FOXHUNT_BeaconTick(void)
|
||||
// (eeprom_compat.c) so aircopy clones them with the VFOs. A factory reset clears it.
|
||||
#define FOXHUNT_CFG_ADDR 0x0090E0u
|
||||
#define FOXHUNT_CFG_MAGIC 0xF4u // tells a written config from erased flash (0xFF)
|
||||
#define FOXHUNT_CFG_LEN 7 // magic + att + graph + audio + idle + fox + tx
|
||||
#define FOXHUNT_CFG_LEN 8 // magic + att + graph + audio + idle + fox + tx + carrier
|
||||
|
||||
// RAM mirror of the bytes last written to flash, so FOXHUNT_SaveConfig only touches
|
||||
// the flash when a value actually changed.
|
||||
@@ -1286,6 +1317,7 @@ static __attribute__((noinline)) void FOXHUNT_ConfigPack(uint8_t out[FOXHUNT_CFG
|
||||
out[4] = beaconIdle;
|
||||
out[5] = foxFox;
|
||||
out[6] = beaconTx;
|
||||
out[7] = beaconCarrierKeyed;
|
||||
}
|
||||
|
||||
// Restore persisted settings; erased/invalid flash leaves the defaults in place.
|
||||
@@ -1305,6 +1337,7 @@ static void FOXHUNT_LoadConfig(void)
|
||||
if (cfg[6] >= FOXHUNT_BEACON_TX_MIN && cfg[6] <= FOXHUNT_BEACON_TX_MAX
|
||||
&& (cfg[6] % FOXHUNT_BEACON_TX_STEP) == 0)
|
||||
beaconTx = cfg[6];
|
||||
if (cfg[7] <= 1) beaconCarrierKeyed = cfg[7]; // erased (0xFF) legacy config -> keep default
|
||||
}
|
||||
|
||||
FOXHUNT_ConfigPack(foxCfgSaved); // mirror the loaded (or default) state
|
||||
@@ -1338,15 +1371,16 @@ static void FOXHUNT_Begin(void)
|
||||
gCurrentVfo = gTxVfo;
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
// 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.
|
||||
// Initialise the full configuration before loading it. The carrier-keying byte was
|
||||
// appended (index 7): an older seven-byte config reads 0xFF there and LoadConfig's
|
||||
// `<= 1` guard rejects it, so it stays at the default below — no migration needed.
|
||||
attStep = 0;
|
||||
foxGraphMode = FOXHUNT_GRAPH_BAR;
|
||||
foxAudioMode = FOXHUNT_AUDIO_OFF;
|
||||
beaconIdle = FOXHUNT_BEACON_IDLE_DEF;
|
||||
beaconTx = FOXHUNT_BEACON_TX_DEF;
|
||||
foxFox = FOXHUNT_FOX_CALL;
|
||||
beaconCarrierKeyed = false; // TONE (F2A) by default
|
||||
beaconPhaseTx = false;
|
||||
FOXHUNT_LoadConfig();
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ typedef struct app_api {
|
||||
void (*tx_tone)(uint16_t hz);/* BK4819_TransmitTone prime (MCW tone) */
|
||||
void (*tx_mute)(bool on); /* key the tone on(false)/off(true) via TxMute */
|
||||
void (*tx_end)(void); /* PA off + RADIO_SetupRegisters (back to RX) */
|
||||
void (*tx_carrier)(bool on); /* gate the PA on/off (beacon CARR carrier keying) */
|
||||
uint32_t (*tx_freq)(void); /* current TX VFO frequency (x10 Hz) */
|
||||
void (*boot_callsign)(char *buf, uint8_t len); /* sanitised callsign from the boot message */
|
||||
void (*print_string)(const char *s, uint8_t start, uint8_t end,
|
||||
|
||||
@@ -850,6 +850,7 @@ static void app_tx_set_params(void) { RADIO_SetTxParameters(); }
|
||||
static void app_tx_tone(uint16_t hz) { BK4819_TransmitTone(false, hz); }
|
||||
static void app_tx_mute(bool on) { if (on) BK4819_EnterTxMute(); else BK4819_ExitTxMute(); }
|
||||
static void app_tx_end(void) { BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false); RADIO_SetupRegisters(true); }
|
||||
static void app_tx_carrier(bool on) { BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, on); }
|
||||
static uint32_t app_tx_freq(void) { return gTxVfo->pTX->Frequency; }
|
||||
static void app_boot_callsign(char *buf, uint8_t len)
|
||||
{
|
||||
@@ -1051,6 +1052,7 @@ static const app_api_t app_api = {
|
||||
.tx_tone = app_tx_tone,
|
||||
.tx_mute = app_tx_mute,
|
||||
.tx_end = app_tx_end,
|
||||
.tx_carrier = app_tx_carrier,
|
||||
.tx_freq = app_tx_freq,
|
||||
.boot_callsign = app_boot_callsign,
|
||||
.print_string = UI_PrintString,
|
||||
|
||||
@@ -17,12 +17,13 @@
|
||||
/*
|
||||
* Beacon (fox) — overlay app. Ported from App/app/foxhunt.c, beacon sub-mode only.
|
||||
* Turns the radio into a hidden ARDF transmitter: keys up on the TX VFO and repeats
|
||||
* a CW fox identifier (MOE..MO5 / MO / "<call> MOE") as MCW for the TX window, then
|
||||
* stays silent for the idle gap. The carrier stays up during the window; only the
|
||||
* tone is keyed per Morse element.
|
||||
* a CW fox identifier (MOE..MO5 / MO / "<call> MOE") for the TX window, then stays
|
||||
* silent for the idle gap. Two keying modes (key 4): TONE (default) keeps the carrier
|
||||
* up and keys only the tone (MCW / F2A); CARR keys the PA together with the tone, so
|
||||
* between elements the carrier itself is gone (carrier interruption, ARDF field pattern).
|
||||
*
|
||||
* Keys: 1 TX window · 2 idle gap · 3 fox id (F reverses) · MENU restart idle
|
||||
* long F keypad lock · EXIT quit.
|
||||
* Keys: 1 TX window · 2 idle gap · 3 fox id · 4 keying mode TONE/CARR (F reverses)
|
||||
* MENU restart idle · long F keypad lock · EXIT quit.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -64,7 +65,7 @@ static const uint8_t FONT_LOCK[9] = {0x7c,0x46,0x45,0x45,0x45,0x45,0x45,0x46,0x7
|
||||
|
||||
static const app_api_t *A;
|
||||
|
||||
static bool foxLocked, fArm, fLongDone, running, phaseTx;
|
||||
static bool foxLocked, fArm, fLongDone, running, phaseTx, carrier;
|
||||
static uint16_t fHoldMs;
|
||||
static uint8_t beaconIdle, idleLeft, idleTick, beaconTx, secShown, charsSent, foxFox;
|
||||
static uint16_t txMsLeft;
|
||||
@@ -95,6 +96,7 @@ static bool settingKey(uint8_t key,int8_t dir){
|
||||
if(dir>0){ if(++foxFox>=FOX_COUNT) foxFox=0; }
|
||||
else foxFox=foxFox?(uint8_t)(foxFox-1u):(uint8_t)(FOX_COUNT-1u);
|
||||
return true;
|
||||
case APP_KEY_4: carrier=!carrier; return true; /* TONE <-> CARR (dir moot) */
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -184,7 +186,8 @@ static void beaconDraw(bool txNow,uint8_t il){
|
||||
}
|
||||
char *o=put(str,"TX "); o=putu(o,beaconTx); o=put(o,"s"); *o='\0'; tag(str,4,5);
|
||||
o=put(str,"IDLE "); o=putu(o,beaconIdle); o=put(o,"s"); *o='\0'; tag(str,4,6);
|
||||
foxLabel(str); tag(str,(uint8_t)(125-slen(str)*4),5);
|
||||
foxLabel(str); tag(str,66,5);
|
||||
{ const char *m=carrier?"CARR":"TONE"; tag(m,(uint8_t)(125-slen(m)*4),5); }
|
||||
}
|
||||
static void blit(void){ A->blit_status(); A->blit_full(); }
|
||||
|
||||
@@ -218,15 +221,19 @@ static bool txDelay(uint16_t ms){
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* Keying: both modes key the tone; CARR gates the PA in lockstep so the carrier is truly
|
||||
* gone between elements (tone muted too, else it bleeds through residual PA leakage). */
|
||||
static void keyOn(void){ A->tx_mute(false); if(carrier) A->tx_carrier(true); }
|
||||
static void keyOff(void){ A->tx_mute(true); if(carrier) A->tx_carrier(false); }
|
||||
static bool morseChar(char c,uint8_t vis){
|
||||
uint8_t code=morseByte(c);
|
||||
if(code==0){ if(txDelay(MORSE_UNIT*4)) return true; updateProgress(vis); return false; }
|
||||
uint8_t bit=0x80; while(!(code&bit))bit>>=1;
|
||||
for(bit>>=1;bit;bit>>=1){
|
||||
uint16_t on=(code&bit)?(MORSE_UNIT*3):MORSE_UNIT;
|
||||
A->tx_mute(false);
|
||||
if(txDelay(on)){ A->tx_mute(true); return true; }
|
||||
A->tx_mute(true);
|
||||
keyOn();
|
||||
if(txDelay(on)){ keyOff(); return true; }
|
||||
keyOff();
|
||||
if(txDelay(MORSE_UNIT)) return true;
|
||||
}
|
||||
updateProgress(vis);
|
||||
@@ -235,7 +242,8 @@ static bool morseChar(char c,uint8_t vis){
|
||||
static void transmit(void){
|
||||
A->tx_set_params();
|
||||
A->tx_tone(TONE_HZ);
|
||||
A->tx_mute(true);
|
||||
A->tx_mute(true); /* open muted (both modes) */
|
||||
if(carrier) A->tx_carrier(false); /* CARR: carrier off until first element */
|
||||
txMsLeft=(uint16_t)beaconTx*1000u;
|
||||
bool stop=false;
|
||||
while(!stop && txMsLeft>0){
|
||||
@@ -270,14 +278,15 @@ static void tickDelay(void){ for(uint8_t i=0;i<TICK_MS/10;i++){ A->delay_ms(10);
|
||||
|
||||
/* ---- config (deferred) ---- */
|
||||
static void loadConfig(void){
|
||||
uint8_t c[4]; A->cfg_load(c,4);
|
||||
uint8_t c[5]; A->cfg_load(c,5);
|
||||
if(c[0]==CFG_MAGIC){
|
||||
if(c[1]>=IDLE_MIN&&c[1]<=IDLE_MAX&&(c[1]%IDLE_STEP)==0) beaconIdle=c[1];
|
||||
if(c[2]<FOX_COUNT) foxFox=c[2];
|
||||
if(c[3]>=TX_MIN&&c[3]<=TX_MAX&&(c[3]%TX_STEP)==0) beaconTx=c[3];
|
||||
if(c[4]<=1) carrier=c[4]; /* erased (0xFF) legacy config -> keep default */
|
||||
}
|
||||
}
|
||||
static void saveConfig(void){ uint8_t c[4]={CFG_MAGIC,beaconIdle,foxFox,beaconTx}; A->cfg_save(c,4); }
|
||||
static void saveConfig(void){ uint8_t c[5]={CFG_MAGIC,beaconIdle,foxFox,beaconTx,(uint8_t)carrier}; A->cfg_save(c,5); }
|
||||
|
||||
static void txDenied(void){
|
||||
chrome();
|
||||
@@ -289,7 +298,7 @@ static void txDenied(void){
|
||||
__attribute__((section(".text.entry"),used))
|
||||
void app_main(const app_api_t *api){
|
||||
A=api;
|
||||
foxLocked=fArm=fLongDone=false; fHoldMs=0;
|
||||
foxLocked=fArm=fLongDone=carrier=false; fHoldMs=0;
|
||||
beaconIdle=IDLE_DEF; beaconTx=TX_DEF; foxFox=FOX_CALL;
|
||||
prevKey=APP_KEY_INVALID;
|
||||
A->boot_callsign(foxCall,sizeof(foxCall));
|
||||
|
||||
@@ -51,10 +51,10 @@ static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
_MK_MAPPING(0x008000, 0x008000, 0x00886E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000) 0x008000 -> 0x00880E
|
||||
// List name * 4 Bytes 0x00880E -> 0x00886E
|
||||
|
||||
_MK_MAPPING(0x009000, 0x009000, 0x0090E7), // 14 VFO * 16 Bytes = 0x9000 -> 0x90E0 (the old
|
||||
_MK_MAPPING(0x009000, 0x009000, 0x0090E8), // 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 / Beacon settings * 7 Bytes 0x90E0 -> 0x90E7
|
||||
// Fox Hunt / Beacon settings * 8 Bytes 0x90E0 -> 0x90E8
|
||||
// (written directly by app/foxhunt.c; concatenated
|
||||
// here so aircopy clones them with the VFOs)
|
||||
|
||||
|
||||
Reference in new issue
Block a user