From 2b33f3ebdd5b62d4540110c89a0d0c7452d1b90c Mon Sep 17 00:00:00 2001 From: Armel FAUVEAU Date: Wed, 30 Sep 2026 05:25:34 +0200 Subject: [PATCH] Mute APRS RX speaker by default, toggle it with key 1 --- App/apps/aprsrx/README.md | 15 ++++++++------- App/apps/aprsrx/aprsrx_app.c | 32 ++++++++++++++++---------------- App/apps/aprsrx/build.sh | 2 +- App/apps/aprsrx/gen_assets.py | 3 ++- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/App/apps/aprsrx/README.md b/App/apps/aprsrx/README.md index 223699ae..6f1b0d26 100644 --- a/App/apps/aprsrx/README.md +++ b/App/apps/aprsrx/README.md @@ -11,7 +11,7 @@ Status: | Audio path an app can sample | **Done** by EPIRB 406: RX audio on PA4, ADC channel 4 at 9.6 kHz | | Integer demodulator, modelled on synthetic audio (`test/model_rx.py`) | **Done** (see below) | | Flipper Zero test transmitter (`test/flipper_aprs.py`) | **Done**, files in `test/flipper/` | -| Radio app (`aprsrx_app.c`) | **v0.1 receives the Flipper frames on the radio**; v0.3 decodes Mic-E (on air: F5RAV via F1PRY-14); v0.4 decodes continuously with 3 slicers (on air: F1PRY-14 via F5KTR-3); v0.5 shows standard positions, the frequency bottom right as Beacon, drops `pp` and the UP/DOWN/5 tuning offset to fit 4 KiB (builds: 3976 B, 120 B free; radio test pending) | +| Radio app (`aprsrx_app.c`) | **v0.1 receives the Flipper frames on the radio**; v0.3 decodes Mic-E (on air: F5RAV via F1PRY-14); v0.4 decodes continuously with 3 slicers (on air: F1PRY-14 via F5KTR-3); v0.5 shows standard positions, the frequency bottom right as Beacon, drops `pp` and the UP/DOWN/5 tuning offset to fit 4 KiB (3976 B); main-screen frequency, WAIT capsule (4068 B); v0.6: speaker off, key 1 toggles it, per-slicer counters dropped (not built yet) | | Bench test with the Flipper on 433.650 MHz | **Done** (2026-09-30, v0.1: `_long` 10/10 in STD; `_badfcs` not shown) | | Real station: FT3D beacon on 144.800 MHz | **Done** (2026-09-30, v0.1: Mic-E frame `>TXUPX9` received, FCS good, = 48°50.89' N 2°16.25' E) | | Mic-E decoding (`test/mice.py`: spec encoder vs the app's decoder) | **Done** in the model: 6 cases + the FT3D frame | @@ -21,8 +21,9 @@ Status: 1. Set the VFO to the APRS frequency, **FM**: 144.800 MHz, or 433.650 MHz for the Flipper test files. 2. Launch **APRS RX**. -3. Lower the volume: the RX audio must stay on (it is what reaches PA4), so the - speaker plays the channel. +3. The speaker is off (v0.6): the decoder does not need it, only the BK4829 AF + output (tested on the radio: PA4 joins the audio before the amplifier). Key 1 + turns it on to listen to the channel, as FoxHunt's audio key. Since v0.4 the app decodes **continuously**, as a TNC does: no RSSI trigger, the FCS and a UI-frame check (control 0x03, PID 0xF0) sort frames from noise. @@ -38,17 +39,18 @@ The screen is redrawn after a new frame, a key, or every 5 s. | Screen | Content | |---|---| -| Status bar | `APRS RX` title, then a `WAIT` capsule until the first frame | +| Status bar | `APRS RX` title, then a `WAIT` capsule until the first frame, and the speaker icon while the speaker is on | | Line 0 | Source call and frame number, e.g. `F4HWN-7 #3` | | Tiny row y=8 | `>DEST,DIGI*,...` (as many path entries as fit on one row) | | Tiny rows y=13/19/25 | Info field, 3 × 32 characters (non-ASCII shown as `.`); for Mic-E (most Yaesu/Kenwood beacons, v0.3): position `48 50.89N 002 16.25E`, then speed km/h, course, symbol and message type (`Off Duty`, `En Route`...), then altitude and comment without the device markers; for an uncompressed position (`!` `=` `/` `@`, v0.5): position, then the timestamp if any, the symbol (table + code) and the comment over two rows. Compressed positions stay raw text | | Lines 4-5 | Under a dotted separator (y=30), the frequency drawn as the main screen draws it (big digits up to the kHz, the last two in the small font), centred; same code as APRS TX | -| Bottom row y=49 | `ok 12 -89dBm sl 5/12/7`: frames received, RSSI at the end of the last frame, frames per slicer (space-favouring / neutral / mark-favouring; a frame found by several slicers counts in each, but once in `ok`) | +| Bottom row y=49 | `ok 12 -89dBm`: frames received, RSSI at the end of the last frame. (v0.4-v0.5 also showed frames per slicer, `sl a/b/c`: on air the outer slicers found frames too; dropped in v0.6 to fit the speaker key) | Keys (UV-K5 and UV-K1): | Key | Action | |---|---| +| 1 | Speaker on/off (off at launch); FoxHunt's speaker icon in the status bar while on | | MENU | Clear the last frame and the counters | | EXIT | Quit | @@ -152,5 +154,4 @@ the standard position display against real frames; 0.065 LSB/Hz), **measured 734-778** with the Flipper and **916** with a real station (F1PRY-14): ~20 % of full scale, plenty of headroom, and the per-tone AGC makes the absolute level irrelevant. -- The per-slicer counters show which decision threshold the stations need: if - one outer slicer does most of the work, the twist of the path is off-centre. + diff --git a/App/apps/aprsrx/aprsrx_app.c b/App/apps/aprsrx/aprsrx_app.c index dda33871..10d583c5 100644 --- a/App/apps/aprsrx/aprsrx_app.c +++ b/App/apps/aprsrx/aprsrx_app.c @@ -33,8 +33,8 @@ * are served every 50 ms only while no slicer is inside a preamble or a * plausible frame; the redraw follows a new frame, a key, or every 5 s. * - * Keys (UV-K5 and UV-K1): MENU clear · EXIT quit. The speaker plays the - * channel: lower the volume. + * Keys (UV-K5 and UV-K1): 1 speaker on/off (off at launch: the decoder does + * not need it) · MENU clear · EXIT quit. * The loader re-runs RADIO_SetupRegisters on exit; the app restores the ADC, * PA4, the DAC and its clock itself. */ @@ -128,11 +128,10 @@ static struct { uint32_t tPrev, tCyc; /* SysTick cycle counter */ uint32_t tFrame, tDraw; /* ticks_ms of the last frame, last redraw */ uint32_t savedSqr3, savedSmpr3, savedModer, savedDac, savedRcc, savedDhr; - uint8_t seq, prevKey, redraw; + uint8_t seq, prevKey, redraw, spk; /* spk: speaker amplifier on (key 1) */ bool running; int16_t rssi; /* RSSI at the end of the last frame */ uint16_t flen, nOk; /* frame length, frames */ - uint16_t good[NSL]; /* frames per slicer (duplicates included) */ } g; static char str[34]; @@ -164,6 +163,7 @@ static char *puti(char *o,int32_t v){ static void tiny(uint8_t y,char *end){ *end='\0'; g.A->print_tiny(str,0,y,false,true); } #define WAIT_CAPS_X 40u /* "WAIT" capsule in the status bar, after the title */ +#define SPK_X 62u /* speaker icon (FoxHunt's), after the capsule */ /* Tiny rows under the source line: the path (capitals and digits: no * descenders), then 3 info rows 6 px apart (the 3x5 font is 5 px, 6 with @@ -334,12 +334,11 @@ static bool slice(sl_t *m,int32_t a,int32_t b){ return false; } -/* ---- a good frame from slicer k: shown unless another slicer just gave it ---- */ -static void accept(sl_t *m,uint8_t k){ +/* ---- a good frame from a slicer: shown unless another slicer just gave it ---- */ +static void accept(sl_t *m){ const app_api_t *A=g.A; uint16_t n=m->n; uint32_t t=A->ticks_ms(); - if(g.good[k]buf[n-1] && g.frm[n-2]==m->buf[n-2] && t-g.tFramebuf,n); @@ -484,6 +483,7 @@ static void draw(void){ A->status_clear(); A->print_inverse(s+T_TITLE,2,0,true,true,(uint8_t)(2u+T_TITLE_CHARS*4u)); A->draw_battery(); + if(g.spk) A->asset_read(BMP_SPEAKER,A->status_line+SPK_X,BMP_SPEAKER_LEN); if(!g.flen) /* no frame yet: a capsule, as APRS TX's TRANSMIT */ A->print_inverse(s+T_WAIT,WAIT_CAPS_X,0,true,true,(uint8_t)(WAIT_CAPS_X+T_WAIT_CHARS*4u)); @@ -517,11 +517,9 @@ static void draw(void){ for(uint8_t x=0;x<128u;x+=2u) A->fb[3][x]|=0x40u; drawFreq(g.vfoFreq); - /* bottom line: "ok 12 -89dBm sl 5/12/7": frames, RSSI of the last one, - * frames per slicer (<= 31 characters) */ + /* bottom line: "ok 12 -89dBm": frames, RSSI of the last one */ o=put(str,s+T_OK); o=puti(o,g.nOk); *o++=' '; *o++=' '; o=puti(o,g.rssi); o=put(o,s+T_DBM); - for(uint8_t k=0;kaudio_path(g.spk); } /* speaker, as FoxHunt's audio */ } /* ---- keys and screen, between frames ---- */ @@ -587,7 +583,7 @@ static void listen(void){ front(&d,adcRead(),&a,&b); bool bz=false; for(uint8_t k=0;kbacklight_on(); g.vfoFreq=api->rx_freq(); - api->audio_path(true); + /* Speaker amplifier (PA8) off: the decoder does not need it (tested on the + * radio, 2026-09-30): PA4, the voice-prompt DAC pin, joins the audio before + * the amplifier; only the BK4829 AF output must be on. Key 1 turns the + * speaker on to listen to the channel. */ + api->audio_path(false); api->set_af(APP_AF_FM); api->delay_ms(50); diff --git a/App/apps/aprsrx/build.sh b/App/apps/aprsrx/build.sh index 9cbb3b30..46fc46f7 100755 --- a/App/apps/aprsrx/build.sh +++ b/App/apps/aprsrx/build.sh @@ -8,7 +8,7 @@ set -euo pipefail APP="$(basename "$PWD")" # breakout, foxhunt, beacon, fm, ... APP_NAME="APRS RX" # <-- the only per-app line -APP_VER="0.5" +APP_VER="0.6" APP_API_MIN=2 APP_VMA=${APP_VMA:-0x20000280} # pinned overlay VMA (Core/py32f071xb.ld) OUT="${APP_NAME// /}" # blob basename ("Broadcast FM" -> BroadcastFM) diff --git a/App/apps/aprsrx/gen_assets.py b/App/apps/aprsrx/gen_assets.py index 89d6ffa1..6b5fc663 100755 --- a/App/apps/aprsrx/gen_assets.py +++ b/App/apps/aprsrx/gen_assets.py @@ -23,7 +23,7 @@ WAIT_CAPS = "WAIT" # status-bar capsule until a frame UI = [ ("T_TITLE", TITLE), ("T_WAIT", WAIT_CAPS), - ("T_DBM", "dBm sl "), + ("T_DBM", "dBm"), ("T_OK", "ok "), ("T_KMH", "km/h "), ("T_CUSTOM", "Custom-"), @@ -47,6 +47,7 @@ a.const("UI_SIZE", ui_size) # the UI block read by draw() a.const("T_TITLE_CHARS", len(TITLE)) a.const("T_WAIT_CHARS", len(WAIT_CAPS)) a.table("T_MSG", MIC_MSG) # Mic-E standard messages +a.u8("BMP_SPEAKER", [0x1c,0x1c,0x3e,0x7f,0x00,0x22,0x1c,0x41,0x22,0x1c]) # FoxHunt's a.i8("COS1200", cos_table(1200)) # 8 entries a.i8("COS2200", cos_table(2200)) # 48 entries a.main()