diff --git a/changelog.json b/changelog.json index 1661df9..b9c527e 100644 --- a/changelog.json +++ b/changelog.json @@ -6,13 +6,15 @@ "Elecraft console: the S-meter is calibrated against a real K3 — S9 and the +dB readings now match the radio’s own display (they read about two S-units low).", "Elecraft console: an SWR spike (the K3’s own blip at the end of an FT8 frame) no longer fades over twelve seconds — the peak shows briefly, then the meter returns straight to the live reading.", "Icom: the IC-7760 joins the model list (CI-V address B2h).", - "Icom console: the IC-7760 gets its real attenuator steps — 6/12/18 dB." + "Icom console: the IC-7760 gets its real attenuator steps — 6/12/18 dB.", + "Icom network audio: the RX stream is decoded correctly — mono is requested and the payload offset was confirmed on a real IC-7760, curing the garbled, clicking audio." ], "fr": [ "Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).", "Console Elecraft : un pic de ROS (le sursaut du K3 en fin de trame FT8) ne s’estompe plus pendant douze secondes — le pic s’affiche brièvement, puis le ros-mètre revient directement à la valeur réelle.", "Icom : l’IC-7760 rejoint la liste des modèles (adresse CI-V B2h).", - "Console Icom : l’IC-7760 reçoit ses vrais crans d’atténuateur — 6/12/18 dB." + "Console Icom : l’IC-7760 reçoit ses vrais crans d’atténuateur — 6/12/18 dB.", + "Audio réseau Icom : le flux RX est décodé correctement — le mono est demandé et l’offset du payload a été confirmé sur un vrai IC-7760, ce qui guérit le son inaudible et les clics." ] }, { diff --git a/internal/cat/icomaudio.go b/internal/cat/icomaudio.go index 678166a..41d4f2e 100644 --- a/internal/cat/icomaudio.go +++ b/internal/cat/icomaudio.go @@ -28,11 +28,13 @@ import ( "time" ) -// icaAudioOffset is where the PCM payload begins inside an audio data packet -// (wfview audio_packet: 16-byte common header + ident@0x10 + datalen@0x12 + -// sendseq@0x14 → audio@0x16). Isolated as a const so a capture-confirmed change -// is a one-line edit. -const icaAudioOffset = 0x16 +// icaAudioOffset is where the PCM payload begins inside an audio data packet. +// CONFIRMED on a real IC-7760 (2026-08-29): 16-byte common header, ident@0x10, +// send seq (BE) @0x12, payload length (BE uint32) @0x14 — 0x500 observed on +// every packet — and the PCM starts at 0x18. The 0x16 first guessed from +// wfview's struct swallowed two header bytes into the audio, one broken sample +// per packet: a 50 Hz click track under everything. +const icaAudioOffset = 0x18 // icaDumpFirst is how many initial audio packets to hex-dump to the debug log for // offset verification. After the layout is confirmed on a real rig this can go to diff --git a/internal/cat/icomnet.go b/internal/cat/icomnet.go index 48901eb..3d1b552 100644 --- a/internal/cat/icomnet.go +++ b/internal/cat/icomnet.go @@ -849,8 +849,13 @@ func icnConnInfo(seq, innerSeq, tokReq uint16, sentid, rcvdid, token uint32, use copy(b[0x60:0x70], icnPasscode(user)) b[0x70] = rxEnable // rxenable: 1 opens the 50003 RX audio stream, 0 = CI-V only b[0x71] = 0x00 // txenable (Phase 5) - b[0x72] = 0x10 // rxcodec - b[0x73] = 0x04 // txcodec + // rxcodec 0x02 = LPCM, ONE channel, 16-bit. 0x10 (two channels) was asked + // for at first, and a real IC-7760 duly sent interleaved stereo — which the + // 16 kHz mono playback path rendered as garble at double speed. Mono is what + // the monitor plays and half the bandwidth; the second channel of an RX + // stream carries nothing a logger wants. + b[0x72] = 0x02 // rxcodec + b[0x73] = 0x04 // txcodec icnBE.PutUint32(b[0x74:], 16000) icnBE.PutUint32(b[0x78:], 8000) icnBE.PutUint32(b[0x7c:], uint32(civPort))