python-aaronia 0.7.5__tar.gz → 0.7.6__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.
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/CHANGELOG.md +85 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/Cargo.lock +2 -2
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/Cargo.toml +1 -1
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/PKG-INFO +1 -1
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/HTTPSPEC.md +6 -1
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/packaging/homebrew/soapy-aaronia.rb +1 -1
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/python-aaronia/Cargo.toml +1 -1
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/file_source.rs +64 -11
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/http_endpoints.rs +398 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/http_source.rs +468 -102
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/lib.rs +3 -2
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/utils.rs +91 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/.cargo/config.toml +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/.gitattributes +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/.gitignore +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/CONTRIBUTING.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/DESIGN.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/LICENSE +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/PLUGINS.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/README.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/benches/decompress_block.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/benches/deinterleave_dual_iq.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/benches/parse_int16_packet.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/benches/rtsa_open_and_read.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/deny.toml +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/APPS.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/FILESPEC.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/QUICKSTART.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/SDKSPEC.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/USAGE.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/docs/VERIFICATION.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/channel_hopping.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/device_control.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/dump_metadata.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/http_iq_quickstart.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/native_sdk_basic.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/native_sdk_transmit.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/noaa_scanner.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/python_arrow_example.py +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/read_rtsa_file.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/examples/soapy_python_example.py +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/include/aaronia.h +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/packaging/homebrew/README.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/pyproject.toml +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/python-aaronia/README.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/python-aaronia/aaronia.pyi +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/python-aaronia/src/lib.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/python-aaronia/test_basic.py +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/scripts/ci-local.sh +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/scripts/validate-iq-live.py +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/AaroniaSoapyDevice.cpp +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/AaroniaSoapyDevice.hpp +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/CMakeLists.txt +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/README.md +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/Registration.cpp +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeCache.txt +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeCXXCompiler.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeSystem.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/a.out +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/apple-sdk.cpp +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/CMakeConfigureLog.yaml +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/CMakeDirectoryInformation.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/CMakeRuleHashes.txt +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/InstallScripts.json +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/Makefile.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/Makefile2 +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/TargetDirectories.txt +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/DependInfo.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/build.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/cmake_clean.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/compiler_depend.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/compiler_depend.ts +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/depend.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/flags.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/link.txt +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/progress.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/cmake.check_cache +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/progress.marks +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/DependInfo.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/build.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/cmake_clean.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/compiler_depend.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/compiler_depend.ts +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/progress.make +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/Makefile +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/build-test/cmake_install.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/packaging/install.ps1 +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/packaging/install.sh +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/soapy-aaronia/print.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/c_api.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/decompression.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/detection.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/error.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/http_sink.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/http_streaming.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/native_sdk.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/sdk_sink.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/sdk_source.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/sdr_source_impl.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/seify_impl.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/unified_sink.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/src/unified_source.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/test.cmake +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/c_api_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/http_mock_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/http_resilience_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/http_sink_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/integration_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/live_smoke.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/native_sdk_load.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/properties.proptest-regressions +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/properties.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/rtsa_negative_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/sdr_source_impl_test.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/spec_coverage.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/test_cw_mag.rs +0 -0
- {python_aaronia-0.7.5 → python_aaronia-0.7.6}/tests/test_cw_meta.rs +0 -0
|
@@ -4,6 +4,91 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.7.6] - 2026-08-15
|
|
8
|
+
|
|
9
|
+
All of this is about the FutureSDR `HttpSource` block (the `futuresdr`
|
|
10
|
+
feature), whose streaming path turned out to be losing most of the
|
|
11
|
+
stream. Found and measured against a live SPECTRAN V6 ECO running a
|
|
12
|
+
49 MHz survey for a trunking decoder.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **The sample buffer guillotined every packet bigger than itself.**
|
|
16
|
+
The capacity trim runs as each packet lands, and with capacity fixed
|
|
17
|
+
at `buffer_size * 2` a device sending 49k-sample packets into a
|
|
18
|
+
16384-sample capacity lost ~75% of every packet before the consumer
|
|
19
|
+
was offered any of it. Measured live: 61.4 MS/s at the device,
|
|
20
|
+
0.33 MS/s reaching the pipeline, and digital decode unable to hold
|
|
21
|
+
frame sync. The capacity now floors at four times the largest packet
|
|
22
|
+
observed, learned from the stream; the floor only ever raises the
|
|
23
|
+
configured value.
|
|
24
|
+
- **The initial tune could claim success while the device never
|
|
25
|
+
moved.** `/control` answers `success=true` whether or not any block
|
|
26
|
+
applies the command — the same device has been measured both
|
|
27
|
+
honouring and ignoring the identical full-tuple payload in different
|
|
28
|
+
mission states. The block's start-up tune now writes the real field
|
|
29
|
+
names (`centerfreq0`, `decimation0`, `reflevel0`) via `/remoteconfig`
|
|
30
|
+
to the block discovered by walking the config tree, then reads them
|
|
31
|
+
back and warns about anything that did not take. Where no such block
|
|
32
|
+
exists (an IQ-demodulator mission exposes different fields), it falls
|
|
33
|
+
back to the old `/control` capture command and says the result is
|
|
34
|
+
unverified. The tune is one-shot: stream restarts after an external
|
|
35
|
+
retune no longer re-push the source's stale target over it.
|
|
36
|
+
- **Launching an app no longer overwrites the operator's gain.** The
|
|
37
|
+
builder's reference level was a bare number pushed on every start, so
|
|
38
|
+
merely starting a flowgraph reset the device to the builder default.
|
|
39
|
+
It is now optional and left untouched unless the caller set one.
|
|
40
|
+
- **`work()` spun.** The runtime ran it whenever the output port had
|
|
41
|
+
any room — measured at 106,000–390,000 calls a second averaging 14
|
|
42
|
+
free samples each — burning ~60% of a core and starving the
|
|
43
|
+
downstream block. The output port now requires a worthwhile block of
|
|
44
|
+
room before `work()` runs; call rate dropped to ~14/s.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- **The HTTP socket is drained by a dedicated task**, not inside
|
|
48
|
+
`work()`. The socket used to be read only while the scheduler happened
|
|
49
|
+
to run the block, which capped throughput at a tenth of what `curl`
|
|
50
|
+
pulls from the same endpoint. A background task now pushes chunks
|
|
51
|
+
into a bounded channel (~10 MB), whose fill is the backpressure point:
|
|
52
|
+
consumer falls behind → channel fills → reader blocks → TCP flow
|
|
53
|
+
control stops the server. Measured at a 49 MHz span: 1.8 → ~7–9 MS/s.
|
|
54
|
+
The honest ceiling is the link (~57 MB/s ≈ 14 MS/s as float32);
|
|
55
|
+
61.44 MS/s over this transport is 2 Gbit/s and not reachable.
|
|
56
|
+
- Buffer-overflow drops are now counted and logged geometrically
|
|
57
|
+
rather than once per occurrence, which at wide span was 5040 log
|
|
58
|
+
lines in 25 seconds for one piece of information.
|
|
59
|
+
- File playback decodes little-endian cf32 with a zero-copy read
|
|
60
|
+
straight into the sample buffer; the explicit per-sample decode
|
|
61
|
+
remains for big-endian hosts, and a round-trip test pins the two to
|
|
62
|
+
identical output.
|
|
63
|
+
|
|
64
|
+
### Fixed (in review, before this release shipped)
|
|
65
|
+
- The refill loop kept iterating through its 50 ms idle sleeps when the
|
|
66
|
+
channel was empty, holding samples already in the buffer for up to
|
|
67
|
+
800 ms before producing them. It now flushes immediately and sleeps
|
|
68
|
+
only when there is nothing to flush.
|
|
69
|
+
- A parse error mid-stream reconnected without reaping the reader task,
|
|
70
|
+
which on a stalled socket keeps holding a server connection — on the
|
|
71
|
+
free licence, the one connection the reconnect needs.
|
|
72
|
+
- Two comments overstated the `/control` finding as "this endpoint does
|
|
73
|
+
not carry these fields on a Spectran V6". Measured today, the same
|
|
74
|
+
device honours the identical full-tuple payload — the accurate claim
|
|
75
|
+
is that `success=true` proves nothing either way, which is the reason
|
|
76
|
+
the verified `/remoteconfig` path exists. Where no `centerfreq0`
|
|
77
|
+
block is found, the initial tune now falls back to `/control` rather
|
|
78
|
+
than silently not tuning (an IQ-demodulator mission exposes
|
|
79
|
+
`centerfreq`, not `centerfreq0`).
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
- `iq_sample_rate_for_decimation_index`, `decimation_index_for_rate`
|
|
83
|
+
and `decimation_index_for_bandwidth` in `utils`: the RTSA "Span"
|
|
84
|
+
enum (`Full` … `1 / 512`) mapped onto the sample-rate ladder and
|
|
85
|
+
back, so a requested span becomes the enum index the device actually
|
|
86
|
+
takes.
|
|
87
|
+
- `HttpEndpointsClient::apply_capture_config` — retune via
|
|
88
|
+
`/remoteconfig` with read-back confirmation — plus
|
|
89
|
+
`find_block_name_with_field`, which discovers the receiver block by
|
|
90
|
+
the field the write will target instead of assuming its category.
|
|
91
|
+
|
|
7
92
|
## [v0.7.5] - 2026-08-13
|
|
8
93
|
|
|
9
94
|
### Added
|
|
@@ -3027,7 +3027,7 @@ dependencies = [
|
|
|
3027
3027
|
|
|
3028
3028
|
[[package]]
|
|
3029
3029
|
name = "python-aaronia"
|
|
3030
|
-
version = "0.7.
|
|
3030
|
+
version = "0.7.6"
|
|
3031
3031
|
dependencies = [
|
|
3032
3032
|
"arrow",
|
|
3033
3033
|
"num-complex",
|
|
@@ -3556,7 +3556,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
|
3556
3556
|
|
|
3557
3557
|
[[package]]
|
|
3558
3558
|
name = "sdr-aaronia-rs"
|
|
3559
|
-
version = "0.7.
|
|
3559
|
+
version = "0.7.6"
|
|
3560
3560
|
dependencies = [
|
|
3561
3561
|
"anyhow",
|
|
3562
3562
|
"bitflags 2.13.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name = "sdr-aaronia-rs"
|
|
3
3
|
description = "Unified Rust interface for Aaronia Spectran Spectrum Analyzers / SDRs, featuring Python bindings, a SoapySDR plugin, HTTP streaming, and native SDK support."
|
|
4
4
|
license = "GPL-3.0-or-later"
|
|
5
|
-
version = "0.7.
|
|
5
|
+
version = "0.7.6"
|
|
6
6
|
edition = "2024"
|
|
7
7
|
repository = "https://github.com/isaacbentley/sdr-aaronia-rs"
|
|
8
8
|
readme = "README.md"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aaronia
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
@@ -302,7 +302,12 @@ from `/remoteconfig` first if you need to target one block.
|
|
|
302
302
|
|
|
303
303
|
These are one-off requests. Aaronia's own words: if they cannot be
|
|
304
304
|
executed, or conflict with the local configuration, the behaviour is
|
|
305
|
-
undefined — nothing reports the conflict.
|
|
305
|
+
undefined — nothing reports the conflict. That is not theoretical: the
|
|
306
|
+
same device has been measured both honouring and ignoring an identical
|
|
307
|
+
full-tuple capture command in different mission states, answering
|
|
308
|
+
`success=true` both times. A retune that must be provable goes through
|
|
309
|
+
`/remoteconfig` and reads the value back
|
|
310
|
+
(`HttpEndpointsClient::apply_capture_config` does exactly this).
|
|
306
311
|
|
|
307
312
|
**The settings each `type` accepts**, per Aaronia support (January
|
|
308
313
|
2024), which goes well beyond the published specification:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
class SoapyAaronia < Formula
|
|
13
13
|
desc "SoapySDR plugin for Aaronia SPECTRAN V6 spectrum analyzers"
|
|
14
14
|
homepage "https://github.com/isaacbentley/sdr-aaronia-rs"
|
|
15
|
-
version "0.7.
|
|
15
|
+
version "0.7.6"
|
|
16
16
|
license "GPL-3.0-or-later"
|
|
17
17
|
|
|
18
18
|
# Prebuilt modules: the plugin statically links its Rust core, so there
|
|
@@ -743,17 +743,42 @@ pub fn rtsa_epoch_seconds(raw: f64) -> f64 {
|
|
|
743
743
|
/// method call plus a `BufReader` bounds check, which dominates on large
|
|
744
744
|
/// sequential reads.
|
|
745
745
|
fn read_iq_f32<R: Read>(reader: &mut R, count: usize) -> Result<Vec<Complex32>> {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
746
|
+
// The wire format is little-endian f32 pairs, and `Complex32` is
|
|
747
|
+
// `#[repr(C)] { re: f32, im: f32 }`. On a little-endian host those bytes
|
|
748
|
+
// are already the in-memory representation, so `from_le_bytes` is a no-op
|
|
749
|
+
// and reading straight into the sample buffer removes both the separate
|
|
750
|
+
// byte allocation and the whole per-sample decode pass. The native-SDK
|
|
751
|
+
// path reinterprets its buffer the same way; this is the file mirror of
|
|
752
|
+
// it. On a big-endian host the bytes must be swapped, so that path keeps
|
|
753
|
+
// the explicit decode.
|
|
754
|
+
const _: () = assert!(std::mem::size_of::<Complex32>() == 2 * std::mem::size_of::<f32>());
|
|
755
|
+
|
|
756
|
+
#[cfg(target_endian = "little")]
|
|
757
|
+
{
|
|
758
|
+
let mut samples = vec![Complex32::new(0.0, 0.0); count];
|
|
759
|
+
// SAFETY: `samples` owns `count * 8` contiguous bytes (the repr and
|
|
760
|
+
// size are asserted above), and `read_exact` writes exactly that many
|
|
761
|
+
// before it returns; any bit pattern is a valid pair of `f32`.
|
|
762
|
+
let byte_view =
|
|
763
|
+
unsafe { std::slice::from_raw_parts_mut(samples.as_mut_ptr() as *mut u8, count * 8) };
|
|
764
|
+
reader.read_exact(byte_view)?;
|
|
765
|
+
Ok(samples)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
#[cfg(not(target_endian = "little"))]
|
|
769
|
+
{
|
|
770
|
+
let mut bytes = vec![0u8; count * 8];
|
|
771
|
+
reader.read_exact(&mut bytes)?;
|
|
772
|
+
Ok(bytes
|
|
773
|
+
.chunks_exact(8)
|
|
774
|
+
.map(|c| {
|
|
775
|
+
Complex32::new(
|
|
776
|
+
f32::from_le_bytes([c[0], c[1], c[2], c[3]]),
|
|
777
|
+
f32::from_le_bytes([c[4], c[5], c[6], c[7]]),
|
|
778
|
+
)
|
|
779
|
+
})
|
|
780
|
+
.collect())
|
|
781
|
+
}
|
|
757
782
|
}
|
|
758
783
|
|
|
759
784
|
/// Bulk-read `count` little-endian i16 IQ pairs, each component scaled by
|
|
@@ -2901,6 +2926,34 @@ impl DsftChunk {
|
|
|
2901
2926
|
|
|
2902
2927
|
#[cfg(test)]
|
|
2903
2928
|
mod tests {
|
|
2929
|
+
/// The zero-copy little-endian `read_iq_f32` must decode identically to
|
|
2930
|
+
/// the explicit `from_le_bytes` version. A raw reinterpret is exactly
|
|
2931
|
+
/// where an endianness or layout mistake hides while still producing
|
|
2932
|
+
/// plausible-looking floats.
|
|
2933
|
+
#[test]
|
|
2934
|
+
fn read_iq_f32_round_trips() {
|
|
2935
|
+
let pairs = [
|
|
2936
|
+
(0.0f32, 0.0f32),
|
|
2937
|
+
(1.0, -1.0),
|
|
2938
|
+
(1.5e-3, -2.7e4),
|
|
2939
|
+
(f32::MIN_POSITIVE, 12345.678),
|
|
2940
|
+
];
|
|
2941
|
+
let mut bytes = Vec::new();
|
|
2942
|
+
for (re, im) in pairs {
|
|
2943
|
+
bytes.extend_from_slice(&re.to_le_bytes());
|
|
2944
|
+
bytes.extend_from_slice(&im.to_le_bytes());
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
let mut cursor = std::io::Cursor::new(bytes);
|
|
2948
|
+
let got = read_iq_f32(&mut cursor, pairs.len()).expect("decode");
|
|
2949
|
+
|
|
2950
|
+
assert_eq!(got.len(), pairs.len());
|
|
2951
|
+
for (g, (re, im)) in got.iter().zip(pairs) {
|
|
2952
|
+
assert_eq!(g.re, re, "re mismatch");
|
|
2953
|
+
assert_eq!(g.im, im, "im mismatch");
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2904
2957
|
use super::*;
|
|
2905
2958
|
use std::io::Cursor;
|
|
2906
2959
|
|