python-aaronia 0.5.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 (73) hide show
  1. python_aaronia-0.5.0/.cargo/config.toml +2 -0
  2. python_aaronia-0.5.0/.gitattributes +1 -0
  3. python_aaronia-0.5.0/.gitignore +24 -0
  4. python_aaronia-0.5.0/CHANGELOG.md +363 -0
  5. python_aaronia-0.5.0/CONTRIBUTING.md +184 -0
  6. python_aaronia-0.5.0/Cargo.lock +5254 -0
  7. python_aaronia-0.5.0/Cargo.toml +136 -0
  8. python_aaronia-0.5.0/DESIGN.md +101 -0
  9. python_aaronia-0.5.0/LICENSE +674 -0
  10. python_aaronia-0.5.0/PKG-INFO +116 -0
  11. python_aaronia-0.5.0/PLUGINS.md +138 -0
  12. python_aaronia-0.5.0/README.md +503 -0
  13. python_aaronia-0.5.0/benches/decompress_block.rs +61 -0
  14. python_aaronia-0.5.0/benches/deinterleave_dual_iq.rs +48 -0
  15. python_aaronia-0.5.0/benches/parse_int16_packet.rs +75 -0
  16. python_aaronia-0.5.0/benches/rtsa_open_and_read.rs +102 -0
  17. python_aaronia-0.5.0/deny.toml +42 -0
  18. python_aaronia-0.5.0/docs/FILESPEC.md +911 -0
  19. python_aaronia-0.5.0/docs/HTTPSPEC.md +707 -0
  20. python_aaronia-0.5.0/docs/SDKSPEC.md +878 -0
  21. python_aaronia-0.5.0/examples/channel_hopping.rs +82 -0
  22. python_aaronia-0.5.0/examples/device_control.rs +29 -0
  23. python_aaronia-0.5.0/examples/dump_metadata.rs +65 -0
  24. python_aaronia-0.5.0/examples/http_iq_quickstart.rs +70 -0
  25. python_aaronia-0.5.0/examples/native_sdk_basic.rs +132 -0
  26. python_aaronia-0.5.0/examples/native_sdk_transmit.rs +145 -0
  27. python_aaronia-0.5.0/examples/noaa_scanner.rs +333 -0
  28. python_aaronia-0.5.0/examples/python_arrow_example.py +41 -0
  29. python_aaronia-0.5.0/examples/read_rtsa_file.rs +34 -0
  30. python_aaronia-0.5.0/examples/soapy_python_example.py +60 -0
  31. python_aaronia-0.5.0/include/aaronia.h +192 -0
  32. python_aaronia-0.5.0/pyproject.toml +28 -0
  33. python_aaronia-0.5.0/python-aaronia/Cargo.toml +25 -0
  34. python_aaronia-0.5.0/python-aaronia/README.md +100 -0
  35. python_aaronia-0.5.0/python-aaronia/src/lib.rs +491 -0
  36. python_aaronia-0.5.0/python-aaronia/test_basic.py +54 -0
  37. python_aaronia-0.5.0/scripts/ci-local.sh +203 -0
  38. python_aaronia-0.5.0/soapy-aaronia/AaroniaSoapyDevice.cpp +577 -0
  39. python_aaronia-0.5.0/soapy-aaronia/AaroniaSoapyDevice.hpp +132 -0
  40. python_aaronia-0.5.0/soapy-aaronia/CMakeLists.txt +76 -0
  41. python_aaronia-0.5.0/soapy-aaronia/README.md +85 -0
  42. python_aaronia-0.5.0/soapy-aaronia/Registration.cpp +153 -0
  43. python_aaronia-0.5.0/src/c_api.rs +1526 -0
  44. python_aaronia-0.5.0/src/decompression.rs +704 -0
  45. python_aaronia-0.5.0/src/detection.rs +259 -0
  46. python_aaronia-0.5.0/src/error.rs +79 -0
  47. python_aaronia-0.5.0/src/file_source.rs +4580 -0
  48. python_aaronia-0.5.0/src/http_endpoints.rs +2086 -0
  49. python_aaronia-0.5.0/src/http_sink.rs +378 -0
  50. python_aaronia-0.5.0/src/http_source.rs +1481 -0
  51. python_aaronia-0.5.0/src/http_streaming.rs +1646 -0
  52. python_aaronia-0.5.0/src/lib.rs +123 -0
  53. python_aaronia-0.5.0/src/native_sdk.rs +3246 -0
  54. python_aaronia-0.5.0/src/sdk_sink.rs +375 -0
  55. python_aaronia-0.5.0/src/sdk_source.rs +544 -0
  56. python_aaronia-0.5.0/src/sdr_source_impl.rs +542 -0
  57. python_aaronia-0.5.0/src/seify_impl.rs +437 -0
  58. python_aaronia-0.5.0/src/unified_sink.rs +294 -0
  59. python_aaronia-0.5.0/src/unified_source.rs +1971 -0
  60. python_aaronia-0.5.0/src/utils.rs +533 -0
  61. python_aaronia-0.5.0/tests/c_api_test.rs +72 -0
  62. python_aaronia-0.5.0/tests/http_mock_test.rs +501 -0
  63. python_aaronia-0.5.0/tests/http_sink_test.rs +100 -0
  64. python_aaronia-0.5.0/tests/integration_test.rs +532 -0
  65. python_aaronia-0.5.0/tests/live_smoke.rs +604 -0
  66. python_aaronia-0.5.0/tests/native_sdk_load.rs +47 -0
  67. python_aaronia-0.5.0/tests/properties.proptest-regressions +8 -0
  68. python_aaronia-0.5.0/tests/properties.rs +407 -0
  69. python_aaronia-0.5.0/tests/rtsa_negative_test.rs +104 -0
  70. python_aaronia-0.5.0/tests/sdr_source_impl_test.rs +420 -0
  71. python_aaronia-0.5.0/tests/spec_coverage.rs +281 -0
  72. python_aaronia-0.5.0/tests/test_cw_mag.rs +69 -0
  73. python_aaronia-0.5.0/tests/test_cw_meta.rs +67 -0
