[DRAFT] Add Hermes-Lite 2 Plus (AK4951 companion board) support - #1
Closed
randal007 wants to merge 1 commit into
Closed
[DRAFT] Add Hermes-Lite 2 Plus (AK4951 companion board) support#1randal007 wants to merge 1 commit into
randal007 wants to merge 1 commit into
Conversation
The HL2+ companion board adds an AK4951 stream codec to a Hermes-Lite 2:
microphone input with PTT, headphone and speaker outputs, and CW sidetone,
on gateware that fills the same Protocol-1 audio slots every other codec
board uses.
An HL2+ answers discovery exactly like a stock HL2 — no board ID, no
gateware marker, nothing to probe — so the operator declares it, and that
declaration promotes the board's capability fingerprint:
For(HermesLite2, variant, hl2PlusCodec: true)
=> caps with { HasOnboardCodec = true, HasMicBias = true }
Promoting the capability rather than special-casing each consumer is the
design. RadioSpeakerAudioSink, the Protocol-1 mic attach, ClampAudioSource
and the frontend already gate on HasOnboardCodec, so one lever lights all
of them, and almost no new audio plumbing was needed: ControlFrame already
writes the EP2 L/R slots and PacketParser already decodes the EP6 mic
slots. Every capability lookup that feeds a gate or a clamp must read the
promoted set — three separate faults during bring-up came from one that
did not, the worst being PushAudioFrontEnd, where ClampAudioSource sent
the operator's Radio Mic selection back to Host while every queryable
surface went on reporting RadioMic.
Config C3 bit 3 is the one hardware conflict. HL2+ gateware reads it as
"a codec is present"; mi0bot's HL2 fork reads the same bit as the Band
Volts PWM enable. The two are made mutually exclusive — declaring HL2+
force-disables Band Volts, arming Band Volts is refused while HL2+ is
declared, and hl2-options reports bandVoltsAvailable so the UI can
disable the control instead of watching a PUT be ignored.
CW sidetone is generated host-side and carried to the codec on the EP2
L/R slots while keyed, following deskHPSDR. The companion gateware does
not sound its own sidetone in practice, despite accepting internal_CW and
a non-zero sidetone level, so the host must supply it; the host's own
monitor tone is suppressed in that case so the operator hears one tone
from the radio rather than a second, late one from the PC. The keyer's
RF_delay now carries piHPSDR's value instead of zero — piHPSDR documents
it as working around a bug in the FPGA iambic keyer.
Also, board-agnostic and useful to any codec board: the radio-speaker
ring now primes before serving audio (its absence produced 5.3 seconds of
gaps on every start, heard as a run of pops) and corrects producer-vs-
packer rate drift a few samples at a time instead of in slices. Three
diagnostics endpoints report what the CW frames carry, the speaker ring's
latency and health, and every link in the radio-microphone chain — each
one located a fault that reading the code had not.
Off by default: a radio whose operator has not declared a companion board
keeps the stock HL2 fingerprint and byte-identical wire behaviour.
Owner
Author
|
Superseded — opening this upstream now that the HL2+ has been tested on air. History squashed and the body rewritten with what testing actually established. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Support for the Hermes-Lite 2 Plus — an HL2 carrying the AK4951 companion board, which adds a stream codec with microphone input, headphone and speaker outputs, and CW sidetone played by the radio itself.
The board cannot be detected, so the operator declares it
An HL2+ answers discovery exactly like a stock HL2. There is no board ID, no gateware version, nothing to probe. So this follows the existing precedent for an operator-selected hardware fact —
OrionMkIIVariant— and lets the declaration promote the capability fingerprint:Promoting the capability rather than special-casing each consumer is the whole design.
RadioSpeakerAudioSink, the Protocol-1 mic attach inDspPipelineService,ClampAudioSourceand the frontend already gate onHasOnboardCodec, so one lever lights all of them. Almost no new audio plumbing was required:ControlFramealready writes the EP2 L/R slots when a source is plumbed, andPacketParser.ExtractMicSamplesalready decodes the EP6 mic slots. They were simply never switched on for this board.HasRadioLineInandHasBalancedXlrstay false — the companion board has neither jack, so those options stay hidden.Two blanket
board == HermesLite2exclusions inRadioSpeakerAudioSinkare replaced by that capability check. They were shorthand for "HL2 has no codec", which stops being true with the companion fitted.Config C3 bit 3 — the one real conflict
The HL2+ gateware reads Config-frame C3 bit 3 as "an audio codec is present". mi0bot's HL2 fork reads the same bit as the Band Volts PWM enable, which is what Zeus currently drives it as:
deskHPSDR sets the identical bit for the codec via its
LT2208_DITHER_ONalias. One bit, two incompatible gateware meanings, so the two features cannot both be armed:SetHl2BandVoltsreturns the effective value, which will befalse);/api/radio/hl2-optionsreportsbandVoltsAvailable, so the UI can disable the control rather than let a PUT be silently ignored.Without this the two settings would quietly mean whatever the loaded gateware believes, which is the worst possible failure mode: no error, just a feature that does something else.
CW sidetone
Modelled directly on deskHPSDR, whose CW sample path writes the host-generated sidetone into both EP2 audio slots alongside the CW I/Q. This is the point of the companion board for CW work — the sidetone reaches the operator from the radio instead of around the host audio loop.
Zeus already generates that sidetone:
CwSidetoneSourcemixes it into the RX audio bus, and the MOX fade silences the band-RX contribution while keying, so during CW transmit that bus holds sidetone and nothing else. Two things kept it off the wire:ControlFramefilled the L/R slots only while MOX was clear. It now fills them under MOX whenCodecAudioWhileMoxis set — written before the I/Q early-returns, so sidetone survives a null TX source orDriveLevel == 0. An operator keying with drive at zero still expects to hear themselves.RadioSpeakerAudioSinkdropped every frame while keyed, on the previously correct grounds that the L/R slots carried no audio during TX. It now keeps feeding in CW only, and still drops in every other mode, where those frames are TX monitor audio that does not belong in the radio's speaker.The ring is drained on both T/R edges. deskHPSDR drains its TX ring on the RX→TX transition "for minimum CW side tone latency"; the same drain also stops a pre-key RX tail replaying into the codec after unkey.
Scope and compatibility
Off by default. A radio whose operator has not declared a companion board keeps the stock HL2 fingerprint and byte-identical wire behaviour, so a stock HL2 is unaffected in every path touched here.
CodecAudioWhileMoxis set only for the HL2+. Other codec boards keep their existing silence-while-keying behaviour — the same change would very likely suit them, but I have no ANAN hardware to confirm it on and did not want to alter transmit-time audio for boards I cannot test.Hl2Pluson theHl2OptionsSetRequestis nullable, matchingIoBoard: a client that PUTs only{bandVolts}must not silently un-declare someone's companion board.Testing
Built clean; the settings, exclusivity and API surface are verified against a live engine:
On-air audio testing is pending — see the note below; I will update this section with results before this is ready to merge.
Notes for reviewers
The HL2+ gateware is not open source. That is why deskHPSDR's author declines to support the board, and it is a reasonable reason for this project to decline it too. Nothing here depends on that gateware being open — the wire side is ordinary Protocol-1 audio slots plus one Config bit — but the feature is only exercisable by operators running it, so it is fair to weigh that before taking this on.
Worth knowing: the AK4951's own internal DSP (ALC, 5-band EQ, mic analog gain) is configured out-of-band by JI1UDD's ak4951_Controller, which reaches the codec over the HL2's I2C-1 bus at address 0x12 (
C0=0x78, C1=0x06, C2=0x92). Zeus does not need to duplicate any of that for mic and phones to work, and this PR does not try to.Authorship disclosure: this patch was written by Claude (Anthropic's AI) working with the station owner, who owns the HL2+ hardware, loaded the companion gateware, and performed the on-air testing. The reasoning and provenance comments are the AI's; the hardware verification is the operator's.
🤖 Generated with Claude Code