ka9q-python 3.4.2__tar.gz → 3.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. ka9q_python-3.7.0/CHANGELOG.md +628 -0
  2. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/MANIFEST.in +11 -6
  3. {ka9q_python-3.4.2/ka9q_python.egg-info → ka9q_python-3.7.0}/PKG-INFO +59 -2
  4. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/README.md +58 -1
  5. ka9q_python-3.7.0/RELEASE_NOTES_v3.5.0.md +75 -0
  6. ka9q_python-3.7.0/docs/API_REFERENCE.md +1438 -0
  7. ka9q_python-3.7.0/docs/ARCHITECTURE.md +534 -0
  8. ka9q_python-3.7.0/docs/CHANGELOG.md +354 -0
  9. ka9q_python-3.7.0/docs/CROSS_PLATFORM_SUPPORT.md +251 -0
  10. ka9q_python-3.7.0/docs/DISTRIBUTION_RECOMMENDATION.md +343 -0
  11. ka9q_python-3.7.0/docs/GETTING_STARTED.md +249 -0
  12. ka9q_python-3.7.0/docs/INSTALLATION.md +254 -0
  13. ka9q_python-3.7.0/docs/MULTI_HOMED_QUICK_REF.md +305 -0
  14. ka9q_python-3.7.0/docs/NATIVE_DISCOVERY.md +276 -0
  15. ka9q_python-3.7.0/docs/PYPI_PUBLICATION_GUIDE.md +404 -0
  16. ka9q_python-3.7.0/docs/QUICK_REFERENCE.md +233 -0
  17. ka9q_python-3.7.0/docs/RTP_RECORDER_PASS_ALL_PACKETS.md +512 -0
  18. ka9q_python-3.7.0/docs/RTP_TIMING_IMPLEMENTATION.md +439 -0
  19. ka9q_python-3.7.0/docs/RTP_TIMING_SUPPORT.md +173 -0
  20. ka9q_python-3.7.0/docs/SECURITY.md +876 -0
  21. ka9q_python-3.7.0/docs/SSRC_COLLISION_PREVENTION.md +1000 -0
  22. ka9q_python-3.7.0/docs/TESTING_GUIDE.md +347 -0
  23. ka9q_python-3.7.0/docs/TESTING_SUMMARY.md +133 -0
  24. ka9q_python-3.7.0/docs/TEST_RESULTS.md +262 -0
  25. ka9q_python-3.7.0/docs/WEB_UI_ENHANCEMENT_GUIDE.md +412 -0
  26. ka9q_python-3.7.0/docs/WEB_UI_ENHANCEMENT_IMPLEMENTED.md +184 -0
  27. ka9q_python-3.7.0/docs/WEB_UI_ESCAPE_SEQUENCE_FIX.md +78 -0
  28. ka9q_python-3.7.0/docs/WEB_UI_FUNCTIONALITY_REVIEW.md +355 -0
  29. ka9q_python-3.7.0/docs/WEB_UI_IMPLEMENTATION_STATUS.md +356 -0
  30. ka9q_python-3.7.0/docs/WEB_UI_INTERACTIVE_COMPLETE.md +333 -0
  31. ka9q_python-3.7.0/docs/development/CHANGES_SUMMARY.md +304 -0
  32. ka9q_python-3.7.0/docs/development/CHANNEL_CLEANUP_ADDITION.md +136 -0
  33. ka9q_python-3.7.0/docs/development/CHANNEL_CLEANUP_COMPLETE.md +224 -0
  34. ka9q_python-3.7.0/docs/development/CHANNEL_TUNING_DIAGNOSTICS.md +495 -0
  35. ka9q_python-3.7.0/docs/development/CODE_REVIEW_RECOMMENDATIONS.md +456 -0
  36. ka9q_python-3.7.0/docs/development/CODE_REVIEW_SUMMARY.md +318 -0
  37. ka9q_python-3.7.0/docs/development/COMMIT_SUMMARY.md +212 -0
  38. ka9q_python-3.7.0/docs/development/CRITICAL_FIXES_CHECKLIST.md +295 -0
  39. ka9q_python-3.7.0/docs/development/FINAL_SUMMARY.md +469 -0
  40. ka9q_python-3.7.0/docs/development/FIXES_SUMMARY.md +89 -0
  41. ka9q_python-3.7.0/docs/development/GIT_STATUS_SUMMARY.md +109 -0
  42. ka9q_python-3.7.0/docs/development/IMPLEMENTATION_COMPLETE.md +441 -0
  43. ka9q_python-3.7.0/docs/development/IMPLEMENTATION_STATUS.md +444 -0
  44. ka9q_python-3.7.0/docs/development/IMPLEMENTATION_SUMMARY.md +70 -0
  45. ka9q_python-3.7.0/docs/development/IMPLEMENTATION_SUMMARY_v2.2.0.md +348 -0
  46. ka9q_python-3.7.0/docs/development/IMPROVEMENTS_IMPLEMENTED.md +454 -0
  47. ka9q_python-3.7.0/docs/development/MULTI_HOMED_ACTION_PLAN.md +304 -0
  48. ka9q_python-3.7.0/docs/development/MULTI_HOMED_IMPLEMENTATION_COMPLETE.md +330 -0
  49. ka9q_python-3.7.0/docs/development/MULTI_HOMED_SUPPORT_REVIEW.md +471 -0
  50. ka9q_python-3.7.0/docs/development/PACKAGE_STATUS.md +257 -0
  51. ka9q_python-3.7.0/docs/development/PACKAGE_VERIFICATION.md +238 -0
  52. ka9q_python-3.7.0/docs/development/PERFORMANCE_FIXES_APPLIED.md +428 -0
  53. ka9q_python-3.7.0/docs/development/PERFORMANCE_REVIEW.md +762 -0
  54. ka9q_python-3.7.0/docs/development/PERFORMANCE_REVIEW_V2.md +776 -0
  55. ka9q_python-3.7.0/docs/development/PERFORMANCE_SUMMARY.md +203 -0
  56. ka9q_python-3.7.0/docs/development/QUICK_ACTION_ITEMS.md +370 -0
  57. ka9q_python-3.7.0/docs/development/QUICK_START_DIAGNOSIS.md +319 -0
  58. ka9q_python-3.7.0/docs/development/RELEASE_CHECKLIST_v3.0.0.md +133 -0
  59. ka9q_python-3.7.0/docs/development/SUMMARY.md +291 -0
  60. ka9q_python-3.7.0/docs/development/SUMMARY_WEB_UI_FIXES.md +153 -0
  61. ka9q_python-3.7.0/docs/development/TUNE_IMPLEMENTATION.md +291 -0
  62. ka9q_python-3.7.0/docs/development/WEBUI_SUMMARY.md +411 -0
  63. ka9q_python-3.7.0/docs/development/WEB_UI_ENHANCEMENTS_COMPLETE.md +198 -0
  64. ka9q_python-3.7.0/docs/features/CONTROL_COMPARISON.md +201 -0
  65. ka9q_python-3.7.0/docs/features/DESTINATION_AWARE_CHANNELS.md +73 -0
  66. ka9q_python-3.7.0/docs/features/NEW_FEATURES.md +239 -0
  67. ka9q_python-3.7.0/docs/features/RADIOD_FEATURES_SUMMARY.md +261 -0
  68. ka9q_python-3.7.0/docs/features/RTP_DESTINATION_FEATURE.md +237 -0
  69. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_INSTRUCTIONS.md +196 -0
  70. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v2.2.0.md +275 -0
  71. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v2.4.0.md +269 -0
  72. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v2.5.0.md +241 -0
  73. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v3.0.0.md +148 -0
  74. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v3.1.0.md +139 -0
  75. ka9q_python-3.7.0/docs/releases/GITHUB_RELEASE_v3.2.0.md +116 -0
  76. ka9q_python-3.7.0/docs/releases/RELEASE_NOTES.md +244 -0
  77. ka9q_python-3.7.0/docs/releases/RELEASE_NOTES_v2.1.0.md +291 -0
  78. ka9q_python-3.7.0/docs/releases/RELEASE_NOTES_v2.2.0.md +554 -0
  79. ka9q_python-3.7.0/docs/releases/RELEASE_NOTES_v2.3.0.md +297 -0
  80. ka9q_python-3.7.0/docs/releases/RELEASE_NOTES_v2.4.0.md +545 -0
  81. ka9q_python-3.7.0/docs/releases/RTP_TIMING_RELEASE_NOTES.md +281 -0
  82. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/__init__.py +11 -2
  83. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/addressing.py +32 -10
  84. ka9q_python-3.7.0/ka9q/compat.py +15 -0
  85. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/control.py +42 -30
  86. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/discovery.py +2 -3
  87. ka9q_python-3.7.0/ka9q/pps_calibrator.py +314 -0
  88. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/rtp_recorder.py +7 -1
  89. ka9q_python-3.7.0/ka9q/types.py +186 -0
  90. {ka9q_python-3.4.2 → ka9q_python-3.7.0/ka9q_python.egg-info}/PKG-INFO +59 -2
  91. ka9q_python-3.7.0/ka9q_python.egg-info/SOURCES.txt +152 -0
  92. ka9q_python-3.7.0/ka9q_radio_compat +3 -0
  93. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/pyproject.toml +1 -1
  94. ka9q_python-3.7.0/scripts/sync_types.py +440 -0
  95. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/setup.py +1 -1
  96. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_addressing.py +18 -0
  97. ka9q_python-3.7.0/tests/test_protocol_compat.py +82 -0
  98. ka9q_python-3.7.0/tests/test_ssrc_radiod_host_unit.py +61 -0
  99. ka9q_python-3.4.2/ka9q/types.py +0 -175
  100. ka9q_python-3.4.2/ka9q_python.egg-info/SOURCES.txt +0 -69
  101. ka9q_python-3.4.2/tests/test_decode_description.py +0 -46
  102. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/LICENSE +0 -0
  103. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/advanced_features_demo.py +0 -0
  104. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/channel_cleanup_example.py +0 -0
  105. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/codar_oceanography.py +0 -0
  106. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/diagnostics/diagnose_packets.py +0 -0
  107. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/diagnostics/repro_utc_bug.py +0 -0
  108. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/discover_example.py +0 -0
  109. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/grape_integration_example.py +0 -0
  110. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/hf_band_scanner.py +0 -0
  111. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/rtp_recorder_example.py +0 -0
  112. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/simple_am_radio.py +0 -0
  113. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/stream_example.py +0 -0
  114. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/superdarn_recorder.py +0 -0
  115. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/test_channel_operations.py +0 -0
  116. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/test_improvements.py +0 -0
  117. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/test_timing_fields.py +0 -0
  118. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/tune.py +0 -0
  119. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/examples/tune_example.py +0 -0
  120. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/exceptions.py +0 -0
  121. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/managed_stream.py +0 -0
  122. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/monitor.py +0 -0
  123. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/resequencer.py +0 -0
  124. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/stream.py +0 -0
  125. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/stream_quality.py +0 -0
  126. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q/utils.py +0 -0
  127. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q_python.egg-info/dependency_links.txt +0 -0
  128. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q_python.egg-info/requires.txt +0 -0
  129. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/ka9q_python.egg-info/top_level.txt +0 -0
  130. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/setup.cfg +0 -0
  131. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/__init__.py +0 -0
  132. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/conftest.py +0 -0
  133. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_channel_verification.py +0 -0
  134. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_create_split_encoding.py +0 -0
  135. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_decode_functions.py +0 -0
  136. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_encode_functions.py +0 -0
  137. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_encode_socket.py +0 -0
  138. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_ensure_channel_encoding.py +0 -0
  139. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_integration.py +0 -0
  140. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_iq_20khz_f32.py +0 -0
  141. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_listen_multicast.py +0 -0
  142. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_managed_stream_recovery.py +0 -0
  143. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_monitor.py +0 -0
  144. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_multihomed.py +0 -0
  145. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_native_discovery.py +0 -0
  146. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_performance_fixes.py +0 -0
  147. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_remove_channel.py +0 -0
  148. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_rtp_recorder.py +0 -0
  149. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_security_features.py +0 -0
  150. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_ssrc_dest_unit.py +0 -0
  151. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_ssrc_encoding_unit.py +0 -0
  152. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_ttl_warning.py +0 -0
  153. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_tune.py +0 -0
  154. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_tune_cli.py +0 -0
  155. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_tune_debug.py +0 -0
  156. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_tune_live.py +0 -0
  157. {ka9q_python-3.4.2 → ka9q_python-3.7.0}/tests/test_tune_method.py +0 -0