@@ -0,0 +1,2 @@
1
+ [env]
2
+ PYO3_USE_ABI3_FORWARD_COMPATIBILITY = "1"
@@ -0,0 +1 @@
1
+ *.rtsa filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,24 @@
1
+ /target/
2
+ **/*.rs.bk
3
+ .DS_Store
4
+
5
+ # Proprietary Aaronia SDK artifacts (header/DLL/import lib) used for local
6
+ # FFI-conformance verification only. Marked CONFIDENTIAL AND PROPRIETARY by
7
+ # Aaronia — must not be redistributed in this public repository.
8
+ tests/sdk/
9
+
10
+ # Local agent configuration/memory
11
+ .agents/
12
+
13
+ # CMake build directory for SoapySDR plugin
14
+ soapy-aaronia/build/
15
+ python-aaronia/__pycache__/test_basic.cpython-314-pytest-9.1.1.pyc
16
+ python-aaronia/err.txt
17
+ python-aaronia/check_errors.txt
18
+ branch_diff.txt
19
+
20
+ # Python bindings dev environment and local plugin builds
21
+ python-aaronia/venv/
22
+ python-aaronia/**/__pycache__/
23
+ soapy-aaronia/build/
24
+ dist/
@@ -0,0 +1,363 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [v0.5.0] - 2026-08-11
6
+
7
+ Breaking 0.x release combining the RTSA file-format verification work
8
+ with the new binding/plugin surfaces. (The recorded floor for this
9
+ range was 0.4.0; it ships as 0.5.0 to reflect the added Python/SoapySDR/
10
+ TX/GPS feature scope. A branch briefly relabelled it 1.0.0 — a 1.0
11
+ stability commitment on top of never-hardware-verified TX and freshly
12
+ reviewed bindings is exactly what 1.0 must not be.) The new surfaces
13
+ (Python bindings, SoapySDR plugin, TX, GPS time) shipped through a
14
+ six-stream production review with live-hardware validation of the RX
15
+ paths; hardware-facing TX/dual-channel paths remain
16
+ **hardware-unverified** where noted in the docs.
17
+
18
+ ### Breaking
19
+ - `RtsaMetadata` lost `device_name`, `stream_sample_rate`, and
20
+ `stream_center_frequency`; `RtsaSource::stream_info()` now returns a
21
+ 3-tuple. These fields were only ever populated by a fabricated
22
+ "proximity" STRM layout that no spec revision or capture uses; real
23
+ files always produced `None`.
24
+ - `SdkConfig` and `AaroniaConfig` gained a public `receiver_channel`
25
+ field (breaks struct-literal construction);
26
+ `NativeSdkSource::configure_iq_receiver` takes the channel as a
27
+ fourth parameter so retunes re-apply it.
28
+
29
+ ### Added
30
+ - **Python bindings (`python-aaronia`)**: PyO3 bindings (module
31
+ `aaronia`, abi3 wheels for CPython 3.9+) with typed exceptions mapped
32
+ from the crate's error enum, readable/settable config covering HTTP
33
+ URL, file playback, serial, format, scale, reference level and
34
+ receiver channel, GIL-released blocking calls, live retuning, and
35
+ single-copy reads into NumPy or PyArrow arrays (including
36
+ dual-channel reads).
37
+ - **SoapySDR plugin (`soapy-aaronia`)**: C++ plugin over the C API —
38
+ RX streaming in CF32/CS16 with honoured `timeoutUs` and partial
39
+ reads, retune-safe locking, per-direction stream handles, discrete
40
+ sample-rate listing, device args (`url`, `file`, `serial`, `freq`,
41
+ `rate`, `ref_level`, `format`, `scale`, `rx_channel`), truthful
42
+ GPS hardware-time probing, and TX (hardware-unverified) via the
43
+ native SDK on Windows/Linux.
44
+ - **Transmit support**: `UnifiedSink` + `aaronia_sink_*` C API driving
45
+ the native-SDK TX path (device open/configure/start, master-clock
46
+ burst timing, caller-controlled packet-boundary flags).
47
+ **Hardware-unverified**; unavailable off Windows/Linux and errors
48
+ clearly there.
49
+ - **GPS time**: `get_gps_time` from the SDK telemetry tree, over FFI,
50
+ with validity gating; native-SDK backend only.
51
+ - **Release automation**: maturin wheels + PyPI trusted publishing and
52
+ SoapySDR plugin packaging in release.yml (first exercised on this
53
+ branch's CI, not at tag time).
54
+ - Receiver-channel selection (`RxChannel`: `Rx1`/`Rx2`/`Rx1And2`) on
55
+ every native-SDK config surface, and true dual-channel capture via
56
+ `read_samples_dual` on `NativeSdkSource`, `SdkSource`, and
57
+ `AaroniaSource`, with a read-mode latch preventing silent mono/dual
58
+ mixing.
59
+ - `StrmChunk::capture_start_offset`: RTSA files' undocumented STRM
60
+ trailing double, verified as the stream-relative capture start;
61
+ `start_time_ns` is now anchored with it so reported spans match the
62
+ recorded data.
63
+ - `scripts/ci-local.sh`: local CI-parity gate incl. cross-target and
64
+ Linux-VM coverage of the OS-gated native-SDK modules (the VM step
65
+ runs CI's exact ubuntu-leg commands).
66
+ - `benches/deinterleave_dual_iq`: criterion bench pinning the
67
+ dual-channel demux hot path (~1.5 Gsamples/s per core baseline).
68
+ - Parser-robustness property tests: `RtsaSource::open` on arbitrary
69
+ byte soup and on structured DSFH/DSFT chunk soup must never panic.
70
+
71
+ ### Fixed
72
+ - RTSA chunk parsing verified against the vendor spec and real
73
+ captures: STRT alignment padding and size-versioned tail offsets,
74
+ SPRV/ANTA fixed-field sizes, single standard STRM layout, official
75
+ `DSST`/`DSSU`/`DSPT` enum numbering with `Unknown` degradation, and
76
+ `mEndTime` treated as stream-relative duration.
77
+ - Compressed `DSPT_SPECTRA` chunks now error explicitly instead of
78
+ returning compressed bytes reinterpreted as f32 spectra.
79
+ - Native SDK: corrupt-packet guards consume the packet before erroring
80
+ (previously a corrupt head-of-queue packet livelocked every
81
+ subsequent read); carry buffers are flushed on `stop_streaming`.
82
+
83
+ ## [v0.3.5] - 2026-08-06
84
+
85
+ Documentation-and-polish release: every Markdown doc was audited against
86
+ the implementation and corrected. No library API changes (the only `src/`
87
+ edits are doc comments).
88
+
89
+ ### Changed
90
+ - DESIGN.md restructured and de-duplicated; verified claim-by-claim
91
+ against the code (external `sdr-source` crate not "vendored", settle
92
+ timing attribution, real module/function names, HTTP format defaults,
93
+ feature-gating and platform caveats).
94
+ - README corrected: install snippet version, builder capabilities,
95
+ `futuresdr` feature-gating of `HttpSource`/`HttpSink`, Windows/Linux-only
96
+ `native-sdk` note, `AARONIA_SDK_PATH` macOS scope, links to the `docs/`
97
+ specifications and changelog.
98
+ - CONTRIBUTING now matches CI: pinned Miri nightly, the `cargo-deny` /
99
+ `cargo-hack` / `cargo-machete` gates, mutants/ASAN cron schedules,
100
+ toolchain floor (edition 2024), and the full test-suite inventory.
101
+ - docs/HTTPSPEC, docs/SDKSPEC, docs/FILESPEC corrected against the
102
+ implementation (wire-format details, endpoint coverage including
103
+ `GET /samples` and the `POST /sample` TX push, `sweepsa` open strings,
104
+ real API paths, enum numeric values, parser limits) with explicit
105
+ "known divergence" notes where only hardware can settle the truth.
106
+ - Clarified that HTTP retuning uses the license-free `/control` endpoint;
107
+ the "Remote Config" license gates only `/remoteconfig` writes
108
+ (stale doc comment on `set_center_frequency` fixed to match).
109
+ - Examples cleaned up: emoji removed, `noaa_scanner` doc header rewritten,
110
+ `http_iq_quickstart` now takes the server URL as an argument instead of
111
+ hardcoding a private hostname.
112
+ - Five assertion-free tests now actually assert.
113
+
114
+ ### Fixed
115
+ - Stale comments: `native_sdk_load` symbol count, `spec_coverage` row
116
+ pointing at a renamed property test, bench comments describing mmap and
117
+ inverted `?scale=` semantics (the `parse_int16_packet` bench now passes
118
+ an encode-side scale, matching live-verified server behavior).
119
+
120
+ ## [v0.3.4] - 2026-07-31
121
+
122
+ ### Fixed
123
+ - **Native SDK: the tail of every oversized IQ packet was discarded.**
124
+ `NativeSdkSource::read_samples` copied `min(packet.num, max_samples)`
125
+ samples and then consumed the *whole* packet via
126
+ `AARTSAAPI_ConsumePackets`. The SDK picks its own packet size, unrelated
127
+ to the caller's `max_samples`, so anything past the request was lost
128
+ permanently — consuming returns the buffer to the SDK. `block_size` is
129
+ caller-set with only a 1024 floor, so a modest block size against
130
+ `AARTSAAPI_MEMORY_MEDIUM` packets left the IQ stream full of holes, which
131
+ breaks the phase continuity downstream ZC correlation and CFO tracking
132
+ depend on. Excess samples are now retained in the (previously dead)
133
+ `sample_buffer` and returned by subsequent calls, mirroring the HTTP
134
+ path's remainder handling. See `read_samples`' new "Carry-over" section;
135
+ `get_sample_buffer_size()` now reports something real.
136
+ - **Native SDK: `read_samples(.., 0)` destroyed a packet.** A zero-sized
137
+ request used to fetch a packet, copy nothing out of it, and consume it.
138
+ It now returns early without polling.
139
+ - **`sdk_sink`: TX bursts were scheduled at the Unix epoch.** The FutureSDR
140
+ sink block built every `TxBurst` with `start_time: 0.0`, contradicting
141
+ `TxBurst`'s own documentation that the device schedules against its
142
+ master stream clock. Timestamps are now derived from
143
+ `AARTSAAPI_GetMasterStreamTime` with a 10 ms lead, falling back to the
144
+ documented `PUSH` immediate-dispatch flag when the clock is unreadable.
145
+ Hardware-unverified, like the rest of the TX path.
146
+ - **`http_source`: reported buffer capacity disagreed with the enforced
147
+ one.** The cap was written twice — `saturating_mul(2).max(1)` where it
148
+ was enforced, a bare `* 2` where `StreamStats` reported it. With
149
+ `buffer_size: 0` (which the builder accepts) `buffer_level` could exceed
150
+ `buffer_capacity`, and a consumer computing a fill ratio divided by zero.
151
+ - **Diagnostic previews truncated on byte offsets, not char boundaries.**
152
+ `&text[..n]` panics when `n` lands inside a multi-byte UTF-8 character,
153
+ and these previews print raw RTSA responses carrying device/mission/
154
+ antenna names straight from user configuration — one non-ASCII character
155
+ in a mission title was enough to panic a diagnostic run.
156
+
157
+ ### Changed
158
+ - **`cargo clippy --all-targets` now compiles on a default checkout.**
159
+ `tests/http_sink_test.rs` needs the `futuresdr` feature but declared no
160
+ `required-features`, so the documented lint command failed with `cannot
161
+ find module or crate futuresdr`.
162
+ - **`SdkConfig::timeout` and `SdkSinkConfig::timeout` documented as
163
+ inert.** Neither is read by any code path; the only timeout in force on
164
+ the read path is the fixed 500 ms `NativeSdkSource::READ_POLL_DEADLINE`.
165
+ Both fields are kept — they are `pub` on a published crate — but now say
166
+ so. Honouring them would change `read_samples`' signature, so that is
167
+ left as a deliberate decision for a future major.
168
+
169
+ ## [v0.3.3] - 2026-07-31
170
+
171
+ ### Fixed
172
+ - **`broad_search_for_chunk` could loop forever.** The RTSA chunk scan did
173
+ not terminate at EOF, so a file whose signature was absent and whose
174
+ search bound exceeded its length spun indefinitely — reachable from
175
+ `RtsaSource::open` on an untrusted file.
176
+ - **`verify_config_changes` leaked probed device state.** A failed
177
+ read-back returned early without restoring the parameter it had just
178
+ offset, leaving the device 1 dB from where it started. The restore now
179
+ runs on every path out, and logs when both the read-back and the restore
180
+ fail.
181
+
182
+ ## [v0.3.2] - 2026-07-13
183
+
184
+ ### Added
185
+ - **`DwellAdvice::channel_override` is now honored**, and the Remote Config
186
+ license gate was dropped from hop mode — channel hopping works on
187
+ unlicensed devices via the free `/control` endpoint.
188
+
189
+ ## [v0.3.1] - 2026-07-11
190
+
191
+ ### Fixed
192
+ - **Decompression: out-of-range `compression_factor` rejected up front.**
193
+ `Decompressor::decompress` now rejects `compression_factor > 31` (the
194
+ documented range per `docs/FILESPEC.md`, "1 to 31 for lossy factor")
195
+ instead of letting it reach `dequantize`'s `1i32 << (compression_factor -
196
+ 1)`, which overflows the sign bit at 32 (silently producing a negative
197
+ quantizer) and panics on overflow at 33+ in debug builds. This value can
198
+ originate from a parsed HTTP-stream packet, so a malformed/corrupt header
199
+ could previously reach the panic.
200
+ - **Decompression: over-produced coefficients truncated, not just
201
+ under-produced ones.** `decompress` already zero-padded a coefficient
202
+ stream shorter than `num_rows * num_cols`; it now also truncates a longer
203
+ one, since `wave_transform_step` derives its own row count from the raw
204
+ buffer length rather than the caller's declared dimensions and would
205
+ otherwise silently operate on more rows than requested.
206
+ - **Aaronia capture thread now panic-guarded**, matching the driver crates
207
+ (USRP/HackRF/Pluto): the `AaroniaSdrSource::start` capture thread body is
208
+ wrapped in `catch_unwind`, so a panic inside it is logged instead of
209
+ silently unwinding the thread with no diagnostic.
210
+ - **HTTP overrun detection wired to `IqPacket::overrun`.** The HTTP reader
211
+ task now runs a `DropDetector` over each packet's timing metadata; a
212
+ detected gap latches a flag that `AaroniaSource::take_overrun()` surfaces
213
+ on the next `read_samples` call. `single_channel_pump`/`hop_pump` in
214
+ `sdr_source_impl.rs` now report real overrun status instead of
215
+ hardcoding `false`. The native-SDK and file backends still report `false`
216
+ (unchanged).
217
+ - **`aaronia_source_read_samples` (C API) copy bound hardened.** The FFI
218
+ sample-copy now clamps to `temp_samples.len()` in addition to the
219
+ caller-supplied capacity, so a hypothetical future miscount from
220
+ `read_samples` can't cause an out-of-bounds read; added a compile-time
221
+ size assertion between `FfiComplex` and `Complex32` guarding the
222
+ reinterpret cast.
223
+ - **`orecchiette-sdr-source-rs` dependency floor** bumped from `0.1.0` to
224
+ `0.1.2` to match every sibling crate's declared floor.
225
+
226
+ ## [v0.3.0] - 2026-07-11
227
+
228
+ ### Removed
229
+ - **BREAKING: `file_performance` module removed** (`MmapRtsaReader`,
230
+ `AdaptiveChunkReader`, `AccessStats`, `CacheStats`, `ChunkType`, and the
231
+ `memmap2` dependency). This tiered-cache, adaptive-read-ahead
232
+ memory-mapped reader was never wired into the crate's actual file-reading
233
+ path — `RtsaSource` (the real hot path, used by every file-source
234
+ consumer) has always read via buffered `std::io` and never touched this
235
+ module. It had zero callers anywhere in the crate outside its own tests.
236
+ If you were depending on these types directly, buffered access through
237
+ `RtsaSource` covers the same file-reading needs; there is no drop-in
238
+ replacement for the standalone mmap/cache API itself.
239
+
240
+ ### Fixed
241
+ - **`HttpSourceBuilder` doc comment misplacement:** the "no-op kept for
242
+ backward compatibility" doc comment was attached to `with_shared_stats`
243
+ (which is not a no-op — it wires up the retune/stats-sharing mechanism)
244
+ instead of `with_native_sdk` (the actual no-op immediately below it).
245
+ Moved to the correct method and gave `with_shared_stats` an accurate doc.
246
+ - **`HttpSink` dropped-sample accounting:** `push_batch` now counts a batch
247
+ as dropped when the background sender task's channel is closed (e.g. the
248
+ task panicked), not just on a failed/timed-out HTTP push. Previously this
249
+ failure mode silently discarded samples without incrementing
250
+ `dropped_samples()` — the documented way to detect a persistently broken
251
+ TX link. `HttpSink` also now aborts its background sender task on drop
252
+ instead of relying on the channel closing to signal it.
253
+
254
+ ### Changed
255
+ - **Native SDK error handling finished:** the last four `NativeSdkClient`
256
+ methods that still hand-rolled their own `AARTSAAPI_Result` check
257
+ (`enum_device`, `config_first`, `config_next`, `get_packet`) now route
258
+ through the `check_res`/`Error::SdkApi` path introduced in v0.2.6,
259
+ matching every other method in the client. Behavior is unchanged for
260
+ callers (`AARTSAAPI_EMPTY` still maps to `Ok(None)`/`Ok(false)`); failures
261
+ now carry a structured `SdkError` instead of a formatted string.
262
+ - **De-duplicated `device_family`/`device_open_mode`:** `SdkConfig` and
263
+ `SdkSinkConfig` shared byte-for-byte identical device-type-splitting
264
+ logic (differing only in the default open-mode suffix, `raw` vs.
265
+ `iqtransmitter`). Extracted into `native_sdk::split_device_type`; both
266
+ public methods keep their existing signatures.
267
+
268
+ ## [v0.2.6] - 2026-07-10
269
+
270
+ ### Added
271
+ - **Native SDK Transmitter (TX):** Added transmit capabilities through the C++ Native SDK (`SdkSink` and `SdkSinkConfig`). This provides TX path parity with the `SdkSource` and includes FutureSDR integration (`SdkSinkBlock`).
272
+ - **Examples:** Added `examples/native_sdk_transmit.rs` to demonstrate programmatic device configuration, master stream time-driven packet pacing, and sending a continuous LoRa-like CSS up-chirp.
273
+
274
+ ### Changed
275
+ - **Granular Errors:** Migrated the opaque `Error::Sdk(String)` to a structured `Error::SdkApi { operation: &'static str, code: SdkError }` to allow programmatic error recovery.
276
+ - **Warning Isolation:** The Native SDK C++ FFI bindings now correctly categorize and log warnings (codes with the `0x40000000` bit set) rather than escalating them to fatal errors, mapping closely to the official Aaronia Java/C++ driver behaviors.
277
+
278
+ ## [v0.2.4] - 2026-07-10
279
+
280
+ ### Performance
281
+ - **HTTP stream parser:** binary formats (int16/float16/float32) now
282
+ deserialize `PacketMetadata` straight from the header bytes instead of
283
+ building — and discarding — a full `serde_json::Value` DOM per packet.
284
+ `parse_int16_packet` improves ~41% at 256 samples/packet down to ~6% at
285
+ 64k (the header cost amortizes over larger payloads). The pure-JSON
286
+ stream still uses the DOM, since it reads sample values from it.
287
+ - **Float32 IQ parse:** on little-endian hosts the payload is bulk-copied
288
+ in one `memcpy` into an aligned `Vec<Complex32>` instead of decoding each
289
+ `f32` individually (portable per-element fallback on big-endian).
290
+ - **RTSA file replay:** IQ/spectra sample reads now do one bulk `read_exact`
291
+ + in-memory decode instead of two `read_f32`/`read_i16` calls per sample.
292
+ - **Native SDK `read_samples`** logs per-read at `trace!` instead of
293
+ `info!`, so an enabled info subscriber no longer pays formatting cost on
294
+ the hot path.
295
+ - Minor: `HttpSource` bulk-`extend`s its sample ring buffer rather than
296
+ pushing element-by-element; the unified HTTP source reuses one
297
+ connection-pooled client for its reader task instead of building a
298
+ second.
299
+
300
+ ### Fixed
301
+ - **Native SDK `read_samples` soundness:** the wide-stride (`stride > 2`) IQ
302
+ gather path underflowed `usize` when `max_samples == 0`, handing
303
+ `slice::from_raw_parts` a ~`usize::MAX` length (UB). It now skips the empty
304
+ case, `packet.num` is bound-checked before the `* 2` to avoid an `i64`
305
+ overflow on a malformed packet count, and `packet.stride` is sanity-bounded
306
+ (a corrupt oversized stride would otherwise size an out-of-bounds slice).
307
+ - **`AaroniaSource` HTTP streaming-task leak:** the background `/stream` reader
308
+ is now aborted on `stop_streaming()` and on drop, instead of lingering
309
+ (holding the open connection, so the device kept streaming) until the next
310
+ packet arrived.
311
+ - **`Decompressor::decompress`** rejects zero `num_rows`/`num_cols` instead of
312
+ spinning the inverse-wavelet loop forever / dividing by zero.
313
+ - **`MmapRtsaReader::read_chunk`** bounds check uses `checked_add`, so a
314
+ pathological offset can't wrap past it into an out-of-bounds slice.
315
+ - **`HttpSourceBuilder`** default reference level corrected from `+20 dBm` to
316
+ `-20 dBm` (matching `AaroniaConfig`); the old default desensitized the
317
+ receiver. The mislabeled "dB" unit comment is fixed too.
318
+ - **`HttpSource::work`** now requests re-scheduling (`io.call_again`) after a
319
+ stream-error reconnect.
320
+
321
+ ### Changed
322
+ - **`HttpSink` `timeout_ms`** is now honored: each transmit push is bounded by
323
+ it (timed-out batches count as dropped) and `HttpSinkBuilder::timeout_ms`
324
+ exposes the knob. Previously the value was silently ignored.
325
+ - README install snippet updated to the current `0.2` version line.
326
+
327
+ ## [v0.2.3] - 2026-07-05
328
+
329
+ ### Fixed
330
+ - **HTTP source now tunes the device on startup.** `init_http_source` was opening `/stream` without first sending a `/control` capture-configuration request, so the SDR always streamed whatever frequency the RTSA Suite happened to be configured to (typically 300 MHz) — completely ignoring the caller's `center_frequency` and `span_frequency`. A `configure_capture` call is now issued before the stream is opened, matching the Native SDK path's behaviour.
331
+
332
+ ### Changed
333
+ - `http_iq_quickstart` example: added CLI arguments for frequency and sample rate, periodic signal-power logging to stderr, and fixed a clippy `needless_borrow` lint.
334
+
335
+ ## [v0.2.2] - 2026-07-05
336
+
337
+ ### Changed
338
+ - Dropped explicit MSRV (Minimum Supported Rust Version) policy to track the latest `stable` Rust compiler.
339
+ - Updated `.github/workflows/ci.yml` to compile tests, clippy, and coverage against `stable` instead of hardcoding an older MSRV. This prevents transitive dependencies that bump their MSRVs from breaking CI.
340
+
341
+ ## [v0.2.1] - 2026-07-05
342
+
343
+ ### Added
344
+ - Native SDK support for configurable receiver channel (`Rx1`, `Rx2`, `Rx1And2`).
345
+ - Support for setting HTTP wire format and stream scale in `AaroniaConfig` and `AaroniaSourceBuilder`.
346
+ - Updated `README.md` with a quickstart guide and more detailed examples.
347
+
348
+ ### Fixed
349
+ - Fixed CI build failures by bumping MSRV to 1.86 and allowing the `Zlib` license in cargo-deny.
350
+ - Changed default HTTP streaming format to `Float32` instead of `Json` to fix high-bandwidth crashes.
351
+ - Resolved `FutureSDR` block execution deadlocks inside `HttpSink` by offloading `reqwest` synchronous HTTP I/O into a detached `tokio` background task.
352
+
353
+ ## [v0.1.1] - 2026-07-03
354
+
355
+ ### Changed
356
+ - Disable `futuresdr` on docs.rs and bump to 0.1.1
357
+ - Fix license badge by using GitHub endpoint instead of crates.io
358
+ - Update Cargo.lock for 0.1.1
359
+
360
+ ## [v0.1.0] - 2026-07-03
361
+
362
+ ### Added
363
+ - Initial release.
@@ -0,0 +1,184 @@
1
+ # Contributing to sdr-aaronia-rs
2
+
3
+ First off, thank you for considering contributing to `sdr-aaronia-rs`! This document explains how the test suite is organized, which tools you'll need, and how to verify your changes locally before opening a pull request.
4
+
5
+ **Toolchain:** the crate uses the 2024 edition, so you need Rust 1.85 or newer. There is no pinned `rust-toolchain.toml`; CI tracks the latest `stable`.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ git clone https://github.com/isaacbentley/sdr-aaronia-rs.git
11
+ cd sdr-aaronia-rs
12
+
13
+ # Run the standard validation suite
14
+ cargo test # unit + integration + properties (default features)
15
+ cargo test --test spec_coverage -- --nocapture # spec inventory report
16
+ cargo clippy --all-features --all-targets -- -D warnings
17
+ cargo fmt --all --check
18
+ ```
19
+
20
+ Note that a plain `cargo test` builds with the default features; suites gated on non-default features (e.g. `http_sink_test`, which needs `futuresdr`) are skipped. CI runs `cargo test --all-features`.
21
+
22
+ ## Pre-push: mirror CI locally
23
+
24
+ Before pushing (or opening a PR), run the CI-parity script:
25
+
26
+ ```bash
27
+ scripts/ci-local.sh
28
+ ```
29
+
30
+ It reproduces the **exact** command each GitHub Actions job runs — fmt,
31
+ clippy, `cargo test --all-features`, `cargo deny --all-features check`,
32
+ `cargo hack check --each-feature --no-dev-deps`, `cargo machete`, and
33
+ the pinned-nightly Miri module suite — and fails fast with install
34
+ hints for any missing tool. Skip individual steps with e.g.
35
+ `SKIP="miri hack" scripts/ci-local.sh` when iterating.
36
+
37
+ The `--all-features` flags matter: several CI jobs validate the
38
+ *all-features* dependency graph and feature set, so a default-features
39
+ `cargo test` / `cargo deny check` passing locally does **not** imply CI
40
+ will pass. If you edit `.github/workflows/ci.yml`, update
41
+ `scripts/ci-local.sh` in the same commit (and vice versa), and run
42
+ `actionlint .github/workflows/ci.yml`.
43
+
44
+ Two steps exist because macOS builds skip the OS-gated native-SDK
45
+ modules entirely (`cfg(any(windows, linux))`), which CI's ubuntu and
46
+ windows legs do compile:
47
+
48
+ - **cross** — `cargo clippy --target x86_64-unknown-linux-gnu` /
49
+ `x86_64-pc-windows-msvc` with `--features native-sdk`, via the rustup
50
+ `stable` toolchain (install the targets with `rustup target add
51
+ <triple> --toolchain stable`). Lib only — the gated *test* code needs
52
+ a real Linux environment.
53
+ - **vm** — runs `cargo test --features native-sdk --lib` inside a Linux
54
+ VM via Apple's `container` CLI (`container system start` first; the
55
+ step auto-skips when the tooling isn't running). This is the only
56
+ local step that compiles and runs `#[cfg(test)]` code inside the
57
+ gated modules — a missed struct field in exactly such a test once
58
+ shipped red to CI past every other local check.
59
+
60
+ Tiers 1–4 below are the core of the suite; the later tiers are optional but recommended if your pull request touches the parser, decompressor, or FFI surface.
61
+
62
+ ## Test Pyramid
63
+
64
+ The crate is modeled on a layered test pyramid. Each tier catches a different class of bug; together they form the "definition of done" for our releases.
65
+
66
+ ### 1. Unit Tests
67
+
68
+ Located alongside the code they test in `src/**/*.rs`. These cover format conversion, frequency parsing, builder defaults, struct sizes, and other basic utilities.
69
+
70
+ Run: `cargo test --lib`
71
+
72
+ ### 2. Integration Tests (`tests/integration_test.rs`)
73
+
74
+ End-to-end tests against bundled `.rtsa` captures stored in Git LFS. The fixtures gracefully skip when LFS content isn't available. To run them fully, ensure you pull the LFS files:
75
+
76
+ ```bash
77
+ git lfs pull
78
+ cargo test --test integration_test
79
+ ```
80
+
81
+ Beyond the tiers named here, `tests/` also contains focused suites for the HTTP mock server (`http_mock_test.rs`), the HTTP sink (`http_sink_test.rs`, requires `futuresdr`), the C API (`c_api_test.rs`), the `sdr-source` implementation (`sdr_source_impl_test.rs`), RTSA negative cases (`rtsa_negative_test.rs`), CW fixtures (`test_cw_mag.rs`, `test_cw_meta.rs`), SDK library loading (`native_sdk_load.rs`), and opt-in live-hardware smoke tests (`live_smoke.rs`). All of these run as part of `cargo test` where their features and environment allow.
82
+
83
+ ### 3. Property Tests (`tests/properties.rs`)
84
+
85
+ We use `proptest` to verify invariants of the parser, decompressor, and validator surfaces.
86
+
87
+ Run: `cargo test --test properties`
88
+
89
+ For a deeper verification pass (useful before submitting a PR):
90
+ ```bash
91
+ PROPTEST_CASES=4096 cargo test --test properties --release
92
+ ```
93
+
94
+ ### 4. Invariant-Coverage Inventory (`tests/spec_coverage.rs`)
95
+
96
+ A single page enumerating which documented invariants the test suite enforces. **If you are adding a new invariant-bound test, please add a row to `ENFORCED` in `tests/spec_coverage.rs`.**
97
+
98
+ Run: `cargo test --test spec_coverage -- --nocapture`
99
+
100
+ ### 5. Miri (Nightly)
101
+
102
+ Catches undefined behavior in safe Rust code. Because Miri refuses to interpret FFI, we only run it on the pure-Rust modules. CI pins `nightly-2026-01-01` because newer nightlies changed the `fadd_fast` intrinsic signature, which breaks the `futuredsp` dependency's build — use the same pin locally:
103
+
104
+ ```bash
105
+ rustup toolchain install nightly-2026-01-01 --component miri
106
+ cargo +nightly-2026-01-01 miri test --lib decompression
107
+ cargo +nightly-2026-01-01 miri test --lib http_streaming
108
+ cargo +nightly-2026-01-01 miri test --lib utils
109
+ ```
110
+
111
+ ### 6. Code Coverage
112
+
113
+ Code coverage is reported to Codecov on PRs targeting `main` (uploaded from the Linux CI leg). Coverage is informational only and will not fail your build. To check coverage locally:
114
+
115
+ ```bash
116
+ cargo install cargo-llvm-cov
117
+ cargo llvm-cov --html
118
+ open target/llvm-cov/html/index.html # xdg-open on Linux, start on Windows
119
+ ```
120
+
121
+ ### 7. Criterion Benchmarks (`benches/`)
122
+
123
+ Three benchmark harnesses track the hot paths across releases. Benchmarks are run on demand to investigate performance implications of a PR.
124
+
125
+ ```bash
126
+ cargo bench # Run all benchmarks
127
+ cargo bench --bench parse_int16_packet # Run a specific harness
128
+ ```
129
+
130
+ ### 8. Mutation Testing
131
+
132
+ `cargo mutants` runs in CI on a weekly cron (Mondays) and on manual dispatch, advisory-only. To run it locally:
133
+
134
+ ```bash
135
+ cargo install cargo-mutants
136
+ cargo mutants --no-shuffle --in-place=false --timeout=180
137
+ ```
138
+
139
+ ### 9. ASAN / UBSAN
140
+
141
+ The C FFI boundary is exercised under AddressSanitizer and UndefinedBehaviorSanitizer via `tests/asan/c_smoke.c`. CI runs this on a weekly cron (Tuesdays).
142
+
143
+ ```bash
144
+ rustup toolchain install nightly
145
+ rustup component add rust-src --toolchain nightly
146
+ # On Debian/Ubuntu, the sanitizer runtimes are also needed:
147
+ sudo apt-get install llvm libubsan1
148
+ bash tests/asan/run_asan.sh
149
+ ```
150
+ *(Note: ASAN scripts currently require a Linux environment.)*
151
+
152
+ ### 10. Dependency Hygiene (CI-enforced)
153
+
154
+ Three additional CI jobs gate every PR; run their tools locally if your change touches dependencies or features:
155
+
156
+ ```bash
157
+ cargo install cargo-deny cargo-hack cargo-machete
158
+ cargo deny check # licenses/advisories/bans, driven by deny.toml
159
+ cargo hack check --each-feature --no-dev-deps # every feature combination compiles
160
+ cargo machete # no unused dependencies
161
+ ```
162
+
163
+ ## Adding a New Spec Invariant
164
+
165
+ When you encounter a documented invariant in `docs/*.md` that the test suite doesn't currently enforce:
166
+ 1. Name the test `prop_<short_description>` or `spec_<area>_<behavior>`.
167
+ 2. Add a `///` doc comment describing the invariant the test pins.
168
+ 3. Add a row to the `ENFORCED` table in `tests/spec_coverage.rs`.
169
+ 4. Run `cargo test --test spec_coverage -- --nocapture` and confirm your row appears.
170
+
171
+ ## Code Style
172
+
173
+ We use standard `rustfmt` defaults. Please run `cargo fmt --all` before pushing.
174
+
175
+ Clippy is run with `-D warnings` in CI. If a lint is genuinely wrong for the situation, use a targeted `#[allow(...)]` with a brief comment explaining why. `unsafe` blocks carry the usual `// SAFETY:` invariant comments.
176
+
177
+ ## Pull Requests
178
+
179
+ - **Commit messages:** Conventional-commits style is preferred but not required. Describe *why* the change is needed and *what* it changes.
180
+ - **Templates:** Please fill out the Pull Request template when opening a PR. It includes checkboxes for the CI validations and a mandatory AI-usage disclosure section.
181
+
182
+ ## License
183
+
184
+ By contributing, you agree your contributions will be licensed under GPL-3.0-or-later, the same as the rest of the project.