v3.14.1 — RadiodStream multi-interface multicast join
Fixed
Extends Rob Robinett's e3acb6a fix from multi_stream.py to stream.py so RadiodStream also joins the multicast group on every local IPv4 interface instead of letting the kernel pick one via INADDR_ANY.
Same root cause, same symptom, different consumer. A co-located radiod with ttl=0, or any multi-homed station where radiod emits on a non-default-route interface, would silently deliver zero packets to a RadiodStream consumer. Most visible to clients that use the per-stream API directly:
codar-sounder(wrapsRadiodStreamin itsRadiodIQSource)hf-timestd's legacy T6 path- Any future client consuming a single RTP stream
MultiStream-based clients (psk-recorder, wspr-recorder, hfdl-recorder, hf-timestd shared-mode) were already fixed in 3.14.x by Rob's commit.
Refactor
The helpers (iter_local_ipv4_interfaces, join_multicast_all_interfaces) move into a package-private ka9q/_multicast.py. Both multi_stream.py and stream.py import from there — one implementation, identical behaviour. A future change to the join semantics applies uniformly.
Not Fixed
rtp_recorder.RtpRecorder still has the single-interface INADDR_ANY join. Left as a separate change in case anyone depends on the exact wire behaviour of the legacy recorder.
Tests
8 new in tests/test_multicast_helpers.py:
- enumerator real-host smoke +
if_nameindexOSError handling + nonexistent-iface filtering - join helper: per-iface setsockopt call sequence + correct mreq bytes
- per-iface failure doesn't abort the loop
- empty enumeration → empty join list (no crash)
- both stream classes pull the helper from the shared module
142 offline unit tests pass.
Install
pip install ka9q-python>=3.14.1