@@ -0,0 +1,628 @@
1
+ # Changelog
2
+
3
+ ## [3.7.0] - 2026-04-12
4
+
5
+ ### Changed
6
+
7
+ - **Radiod-aware multicast addressing**: `generate_multicast_ip()` now accepts a `radiod_host` keyword argument. When provided, the hash is computed over both the client identifier and the radiod host, producing a distinct multicast IP for each (client, radiod) pair. This prevents address collisions when the same client application (e.g., `hf-timestd`) connects to multiple radiod instances simultaneously.
8
+ - **Radiod-aware SSRC allocation**: `allocate_ssrc()` now accepts a `radiod_host` parameter, included in the deterministic hash. The same channel parameters on different radiod instances produce distinct SSRCs.
9
+ - **Automatic radiod identity propagation**: `RadiodControl.create_channel()` and `RadiodControl.ensure_channel()` automatically pass `self.status_address` as `radiod_host` when computing SSRCs. No API changes needed for callers using the `RadiodControl` methods — radiod-aware uniqueness is automatic.
10
+
11
+ ### Backward Compatibility
12
+
13
+ - `generate_multicast_ip(unique_id)` without `radiod_host` produces identical results to v3.6.0.
14
+ - `allocate_ssrc()` without `radiod_host` changes the hash format (trailing separator added), so SSRC values will differ from v3.6.0 for existing deployments. This is intentional — channels will be re-created with new SSRCs on upgrade. The old SSRCs were not radiod-aware and could collide when a client talked to multiple radiod instances.
15
+ - `ManagedStream`, `RadiodStream`, and `RTPRecorder` require no changes — they receive the already-computed SSRC/address via `ChannelInfo`.
16
+
17
+ ---
18
+
19
+ ## [3.6.0] - 2026-04-09
20
+
21
+ ### Added
22
+
23
+ - **L6 BPSK PPS chain-delay calibration** (`ka9q/pps_calibrator.py`): New utility module for measuring end-to-end RF-to-RTP chain delay using a local GPS-disciplined BPSK PPS injector (WB6CXC design). Three new public classes:
24
+ - `BpskPpsCalibrator` — detects PPS edges in BPSK IQ streams via phase-transition detection, validates timing consistency, and reports the measured chain delay once locked. Algorithm ported from Scott Newell's wd-record.c `bpsk_state_machine()`.
25
+ - `PpsCalibrationResult` — dataclass returned by the calibrator with `chain_delay_ns`, `chain_delay_samples`, edge counters, and lock status.
26
+ - `NotchFilter500Hz` — biquad IIR notch filter at 500 Hz for interference rejection on the BPSK channel.
27
+ - **`ChannelInfo.chain_delay_correction_ns`** (`ka9q/discovery.py`): New optional field. When set, `rtp_to_wallclock()` automatically subtracts this correction from the computed wall time, compensating for the measured RF/ADC/DSP/RTP chain latency. Defaults to `None` (no correction; backward compatible).
28
+ - All three new classes exported from `ka9q/__init__.py`.
29
+
30
+ ### Changed
31
+
32
+ - **`rtp_to_wallclock()`** (`ka9q/rtp_recorder.py`): Now applies `channel.chain_delay_correction_ns` when present. Existing callers are unaffected (the field defaults to `None`).
33
+
34
+ ---
35
+
36
+ ## [3.5.1] - 2026-04-09
37
+
38
+ ### Added
39
+
40
+ - **`DemodType` enum** (`ka9q/types.py`): New auto-generated class mirroring `enum demod_type` from ka9q-radio `radio.h`. Exposes `LINEAR_DEMOD`, `FM_DEMOD`, `WFM_DEMOD`, `SPECT_DEMOD`, `SPECT2_DEMOD`, and `N_DEMOD`.
41
+ - **`WindowType` enum** (`ka9q/types.py`): New auto-generated class mirroring `enum window_type` from ka9q-radio `window.h`. Exposes all 9 FFT window types (`KAISER_WINDOW` through `HP5FT_WINDOW`) plus `N_WINDOW` sentinel.
42
+ - Both new types are exported from `ka9q/__init__.py` and tracked by the drift detection system.
43
+
44
+ ### Fixed
45
+
46
+ - **`set_demod_type()` validation**: Was rejecting demod_type=4 (`SPECT2_DEMOD`), the spectrum v2 mode added in ka9q-radio. Validation range corrected from 0-3 to 0-4.
47
+
48
+ ### Changed
49
+
50
+ - **`sync_types.py` expanded**: Now parses 4 C headers (`status.h`, `rtp.h`, `radio.h`, `window.h`) instead of 2. Drift detection covers `DemodType` and `WindowType` in addition to `StatusType` and `Encoding`. The `_check_enum()` helper replaces duplicated per-enum comparison logic.
51
+ - **Compatibility pin bumped** to ka9q-radio `d39fea8` (no protocol-level changes from previous pin `6b0fec7`; intervening commits were wd-record and bug fixes).
52
+
53
+ ---
54
+
55
+ ## [3.5.0] - 2026-03-31
56
+
57
+ ### Added
58
+
59
+ - **Protocol Drift Detection** (`scripts/sync_types.py`): New tool that code-generates `ka9q/types.py` from the ka9q-radio C headers (`status.h`, `rtp.h`). Three modes:
60
+ - `--check`: exits non-zero if `types.py` is out of sync (for CI and tests)
61
+ - `--apply`: regenerates `types.py` and updates compatibility pins
62
+ - `--diff`: dry-run showing what would change
63
+ - **Compatibility Pin** (`ka9q_radio_compat`): Tracked plain-text file recording the ka9q-radio commit hash that `types.py` was last validated against.
64
+ - **Importable Pin** (`ka9q/compat.py`): `KA9Q_RADIO_COMMIT` constant for use by deployment tooling (e.g. `ka9q-update`).
65
+ - **Drift Test** (`tests/test_protocol_compat.py`): Pytest test that runs `sync_types.py --check` and verifies the pin matches ka9q-radio HEAD. Auto-skips when ka9q-radio source is not present.
66
+ - **New StatusType**: `SPECTRUM_OVERLAP` (116) for FFT window overlap control.
67
+ - **New Encodings**: `MULAW` (10), `ALAW` (11) for telephony-grade audio.
68
+ - **`set_max_delay()`**: New control method replacing `set_packet_buffering()`. Sets maximum aggregation delay in blocks (0-5).
69
+
70
+ ### Changed
71
+
72
+ - **StatusType renames** (matching ka9q-radio HEAD):
73
+ - `MINPACKET` → `MAXDELAY`
74
+ - `GAINSTEP` → `UNUSED4`
75
+ - `CONVERTER_OFFSET` → `UNUSED3`
76
+ - `COHERENT_BIN_SPACING` → `UNUSED2`
77
+ - `BLOCKS_SINCE_POLL` → `UNUSED`
78
+ - **Encoding**: `UNUSED_ENCODING` sentinel shifted from 10 to 12.
79
+ - `types.py` is now auto-generated with C header comments preserved.
80
+
81
+ ### Removed
82
+
83
+ - `compare_encodings.py` and `compare_status_types.py` (replaced by `scripts/sync_types.py`).
84
+
85
+ ### Backward Compatibility
86
+
87
+ - `set_packet_buffering()` retained as a deprecated alias for `set_max_delay()`.
88
+ - `Encoding.F32` and `Encoding.F16` aliases retained for `F32LE` and `F16LE`.
89
+
90
+ ---
91
+
92
+ ## [3.4.2] - 2026-02-05
93
+
94
+ ### Added
95
+
96
+ - **Comprehensive Getting Started Guide**: Added a new `docs/GETTING_STARTED.md` file. This guide provides a step-by-step tutorial for new users, covering installation, a simple first program, core concepts, and troubleshooting tips. It is now the recommended entry point for new users.
97
+ - **Examples README**: Added a new `examples/README.md` file to organize the examples directory. It categorizes examples by complexity (basic, intermediate, advanced) and provides a recommended learning path, making it easier for users to find relevant examples.
98
+
99
+ ### Fixed
100
+
101
+ - **`stream_example.py` Bug**: Fixed a critical bug in `examples/stream_example.py` where the code incorrectly iterated over the dictionary returned by `discover_channels`. The example now correctly accesses `ChannelInfo` objects, preventing an `AttributeError` and allowing the example to run as intended.
102
+
103
+ ### Changed
104
+
105
+ - **Updated API Documentation**: The documentation for `create_channel` in `docs/API_REFERENCE.md` has been updated to match the actual implementation. This includes adding the `destination` and `encoding` parameters, correcting the parameter order, and documenting the return type (`int` SSRC).
106
+ - **Updated README**: The main `README.md` has been updated to link to the new Getting Started guide.
107
+
108
+ ---
109
+
110
+ ## [3.4.1] - 2026-01-27
111
+
112
+ ### Fixed
113
+
114
+ - **Stable SSRC Allocation**: Replaced unstable Python `hash()` with `hashlib.sha256` in `allocate_ssrc()`. This ensure SSRCs remain consistent across process restarts, allowing `radiod` to reuse existing receivers and preventing resource exhaustion.
115
+
116
+ ## [3.4.0] - 2026-01-13
117
+
118
+ ### Added
119
+
120
+ - **TTL Reporting**: Implemented decoding of `OUTPUT_TTL` (status type 19) from `radiod`.
121
+ - **TTL Warning**: Added a warning log when `radiod` reports a TTL of 0, indicating multicast restrictions.
122
+ - **Web API**: Exposed the `ttl` value in the `/api/channel/<address>/<ssrc>` endpoint.
123
+ - **Tests**: Added unit and integration tests for TTL decoding and warning logic.
124
+
125
+ ### Fixed
126
+
127
+ - **Web UI Start Script**: Fixed `webui/start.sh` to correctly locate `app.py` when running from outside the `webui` directory.
128
+
129
+ ## [3.2.7] - 2025-12-17
130
+
131
+ ### Added
132
+
133
+ - **ChannelMonitor**: New service that provides automatic recovery from `radiod` restarts. It monitors registered channels and automatically invokes `ensure_channel` to restore them if they disappear.
134
+ - Usage: `monitor = ChannelMonitor(control); monitor.start(); monitor.monitor_channel(...)`
135
+
136
+ ## [3.2.6] - 2025-12-17
137
+
138
+ ### Added
139
+
140
+ - **Output Encoding Support**: Added complete support for specifying output encoding (e.g., F32, S16LE, OPUS) in `ensure_channel` and `create_channel`.
141
+ - `create_channel` now automatically sends the required follow-up `OUTPUT_ENCODING` command to `radiod`.
142
+ - `ensure_channel` verifies the encoding of existing channels and reconfigures them if different from the requested encoding.
143
+ - `ChannelInfo` now includes the `encoding` field for discovered channels.
144
+
145
+ ## [3.2.5] - 2025-12-17
146
+
147
+ ### Added
148
+
149
+ - **Destination-Aware Channels**: `ka9q-python` now supports unique destination IP addresses per client application. The `ensure_channel` and `create_channel` methods now accept a `destination` parameter.
150
+ - **Unique IP Generation**: Added `generate_multicast_ip(unique_id)` helper function to deterministically map application IDs to the `239.0.0.0/8` multicast range.
151
+ - **Improved SSRC Allocation**: `allocate_ssrc` now includes the destination address in its hash calculation, ensuring that streams with different destinations get unique SSRCs.
152
+
153
+ ## [3.2.4] - 2025-12-16
154
+
155
+ ### Fixed
156
+
157
+ - **Resequencer Fragmented IQ Support** - Fixed resequencer to correctly handle fragmented IQ packets from radiod. The resequencer now uses actual packet sample count for timestamp tracking instead of the fixed `samples_per_packet` value. This prevents false gap detection when radiod fragments large IQ payloads (e.g., IQ 20kHz F32 fragments into 1440+1440+320 byte packets). Affects `_try_output()`, `_handle_lost_packet()`, and `flush()` methods.
158
+
159
+ ### Added
160
+
161
+ - **Channel Verification Test Suite** - Comprehensive test suite (`tests/test_channel_verification.py`) that verifies radiod channel creation, encoding, sample rate, and destination configuration. Demonstrates ka9q-python usage patterns and serves as integration test.
162
+
163
+ ## [3.2.3] - 2025-12-16
164
+
165
+ ### Fixed
166
+
167
+ - **Socket Reconnection Vulnerability** - Fixed vulnerability where socket errors would terminate the receive loop permanently. Both `RadiodStream` and `RTPRecorder` now implement automatic reconnection with exponential backoff (1s to 60s max). This provides robustness against network interface restarts, multicast group membership drops, and transient network errors.
168
+
169
+ ## [3.2.2] - 2025-12-13
170
+
171
+ ### Fixed
172
+
173
+ - **Time Calculation** - Fixed 18-second error in UTC timestamp calculation caused by leap seconds. `rtp_to_wallclock()` now correctly subtracts the current 18-second GPS-UTC offset.
174
+
175
+ ## [3.2.1] - 2025-12-11
176
+
177
+ ### Fixed
178
+
179
+ - **Time Calculation** - Fixed GPS-to-Unix time conversion in `rtp_to_wallclock()`. Previous formula incorrectly applied NTP epoch offset, resulting in future timestamps.
180
+ - **Documentation** - Updated `RTP_TIMING_SUPPORT.md` with the correct formula.
181
+
182
+ ## [3.2.0] - 2025-12-01
183
+
184
+ ### 🌊 RadiodStream API - Continuous Sample Delivery with Quality Tracking
185
+
186
+ This release adds a high-level streaming API that delivers continuous sample streams with comprehensive quality metadata. Designed for applications like GRAPE, WSPR, CODAR, and SuperDARN that need reliable data capture with gap detection and quality metrics.
187
+
188
+ ### Added
189
+
190
+ **Stream Module (`ka9q.stream`):**
191
+
192
+ - `RadiodStream` - High-level sample stream with automatic resequencing and gap filling
193
+ - Callback-based delivery: `on_samples(samples: np.ndarray, quality: StreamQuality)`
194
+ - Handles IQ and audio modes automatically
195
+ - Cross-platform multicast support (Linux, macOS, Windows)
196
+
197
+ **Quality Tracking (`ka9q.stream_quality`):**
198
+
199
+ - `StreamQuality` - Comprehensive quality metrics per batch and cumulative
200
+ - `completeness_pct` - Percentage of expected samples received
201
+ - `total_gap_events` / `total_gaps_filled` - Gap statistics
202
+ - RTP packet metrics: received, lost, duplicate, resequenced
203
+ - RTP timestamps: `first_rtp_timestamp`, `last_rtp_timestamp` for precise timing
204
+ - `GapEvent` - Individual gap details (position, duration, source)
205
+ - `GapSource` - Gap type classification (NETWORK_LOSS, RESEQUENCE_TIMEOUT, EMPTY_PAYLOAD, etc.)
206
+
207
+ **Resequencer (`ka9q.resequencer`):**
208
+
209
+ - `PacketResequencer` - Circular buffer resequencing with gap detection
210
+ - Handles out-of-order packet delivery
211
+ - KA9Q-style signed 32-bit timestamp arithmetic for wrap handling
212
+ - Zero-fills gaps for continuous stream integrity
213
+ - `RTPPacket` - Packet data structure with samples
214
+ - `ResequencerStats` - Resequencing statistics
215
+
216
+ **Examples:**
217
+
218
+ - `examples/stream_example.py` - Basic streaming demonstration
219
+ - `examples/grape_integration_example.py` - Two-phase recording pattern (startup buffer → recording)
220
+
221
+ ### Example Usage
222
+
223
+ ```python
224
+ from ka9q import RadiodStream, StreamQuality, discover_channels
225
+
226
+ def on_samples(samples, quality: StreamQuality):
227
+ print(f"Got {len(samples)} samples, {quality.completeness_pct:.1f}% complete")
228
+ for gap in quality.batch_gaps:
229
+ print(f" Gap: {gap.source.value}, {gap.duration_samples} samples")
230
+
231
+ channels = discover_channels('radiod.local')
232
+ stream = RadiodStream(
233
+ channel=channels[10000000],
234
+ on_samples=on_samples,
235
+ samples_per_packet=320, # RTP timestamp increment at 16kHz
236
+ )
237
+ stream.start()
238
+ # ... run until done ...
239
+ final_quality = stream.stop()
240
+ ```
241
+
242
+ ### Architecture
243
+
244
+ ```
245
+ radiod → Multicast → RadiodStream → PacketResequencer → App Callback
246
+
247
+ StreamQuality (per batch + cumulative)
248
+ ```
249
+
250
+ **Core delivers:**
251
+
252
+ - Continuous sample stream (gaps zero-filled)
253
+ - Quality metadata with every callback
254
+
255
+ **Applications handle:**
256
+
257
+ - Segmentation (1-minute NPZ, 2-minute WAV, etc.)
258
+ - Format conversion
259
+ - App-specific gap classification (cadence_fill, late_start, etc.)
260
+
261
+ ---
262
+
263
+ ## [3.1.0] - 2025-12-01
264
+
265
+ ### 🎯 SSRC Abstraction - SSRC-Free Channel Creation
266
+
267
+ This release removes SSRC from the application concern. Applications now specify **what they want** (frequency, mode, sample rate) and the system handles SSRC allocation internally.
268
+
269
+ ### Added
270
+
271
+ **SSRC Allocation:**
272
+
273
+ - `allocate_ssrc(frequency_hz, preset, sample_rate, agc, gain)` - Deterministic SSRC allocation from channel parameters. Same parameters always produce the same SSRC, enabling stream sharing across applications.
274
+
275
+ **SSRC-Free `create_channel()`:**
276
+
277
+ - `ssrc` parameter is now **optional** (moved to end of parameters)
278
+ - When omitted, SSRC is auto-allocated using `allocate_ssrc()`
279
+ - Method now **returns the SSRC** (useful when auto-allocated)
280
+
281
+ ### Changed
282
+
283
+ - `create_channel()` signature: `frequency_hz` is now the first (required) parameter
284
+ - `create_channel()` now returns `int` (the SSRC) instead of `None`
285
+
286
+ ### Cross-Library Compatibility
287
+
288
+ The SSRC allocation algorithm matches signal-recorder's `StreamSpec.ssrc_hash()`:
289
+
290
+ ```python
291
+ key = (round(frequency_hz), preset.lower(), sample_rate, agc, round(gain, 1))
292
+ return hash(key) & 0x7FFFFFFF
293
+ ```
294
+
295
+ This ensures:
296
+
297
+ - Same parameters → same SSRC in both ka9q-python and signal-recorder
298
+ - Stream sharing works across applications using either library
299
+ - Deterministic allocation for coordination
300
+
301
+ ### Example
302
+
303
+ ```python
304
+ from ka9q import RadiodControl, allocate_ssrc
305
+
306
+ # SSRC-free API (recommended)
307
+ with RadiodControl("radiod.local") as control:
308
+ ssrc = control.create_channel(
309
+ frequency_hz=14.074e6,
310
+ preset="usb",
311
+ sample_rate=12000
312
+ )
313
+ print(f"Created channel with SSRC: {ssrc}")
314
+
315
+ # Or use allocate_ssrc() directly for coordination
316
+ ssrc = allocate_ssrc(10.0e6, "iq", 16000)
317
+ ```
318
+
319
+ ---
320
+
321
+ ## [3.0.0] - 2025-12-01
322
+
323
+ ### 🎉 Major Release: Complete RadioD Feature Exposure
324
+
325
+ This major release exposes **all remaining radiod features** through the Python interface, providing comprehensive control over every aspect of ka9q-radio operation.
326
+
327
+ ### Added - 20 New Control Methods
328
+
329
+ **Tracking & Tuning:**
330
+
331
+ - `set_doppler(ssrc, doppler_hz, doppler_rate_hz_per_sec)` - Doppler frequency shift and rate for satellite tracking
332
+ - `set_first_lo(ssrc, frequency_hz)` - Direct hardware tuner frequency control
333
+
334
+ **Signal Processing:**
335
+
336
+ - `set_pll(ssrc, enable, bandwidth_hz, square)` - Phase-locked loop configuration for carrier tracking
337
+ - `set_squelch(ssrc, enable, open_snr_db, close_snr_db)` - SNR-based squelch with hysteresis
338
+ - `set_envelope_detection(ssrc, enable)` - Toggle between envelope (AM) and synchronous detection
339
+ - `set_independent_sideband(ssrc, enable)` - ISB mode (USB/LSB to separate L/R channels)
340
+ - `set_fm_threshold_extension(ssrc, enable)` - Improve FM reception at weak signal levels
341
+
342
+ **AGC & Levels:**
343
+
344
+ - `set_agc_threshold(ssrc, threshold_db)` - Set AGC activation threshold above noise floor
345
+
346
+ **Output Control:**
347
+
348
+ - `set_output_channels(ssrc, channels)` - Configure mono (1) or stereo (2) output
349
+ - `set_output_encoding(ssrc, encoding)` - Select output format (S16BE, S16LE, F32, F16, OPUS)
350
+ - `set_opus_bitrate(ssrc, bitrate)` - Configure Opus encoder bitrate (0=auto, typical 32000-128000)
351
+ - `set_packet_buffering(ssrc, min_blocks)` - Control RTP packet buffering (0-4 blocks)
352
+ - `set_destination(ssrc, address, port)` - Change RTP output multicast destination
353
+
354
+ **Filtering:**
355
+
356
+ - `set_filter2(ssrc, blocksize, kaiser_beta)` - Configure secondary filter for additional selectivity
357
+
358
+ **Spectrum Analysis:**
359
+
360
+ - `set_spectrum(ssrc, bin_bw_hz, bin_count, crossover_hz, kaiser_beta)` - Configure spectrum analyzer parameters
361
+
362
+ **System Control:**
363
+
364
+ - `set_status_interval(ssrc, interval)` - Set automatic status reporting rate
365
+ - `set_demod_type(ssrc, demod_type)` - Switch demodulator type (LINEAR/FM/WFM/SPECTRUM)
366
+ - `set_rf_gain(ssrc, gain_db)` - Control RF front-end gain (hardware-dependent)
367
+ - `set_rf_attenuation(ssrc, atten_db)` - Control RF front-end attenuation (hardware-dependent)
368
+ - `set_options(ssrc, set_bits, clear_bits)` - Set/clear experimental option bits
369
+
370
+ ### Updated - Type Definitions
371
+
372
+ Fixed `StatusType` constants in `ka9q/types.py`:
373
+
374
+ - `SPECTRUM_FFT_N = 76` (was UNUSED16)
375
+ - `SPECTRUM_KAISER_BETA = 91` (was UNUSED20)
376
+ - `CROSSOVER = 95` (was UNUSED21)
377
+
378
+ ### Documentation
379
+
380
+ **New Documentation Files:**
381
+
382
+ - `NEW_FEATURES.md` - Comprehensive documentation of all 20 new features with examples
383
+ - `QUICK_REFERENCE.md` - Quick reference guide with practical code examples
384
+ - `RADIOD_FEATURES_SUMMARY.md` - Complete implementation summary and verification
385
+ - `examples/advanced_features_demo.py` - Working demonstration script
386
+
387
+ ### Feature Coverage
388
+
389
+ This release provides complete coverage of radiod's TLV command set:
390
+
391
+ - ✅ All 35+ radiod TLV commands now supported
392
+ - ✅ Doppler tracking for satellite reception
393
+ - ✅ PLL carrier tracking for coherent detection
394
+ - ✅ SNR squelch with hysteresis
395
+ - ✅ Independent sideband (ISB) mode
396
+ - ✅ FM threshold extension
397
+ - ✅ Secondary filtering
398
+ - ✅ Spectrum analyzer configuration
399
+ - ✅ Output encoding selection
400
+ - ✅ RF hardware controls
401
+ - ✅ Experimental option bits
402
+
403
+ ### Use Cases Enabled
404
+
405
+ **Satellite Communications:**
406
+
407
+ ```python
408
+ control.set_doppler(ssrc=12345, doppler_hz=-5000, doppler_rate_hz_per_sec=100)
409
+ ```
410
+
411
+ **Coherent AM Reception:**
412
+
413
+ ```python
414
+ control.set_pll(ssrc=12345, enable=True, bandwidth_hz=50)
415
+ control.set_envelope_detection(ssrc=12345, enable=False)
416
+ ```
417
+
418
+ **Independent Sideband:**
419
+
420
+ ```python
421
+ control.set_independent_sideband(ssrc=12345, enable=True)
422
+ control.set_output_channels(ssrc=12345, channels=2)
423
+ ```
424
+
425
+ **Opus Audio Streaming:**
426
+
427
+ ```python
428
+ control.set_output_encoding(ssrc=12345, encoding=Encoding.OPUS)
429
+ control.set_opus_bitrate(ssrc=12345, bitrate=64000)
430
+ ```
431
+
432
+ **Spectrum Analysis:**
433
+
434
+ ```python
435
+ control.set_spectrum(ssrc=12345, bin_bw_hz=100, bin_count=512)
436
+ ```
437
+
438
+ ### Breaking Changes
439
+
440
+ ⚠️ **None** - This release is 100% backward compatible. All existing code will continue to work without modification.
441
+
442
+ ### Implementation Details
443
+
444
+ - All new methods follow existing design patterns
445
+ - Comprehensive input validation with clear error messages
446
+ - Full logging support for debugging
447
+ - Proper docstrings with examples for all methods
448
+ - Thread-safe operation via existing infrastructure
449
+ - ~400 lines of new, tested code
450
+
451
+ ### Verification
452
+
453
+ ✅ All code compiles successfully
454
+ ✅ All imports validated
455
+ ✅ 20 new methods confirmed available
456
+ ✅ Pattern consistency verified
457
+ ✅ Documentation complete
458
+
459
+ ### Migration Guide
460
+
461
+ No migration needed - all changes are additive. Simply update to v3.0.0 and start using the new features as needed.
462
+
463
+ For examples, see:
464
+
465
+ - `NEW_FEATURES.md` for detailed feature documentation
466
+ - `QUICK_REFERENCE.md` for quick code examples
467
+ - `examples/advanced_features_demo.py` for working demonstrations
468
+
469
+ ---
470
+
471
+ ## [2.5.0] - 2025-11-30
472
+
473
+ ### Added - RTP Recorder Enhancement
474
+
475
+ - **`pass_all_packets` parameter** in `RTPRecorder.__init__()`
476
+ - Allows bypassing internal packet resequencing logic
477
+ - When `True`, passes ALL packets to callback regardless of sequence gaps
478
+ - Metrics still track sequence errors, dropped packets, and timestamp jumps
479
+ - Designed for applications with external resequencers (e.g., signal-recorder's PacketResequencer)
480
+ - `max_packet_gap` parameter ignored when `pass_all_packets=True`
481
+ - No resync state transitions - stays in RECORDING mode continuously
482
+
483
+ - **Updated `_validate_packet()` method** in `RTPRecorder`
484
+ - Conditional resync triggering based on `pass_all_packets` flag
485
+ - Metrics tracking independent of pass-through mode
486
+ - Early return for pass-all mode to bypass resync state handling
487
+ - Preserves original behavior when `pass_all_packets=False` (default)
488
+
489
+ ### Documentation
490
+
491
+ - **API Reference** - Added `pass_all_packets` parameter documentation
492
+ - Added example showing external resequencer usage
493
+ - Updated parameter descriptions
494
+
495
+ - **Implementation Guide** - Usage patterns for external resequencing
496
+
497
+ ### Backward Compatibility
498
+
499
+ 100% backward compatible - `pass_all_packets` defaults to `False`, preserving existing behavior.
500
+
501
+ ---
502
+
503
+ ## [2.4.0] - 2025-11-29
504
+
505
+ ### Added - RTP Destination Control
506
+
507
+ - **`encode_socket()` function** in `ka9q/control.py`
508
+ - Encodes IPv4 socket addresses in radiod's TLV format (6-byte format)
509
+ - Validates IP addresses and port numbers
510
+ - Matches radiod's `decode_socket()` expectations exactly
511
+
512
+ - **`_validate_multicast_address()` function** in `ka9q/control.py`
513
+ - Validates multicast address format (IP or hostname)
514
+ - Allows both IP addresses and DNS names
515
+
516
+ - **`destination` parameter** to `create_channel()` method
517
+ - Accepts format: "address" or "address:port"
518
+ - Examples: "239.1.2.3", "wspr.local", "239.1.2.3:5004"
519
+ - Enables per-channel RTP destination control
520
+
521
+ - **Full `destination` support** in `tune()` method
522
+ - Removed "not implemented" warning
523
+ - Properly encodes destination socket addresses
524
+ - Supports port specification (defaults to 5004)
525
+
526
+ - **Comprehensive documentation**:
527
+ - `RTP_DESTINATION_FEATURE.md` - Feature documentation with examples
528
+ - `CONTROL_COMPARISON.md` - Command-by-command comparison with control.c
529
+ - `IMPLEMENTATION_STATUS.md` - Complete status of ka9q-python vs control/tune
530
+ - `WEBUI_SUMMARY.md` - Web UI implementation details
531
+
532
+ - **Test suite** for socket encoding
533
+ - `tests/test_encode_socket.py` - Comprehensive tests for encode_socket()
534
+ - Tests roundtrip encoding/decoding
535
+ - Tests error handling and validation
536
+
537
+ ### Added - Web UI
538
+
539
+ - **Complete web-based control interface** (`webui/` directory)
540
+ - `webui/app.py` - Flask backend with REST API
541
+ - `webui/templates/index.html` - Modern responsive UI
542
+ - `webui/static/style.css` - Dark theme styling
543
+ - `webui/static/app.js` - Frontend logic with auto-refresh
544
+ - `webui/start.sh` - Quick start script
545
+ - `webui/requirements.txt` - Python dependencies
546
+ - `webui/README.md` - Complete usage documentation
547
+
548
+ - **Web UI Features**:
549
+ - Auto-discovery of radiod instances on LAN
550
+ - Pull-down selector for radiod instances
551
+ - Channel list with frequency, mode, sample rate, destination
552
+ - Real-time channel monitoring (1-second auto-refresh)
553
+ - 4-column layout: Tuning, Filter, Output, Signal
554
+ - Full-width Gain & AGC section
555
+ - Live SNR, baseband power, noise density
556
+ - Error handling with auto-stop after 3 consecutive failures
557
+ - Responsive design (desktop, tablet, mobile)
558
+ - No framework dependencies (vanilla JavaScript)
559
+
560
+ - **Web UI API Endpoints**:
561
+ - `GET /api/discover` - Discover radiod instances
562
+ - `GET /api/channels/<address>` - List channels
563
+ - `GET /api/channel/<address>/<ssrc>` - Get channel status
564
+ - `POST /api/tune/<address>/<ssrc>` - Tune channel
565
+
566
+ ### Fixed
567
+
568
+ - **Deduplication** in `discover_radiod_services()`
569
+ - Uses dict with address as key to remove duplicates
570
+ - Sorts results by name for consistency
571
+ - Fixes multiple entries from avahi-browse (IPv4/IPv6, multiple interfaces)
572
+
573
+ - **Timeout handling** in web UI
574
+ - Increased backend timeout from 2s to 10s
575
+ - Added consecutive error tracking (stops after 3 failures)
576
+ - Better error messages for inactive channels
577
+ - Prevents refresh interval overlap
578
+
579
+ - **Channel selection** in web UI
580
+ - Fixed bouncing between multiple channels
581
+ - Only one channel refreshes at a time
582
+ - Proper cleanup when switching channels
583
+ - Added `isRefreshing` flag to prevent race conditions
584
+
585
+ ### Changed
586
+
587
+ - **Control comparison** - Documented 52.5% coverage of control.c commands
588
+ - 21 commands fully implemented (all core functionality)
589
+ - 19 advanced commands not yet implemented (PLL, squelch, etc.)
590
+ - 100% coverage of tune.c commands
591
+
592
+ - **Implementation status** - Confirmed ka9q-python is production-ready
593
+ - Complete frequency control
594
+ - Complete mode/preset control
595
+ - Complete filter control (including Kaiser beta)
596
+ - Complete AGC control (all 5 parameters)
597
+ - Complete gain control (manual, RF gain, RF atten)
598
+ - Complete output control (sample rate, encoding, destination)
599
+ - Complete status interrogation
600
+
601
+ ## Summary of Changes
602
+
603
+ This release adds:
604
+
605
+ 1. **Per-channel RTP destination control** - Clients can now specify unique RTP destinations for each channel
606
+ 2. **Web UI** - Modern browser-based interface for monitoring and controlling radiod
607
+ 3. **Better discovery** - Deduplicated radiod instance discovery
608
+ 4. **Comprehensive documentation** - Full comparison with control.c and implementation status
609
+
610
+ ### Files Modified
611
+
612
+ - `ka9q/control.py` - Added encode_socket(), updated tune() and create_channel()
613
+ - `ka9q/discovery.py` - Fixed deduplication in discover_radiod_services()
614
+ - `tests/test_encode_socket.py` - New test suite
615
+
616
+ ### Files Added
617
+
618
+ - `webui/` - Complete web UI implementation (7 files)
619
+ - `RTP_DESTINATION_FEATURE.md` - Feature documentation
620
+ - `CONTROL_COMPARISON.md` - Command comparison
621
+ - `IMPLEMENTATION_STATUS.md` - Implementation status
622
+ - `WEBUI_SUMMARY.md` - Web UI documentation
623
+ - `CHANGELOG.md` - This file
624
+
625
+ ### Backward Compatibility
626
+
627
+ All changes are backward compatible. Existing code continues to work without modification.
628
+ The `destination` parameter is optional in both `create_channel()` and `tune()`.
@@ -1,12 +1,14 @@
1
1
  # Include documentation
2
2
  include README.md
3
3
  include LICENSE
4
- include SUMMARY.md
5
- include TUNE_IMPLEMENTATION.md
6
- include NATIVE_DISCOVERY.md
7
- include CROSS_PLATFORM_SUPPORT.md
8
- include TEST_RESULTS.md
9
- include TESTING_SUMMARY.md
4
+ include CHANGELOG.md
5
+ include RELEASE_NOTES_v3.5.0.md
6
+
7
+ # Include protocol compatibility pin
8
+ include ka9q_radio_compat
9
+
10
+ # Include sync tooling
11
+ recursive-include scripts *.py
10
12
 
11
13
  # Include examples
12
14
  recursive-include examples *.py
@@ -14,6 +16,9 @@ recursive-include examples *.py
14
16
  # Include tests
15
17
  recursive-include tests *.py
16
18
 
19
+ # Include docs
20
+ recursive-include docs *.md
21
+
17
22
  # Exclude compiled files
18
23
  global-exclude *.pyc
19
24
  global-exclude __pycache__