Air band AM receive and broadcast FM receive
Status: written, not yet tested on a radio (2026-10-22). What the radios RadioFoundry supports can do on the aeronautical VHF band (AM, 108-137 MHz) and FM broadcast (65-108 MHz), and how RadioFoundry does it on any radio that has the hardware. Both are receive only and need no menu: the frequency decides. An FM channel or the VFO in the air band is received in AM; one in the broadcast band plays on the broadcast receiver.
1. Which radios
A radio has air band receive when its build has CONFIG_AM_RX and its board sets CAP_AM_RX and rx_am, and
broadcast receive when it has CONFIG_BCAST_FM and sets CAP_BCAST_FM and board.bcast. Which radios do is in
feature-matrix.md (am_rx, bcast_fm), generated from the boards. A radio's own facts (pins,
audio path, open questions, bring-up checks) are on its hardware and bring-up pages.
2. Chips
| Chip | Air band AM | Notes |
|---|---|---|
| FD6818 | yes (inferred) | BK4819 class: the AF output type in 0x47 bits 11:8, type 7 = AM on the BK4819 (Apache-2.0 UV-K5 firmware). Unconfirmed on the FD6818 until a radio's bring-up |
| BK4819 | yes | the same register, documented |
| AT1846S | no | FM and the DMR baseband modes only (datasheet, our driver) |
| Broadcast tuner | Driver |
|---|---|
| RDA5807M | drivers/bcast/rda5807m (4.2) |
| RDA5802N | none checked yet; the RDA5807M driver may serve it after a bring-up check |
3. Air band AM
3.1 Representation: derived from the frequency, codeplug unchanged
The open-firmware CPS codeplug has no AM modulation, and RadioFoundry keeps the standard format as long as possible. So AM is not
stored: an FM channel or the VFO tuned inside 108-137 MHz is received in AM on a radio that can. This is how
most radios with an air band behave, it needs no CPS change, and qdmr can program air channels today (as FM
channels in that range). The band is an ITU allocation, the same everywhere, so the range is a core constant
(RADIO_AM_LO_HZ / RADIO_AM_HI_HZ in core/radio/radio_am.h), not a board value. A DMR channel in the range stays
DMR (and can't transmit there). Against a stored AM flag: it would need the native codeplug first, and an AM
channel outside the air band has no use on these radios.
3.2 Behavior
| Item | Behavior |
|---|---|
| When | CONFIG_AM_RX, the board has CAP_AM_RX and board.radio->rx_am, the channel is FM and its RX frequency is in [108, 137) MHz |
| Tuning | tune() calls rx_am(hz, bw) instead of rx(); if the board refuses, FM as before. view.am says AM is on |
| Transmit | refused with TXDENY_AM whenever view.am, and for any TX frequency in the air band on an AM radio (split channels, DMR channels, the VFO, the data service, the hotspot's frequencies) |
| Squelch | on the RSSI: radio_am_noise() maps dBm onto the FM noise scale (-18 dBm = 0, 1 dB per step), so the levels and hysteresis are the FM ones: level 1 opens above -82 dBm, 5 above -70, 9 above -58. The boards' AM RSSI isn't calibrated: empty air band channels read -76 to -86 dBm on a radio (2026-10-22), so level 5 sits about 8 dB above that. view.noise holds that value in AM. Scan uses the same value |
| Sub-audio | ignored in AM (tone_needed() is false, the tone counts as heard) |
| Steps | radio_req_vfo_step_size() (RREQ_VFO_STEP_SIZE, stored in settings_t.vfo_step_hz; 0 is auto, the default: 25 kHz at width 25K or in AM, else RADIO_VFO_STEP_HZ, recomputed on every view change, with view.vfo_step_auto set; Settings › Talk › VFO step); 8333 means the exact 8.33 kHz grid (n x 25/3 kHz, radio_am_step() snaps and steps without drift); 25 kHz is a plain step |
| Band table | the board lists the air band as an RX-only band ({108000000, 136000000, false} after the VHF TX band) |
| Screen | "AM" in place of "FM" for the mode on Home, in the strip, on the mode tile and in the channel list. Home's chips are "Air Band AM" and the squelch level (no tone, width or power). PTT shows "Air band: receive only" |
3.3 Board contract (hal_radio.h)
rx_am(hz, bw) tunes and receives AM; the board leaves AM at the next rx() or set_mode(). status() gives an
RSSI in dBm (noise isn't used in AM) and tone_ok = true. tx() is never called while AM is on. NULL = no AM.
On an FD6818 rx_am is rx plus the AF output type AM (fd6818_set_am, 0x47 = 0x0740; inferred, section 2);
rx() and set_mode() write FM back.
4. Broadcast FM
4.1 A second receiver, chosen by the frequency
Broadcast FM is wideband FM (about 200 kHz) on a separate chip, so it is a separate receiver (hal_bcast.h,
board.bcast, CAP_BCAST_FM), not a channel mode. As with the air band, nothing is stored: an FM channel or the
VFO tuned inside the broadcast receiver's range (board.bcast->lo_khz to hi_khz, 65-108 MHz on the RDA5807M) plays
on it. The helpers are in core/radio/radio_bcast.h. The channel receiver stays on its last frequency, muted.
4.2 RDA5807M (drivers/bcast/rda5807m)
From the public datasheet (RDA Microelectronics, rev 1.1; register summary checked against done.land).
| Item | Value |
|---|---|
| Bus | I2C, random access at 0x11: write reg, hi, lo; read reg, repeated START, hi, lo. Chip ID 0x58 in 0x00 bits 15:8 |
| 0x02 | DHIZ b15, DMUTE b14 (1 = audio), MONO, BASS, RCLK non-calibrate b11, RCLK direct b10, SEEKUP b9, SEEK b8, SKMODE b7 (0 = wrap), CLK_MODE b6:4, RDS_EN b3, NEW_METHOD b2, SOFT_RESET b1, ENABLE b0 |
| 0x03 | CHAN b15:6, TUNE b4, BAND b3:2 (2 = 76-108, 3 = 65-76 with 0x07 b9), SPACE b1:0 (2 = 50 kHz) |
| 0x04 / 0x05 / 0x07 | de-emphasis 50 us b11 / INT_MODE, SEEKTH b11:8, LNA port b7:6, VOLUME b3:0 / soft blend threshold, 65M mode b9, soft blend b1 |
| 0x0A / 0x0B | STC b14, SF b13, ST b10, READCHAN b9:0 / RSSI b15:9, FM_TRUE b8, FM_READY b7 |
The driver tunes 65-76 and 76-108 MHz on a 50 kHz grid, keeps shadows so TUNE and SEEK are never written twice, and
follows the chip's STC. The board supplies the bus; drivers/bus/i2c_gpio is a bit-banged master (open-drain SDA,
push-pull SCL, no clock stretching).
4.3 Behavior
| Item | Behavior |
|---|---|
| When | CONFIG_BCAST_FM, the board has CAP_BCAST_FM and board.bcast, the channel is FM and its RX frequency is in the receiver's range. view.bcast says the broadcast receiver plays |
| Tuning | the receiver is powered and tuned to the nearest point of its grid (radio_bcast_khz). Leaving the range powers it off. If the chip doesn't answer, view.bcast_fault is set and the radio stays idle |
| Transmit | refused with TXDENY_BCAST whenever view.bcast, and for any TX frequency in the range on a radio with the receiver (split channels, DMR channels, the VFO, the hotspot's frequencies) |
| Squelch | on the receiver's RSSI (about dBuV): noise = 97 - RSSI on the FM noise scale, so level 5 opens above 45 dBuV, 1 above 33, 9 above 57. Set from a receiver without an antenna (2026-10-22): empty frequencies read 22 to 37, stations 50 and up. The signal meter shows RSSI - 107 dBm. Only a scan uses it (to stop on stations): otherwise the state stays RX, the LED stays on, the screen shows the level and the audio plays whatever the signal (bcast_listening()) |
| Sub-audio | ignored (tone_needed() is false) |
| Volume | the radio's own volume where it has one (radio_volume_soft), mapped onto the receiver's 0-15; a fixed 12 where a knob after the amplifier sets the level |
| Steps | the VFO's automatic step is the broadcast step setting (4.4). An explicit VFO step (Settings › Talk › VFO step) is used as set |
| Power | battery save is off while the receiver plays; hotspot mode powers it off |
| Screen | Home's chips are "Broadcast FM" alone (no tone, width, power or squelch level). The mode word stays "FM". PTT shows "Broadcast: receive only" |
| APRS | a beacon's carrier sense reads the channel receiver on the APRS frequency, as on any channel |
4.4 Broadcast step
Settings › Talk & listen › FM broadcast step, radio_settings_t.bcast_step, stored in settings_t.rs_bcast_step
(page 1; bcast_step + 1, 0 = the default). The row shows only on a radio with the receiver (UI_FEAT_BCAST).
| Setting | VFO knob and Up / Down |
|---|---|
| 200 kHz (default) | to the next odd tenth of a MHz: 88.1, 88.3 ... 107.9, the broadcast channels of the Americas. From an even tenth or a point between, the first step lands on the next odd tenth in that direction (90.2 up is 90.3, down is 90.1) |
| 100 kHz | every 100 kHz, from wherever the VFO is |
Either way any frequency can be typed in: 90.2 is tuned and heard as entered.
4.5 Board glue
The HR_C7000 boards share one composition, platform/hrc7000/bcast_hrc.c (hrc_bcast_init): the RDA5807M driver on
a bit-banged I2C bus (open-drain SDA) on the board's two pins, an optional enable pin and level, and the board's
audio path callback. A board fills in hrc_bcast_cfg_t and sets board.bcast = &rda5807m_hal; its pins and audio path
are on its hardware page.
The board decides how the broadcast reaches the speaker. The core keeps the channel receiver's audio off while the
broadcast plays, so the two never ask for the speaker at once (hal_bcast.h audio).
5. Build switches
| Switch | Default | Covers |
|---|---|---|
CONFIG_AM_RX |
on with FM; off where the receiver has no AM | radio_am.c in the radio core, RREQ_VFO_STEP_SIZE; needs FM |
CONFIG_BCAST_FM |
on; off where there is no broadcast receiver | radio_bcast.c in the radio core, the board's tuner glue, the step setting |
The board must also set the capability bits; without them the code does nothing at run time. Per-board defaults: feature-matrix.md.
6. Tests
| Test | Covers |
|---|---|
tests/host/radioListenBandsTest.c |
AM and broadcast channels and VFO, TX refusal, squelch, the steps, a dead receiver |
tests/host/fd6818Test.c |
the AF output word for AM and back to FM |
tests/host/uiFlowTest.c |
the FM broadcast step row on a radio with the receiver |
tests/hil/test_listen_bands.py |
the VFO at 118.0 MHz gives am=1; at 100.1 MHz bcast=1 and bcast_fault=0. Nothing keys |