python-aaronia 0.7.3__tar.gz → 0.7.5__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.3 → python_aaronia-0.7.5}/CHANGELOG.md +149 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/Cargo.lock +2 -2
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/Cargo.toml +1 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/PKG-INFO +58 -8
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/APPS.md +13 -5
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/HTTPSPEC.md +242 -9
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/QUICKSTART.md +35 -5
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/SDKSPEC.md +14 -8
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/VERIFICATION.md +1 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/packaging/homebrew/README.md +11 -4
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/packaging/homebrew/soapy-aaronia.rb +1 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/python-aaronia/Cargo.toml +1 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/python-aaronia/README.md +57 -7
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/python-aaronia/aaronia.pyi +11 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/python-aaronia/src/lib.rs +33 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/python-aaronia/test_basic.py +22 -0
- python_aaronia-0.7.5/scripts/validate-iq-live.py +386 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/README.md +23 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/Registration.cpp +15 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/http_endpoints.rs +15 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/seify_impl.rs +6 -1
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/unified_source.rs +9 -7
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/utils.rs +35 -7
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/.cargo/config.toml +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/.gitattributes +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/.gitignore +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/CONTRIBUTING.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/DESIGN.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/LICENSE +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/PLUGINS.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/README.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/benches/decompress_block.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/benches/deinterleave_dual_iq.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/benches/parse_int16_packet.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/benches/rtsa_open_and_read.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/deny.toml +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/FILESPEC.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/docs/USAGE.md +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/channel_hopping.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/device_control.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/dump_metadata.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/http_iq_quickstart.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/native_sdk_basic.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/native_sdk_transmit.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/noaa_scanner.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/python_arrow_example.py +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/read_rtsa_file.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/examples/soapy_python_example.py +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/include/aaronia.h +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/pyproject.toml +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/scripts/ci-local.sh +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/AaroniaSoapyDevice.cpp +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/AaroniaSoapyDevice.hpp +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/CMakeLists.txt +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeCache.txt +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeCXXCompiler.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CMakeSystem.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/a.out +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/4.4.2/CompilerIdCXX/apple-sdk.cpp +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/CMakeConfigureLog.yaml +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/CMakeDirectoryInformation.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/CMakeRuleHashes.txt +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/InstallScripts.json +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/Makefile.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/Makefile2 +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/TargetDirectories.txt +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/DependInfo.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/build.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/cmake_clean.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/compiler_depend.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/compiler_depend.ts +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/depend.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/flags.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/link.txt +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/aaroniaSupport.dir/progress.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/cmake.check_cache +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/progress.marks +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/DependInfo.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/build.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/cmake_clean.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/compiler_depend.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/compiler_depend.ts +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/CMakeFiles/sdr_aaronia_rs_static.dir/progress.make +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/Makefile +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/build-test/cmake_install.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/packaging/install.ps1 +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/packaging/install.sh +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/soapy-aaronia/print.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/c_api.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/decompression.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/detection.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/error.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/file_source.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/http_sink.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/http_source.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/http_streaming.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/lib.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/native_sdk.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/sdk_sink.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/sdk_source.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/sdr_source_impl.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/src/unified_sink.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/test.cmake +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/c_api_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/http_mock_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/http_resilience_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/http_sink_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/integration_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/live_smoke.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/native_sdk_load.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/properties.proptest-regressions +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/properties.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/rtsa_negative_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/sdr_source_impl_test.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/spec_coverage.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/test_cw_mag.rs +0 -0
- {python_aaronia-0.7.3 → python_aaronia-0.7.5}/tests/test_cw_meta.rs +0 -0
|
@@ -4,6 +4,155 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.7.5] - 2026-08-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`scale` on the Python config and `aaronia.open()`**, the integer
|
|
11
|
+
encode multiplier for the `I16` wire format. Rust, the C API and the
|
|
12
|
+
SoapySDR plugin all had it; Python did not, so a Python program
|
|
13
|
+
choosing `I16` for bandwidth had no way out of the trap below.
|
|
14
|
+
- **`scripts/validate-iq-live.py`**, an end-to-end check that the
|
|
15
|
+
samples an application receives are the ones the device sent, rather
|
|
16
|
+
than that bytes arrived. Every wire format must decode to the same
|
|
17
|
+
spectrum, the Python and SoapySDR paths must agree, and a known
|
|
18
|
+
transmitter must land where it should. Run against a live server it
|
|
19
|
+
places a NOAA weather-radio carrier within 312 Hz of 162.400 MHz and
|
|
20
|
+
on the correct side of zero — the one check that catches transposed
|
|
21
|
+
I and Q, which nothing comparing the radio against itself can see.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **`format="I16"` silently discards weak signals at the default
|
|
25
|
+
scale.** The server sends `round(value * scale)`, so the step is
|
|
26
|
+
`1 / scale`, and the default of 16384 gives 6.1e-5 — coarser than a
|
|
27
|
+
quiet band's noise floor. Measured against a live server, **68% of
|
|
28
|
+
int16 samples came back exactly zero** where float32 had none. At
|
|
29
|
+
`scale=1e6` the zero fraction was 0.0% and the amplitude matched
|
|
30
|
+
float32. Documented, with the measurements, in the Python README.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- **The Homebrew formula is no longer a release asset.** It is uploaded
|
|
34
|
+
as the `homebrew-formula` workflow artifact instead. Homebrew 4 and
|
|
35
|
+
later cannot install from a formula URL, so on the release page it
|
|
36
|
+
was a file no user could act on, listed among ones they can. Its only
|
|
37
|
+
reader is whoever updates the tap, and the checksums are still
|
|
38
|
+
rendered against the published archives so none are computed by hand.
|
|
39
|
+
|
|
40
|
+
### Documentation
|
|
41
|
+
- **Wire format is a throughput decision, and the default is the
|
|
42
|
+
expensive one.** At 15.36 MS/s over a LAN, float32 needs 123 MB/s:
|
|
43
|
+
measured, it delivered 6.5 MS/s with 290 drops, while float16 and
|
|
44
|
+
int16 both delivered 15.1 MS/s. The Python README now carries the
|
|
45
|
+
numbers rather than a general warning.
|
|
46
|
+
- **Measured what the 80% usable-bandwidth figure actually is, and
|
|
47
|
+
explained it in one sentence.** Sample rate and RF bandwidth were
|
|
48
|
+
described as related by a ratio without saying what the ratio was or
|
|
49
|
+
where it came from. RTSA declares exactly 0.8 x Fs as the packet's
|
|
50
|
+
frequency range at every rate — a fixed rule, checked at 61.44,
|
|
51
|
+
15.36, 7.68 and 3.84 MHz — and every sample still arrives, so an FFT
|
|
52
|
+
spans the whole rate while only that 80% is flat and calibrated.
|
|
53
|
+
Sweeping the receiver's own noise floor on a V6 ECO confirms it: the
|
|
54
|
+
response is flat within 0.5 dB across 0.80 of the rate at 15.36 MHz
|
|
55
|
+
sampling and 0.89 at 7.68 MHz, and at full span the analog filter is
|
|
56
|
+
about 1 dB down by the declared edge — which is where Aaronia's
|
|
57
|
+
44 MHz data-sheet figure comes from, against the 49.152 MHz the
|
|
58
|
+
device itself declares. The guidance readers need is one line: to see
|
|
59
|
+
N Hz of spectrum, sample at N / 0.8.
|
|
60
|
+
- **The READMEs stated the V6 ECO's sample-rate ladder as if it were
|
|
61
|
+
every device's.** "The device runs a fixed ladder of rates: 61.44 MHz
|
|
62
|
+
halved down to 120 kHz" is measured and true for an ECO, but a full
|
|
63
|
+
V6 selects its receiver clock and starts higher — by how much is the
|
|
64
|
+
open question the specs already record, and the user-facing pages did
|
|
65
|
+
not carry it. The Python, SoapySDR, quickstart and applications docs
|
|
66
|
+
now say whose ladder it is and point at the note, and
|
|
67
|
+
`iq_sample_rates` says so in its own documentation.
|
|
68
|
+
- The SoapySDR README gained a Sample rates section: why
|
|
69
|
+
`listSampleRates` reports the real ladder, that `setSampleRate` snaps
|
|
70
|
+
and logs, and that `getSampleRate` while streaming is the number to
|
|
71
|
+
trust on hardware the advertised ladder does not describe.
|
|
72
|
+
- SDKSPEC still gave the eco family's clock as 61.44 MHz in a second
|
|
73
|
+
place, contradicting the correction made elsewhere in the same
|
|
74
|
+
document. It is 92.16 MHz; 61.44 MHz is the top IQ rate.
|
|
75
|
+
- The same sweep caught the claim in `unified_source`'s own
|
|
76
|
+
documentation, in the `/control` span note in HTTPSPEC, and in the
|
|
77
|
+
0.8 usable-bandwidth ratio, all of which read as universal and are
|
|
78
|
+
measurements from one device. `seify_impl`'s sample-rate range is
|
|
79
|
+
capped at 61.44 MHz for the same reason and now says so: seify has no
|
|
80
|
+
device handle at that point to ask for something better.
|
|
81
|
+
|
|
82
|
+
## [v0.7.4] - 2026-08-12
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
- **The SoapySDR plugin ignored an unrecognised `format=` silently.**
|
|
86
|
+
A device string carrying `format=int16` — the wire name rather than
|
|
87
|
+
the plugin's `I16` — streamed the default format while claiming
|
|
88
|
+
otherwise. It now warns and continues. The server behaves the same
|
|
89
|
+
way and worse: an unrecognised `format=` on `/stream` serves the
|
|
90
|
+
RTSA file format with HTTP 200 rather than an error, so a typo
|
|
91
|
+
changes the wire format entirely. `raw16`, which Aaronia's own Qt
|
|
92
|
+
reference client sends, is a working alias for `int16`.
|
|
93
|
+
|
|
94
|
+
### Documentation
|
|
95
|
+
- Checked Aaronia's V6 remote control notes (rev 4, May 2026) against
|
|
96
|
+
the hardware. `/remoteconfig` enum fields take an index as well as a
|
|
97
|
+
label; one `simpleconfig` PUT can carry several groups, and groups
|
|
98
|
+
other than `main` work; and a PUT naming a block that is not in the
|
|
99
|
+
mission returns 200 and changes nothing, which
|
|
100
|
+
`simple_remote_config` now warns about since it reports `Ok(())` for
|
|
101
|
+
a write that did not happen. In the config-tree form the receiver
|
|
102
|
+
name is ignored altogether — the write is routed by `config.name`.
|
|
103
|
+
- Documented loading a mission over `/control`, and that every
|
|
104
|
+
`/control` payload needs its `type` or the server answers `400`.
|
|
105
|
+
Loading a mission is deliberately not exposed by the crate: swapping
|
|
106
|
+
the mission under a running capture should be a caller's decision,
|
|
107
|
+
not a side effect.
|
|
108
|
+
- Noted that RTSA-Suite has no status endpoint. Aaronia's own liveness
|
|
109
|
+
check reads the `404` from `/api/status` as proof the server is up.
|
|
110
|
+
- **What "Full" means on a full V6 is unresolved.** A V6 ECO follows the
|
|
111
|
+
SDK's `spanfreq <= receiverclock / 1.5`, measured. Aaronia's Remote
|
|
112
|
+
Config screenshots show a full V6 at a 92 MHz clock delivering
|
|
113
|
+
92.16 MHz of IQ samples per second at span "Full" — the clock itself.
|
|
114
|
+
`iq_sample_rates_for_clock` may therefore understate the top of the
|
|
115
|
+
ladder by 1.5x for a full V6 at a non-default clock; it says so now.
|
|
116
|
+
Settling it needs a full V6.
|
|
117
|
+
- HTTPSPEC contradicted itself on the Remote Config licence, asserting
|
|
118
|
+
in one section that writes need it and in another that a live
|
|
119
|
+
unlicensed system accepts them. The second is what the hardware does,
|
|
120
|
+
re-confirmed for centre frequency, decimation, reference level and
|
|
121
|
+
the preamplifier.
|
|
122
|
+
- SDKSPEC still gave the V6 ECO's receiver clock as 61.44 MHz, which
|
|
123
|
+
0.6.2 corrected in code to 92.16 MHz. 61.44 MHz is the ECO's top IQ
|
|
124
|
+
rate, that clock over 1.5; the document had the two confused.
|
|
125
|
+
- **A marker stream is not a categories packet**, which the previous
|
|
126
|
+
draft of this entry got wrong. Aaronia's example declares
|
|
127
|
+
`payload: "spectra"`, and spectra samples are a 2D array, so its
|
|
128
|
+
nesting is correct for what it says it is. Its three frequency fields
|
|
129
|
+
are all zero, so the category names and ranges are the only
|
|
130
|
+
description of what the numbers mean.
|
|
131
|
+
- Aaronia's endpoint specification (rev 11) settles several things this
|
|
132
|
+
document had only inferred, and their support answers go further.
|
|
133
|
+
`/control` takes PUT only, and a command reaches every block that
|
|
134
|
+
understands it unless `receiverUUID` or `receiverName` scopes it —
|
|
135
|
+
the specification says such commands cannot be addressed to a block,
|
|
136
|
+
which their support corrected in 2024. The per-type settings are now
|
|
137
|
+
listed in full, including `deviceconnect` and `camera`, which this
|
|
138
|
+
crate does not model. Zones cannot be configured remotely at all.
|
|
139
|
+
The server starts dropping data once its outbound TCP buffer passes
|
|
140
|
+
8 MB, which is the mechanism behind most unexplained gaps.
|
|
141
|
+
`/healthstatus` is organised as `info`, `status`, `health`,
|
|
142
|
+
`settings` and `components`, the last being how satellites attached
|
|
143
|
+
over HTTP appear in a local tree.
|
|
144
|
+
- **`status/iqsamples` is the native rate, not the delivered one.** It
|
|
145
|
+
held at 61.44 MHz while the same device delivered 15.36, then 7.68,
|
|
146
|
+
then 61.44 MS/s. It looks like a sample rate and is not the one your
|
|
147
|
+
stream is running at; read `sampleFrequency` from packet metadata.
|
|
148
|
+
Documented the other fields a V6 ECO reports alongside it.
|
|
149
|
+
- **One HTTP Server and one HTTP Client connection are free**;
|
|
150
|
+
additional instances and connections are licensed separately, as are
|
|
151
|
+
Stream Merger and Stream Splitter. Running this crate and a second
|
|
152
|
+
client against one server at the same time is a second connection —
|
|
153
|
+
the licence limit most likely to be met in practice, and unrelated to
|
|
154
|
+
Remote Config.
|
|
155
|
+
|
|
7
156
|
## [v0.7.3] - 2026-08-12
|
|
8
157
|
|
|
9
158
|
### Fixed
|
|
@@ -3027,7 +3027,7 @@ dependencies = [
|
|
|
3027
3027
|
|
|
3028
3028
|
[[package]]
|
|
3029
3029
|
name = "python-aaronia"
|
|
3030
|
-
version = "0.7.
|
|
3030
|
+
version = "0.7.5"
|
|
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.5"
|
|
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.5"
|
|
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.5
|
|
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+)
|
|
@@ -88,17 +88,66 @@ above depends on.
|
|
|
88
88
|
|
|
89
89
|
## Sample rates
|
|
90
90
|
|
|
91
|
-
The device runs a
|
|
92
|
-
|
|
93
|
-
leaving your program computing against a rate
|
|
91
|
+
The device runs a ladder of rates rather than a continuous range: each
|
|
92
|
+
rung is half the one above it. Ask for anything else and it quietly
|
|
93
|
+
uses the nearest rung, leaving your program computing against a rate
|
|
94
|
+
that is not in use.
|
|
94
95
|
|
|
95
96
|
```python
|
|
96
97
|
aaronia.sample_rates() # every rate, highest first
|
|
97
98
|
aaronia.sample_rate_for_bandwidth(8e6) # 15.36e6: the lowest rate covering 8 MHz
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
**Sample rate is not RF bandwidth.** You get every sample, so an FFT of
|
|
102
|
+
them spans the full rate — but only the middle 80% is flat and
|
|
103
|
+
calibrated. That is not an approximation: RTSA reports exactly 0.8 x Fs
|
|
104
|
+
as the packet's frequency range at every rate. Outside it, data still
|
|
105
|
+
arrives, attenuated and uncalibrated.
|
|
106
|
+
|
|
107
|
+
So **to see N Hz of spectrum, sample at N / 0.8**, which is what
|
|
108
|
+
`sample_rate_for_bandwidth()` computes. Aaronia's data sheet quotes a
|
|
109
|
+
more conservative figure still — 44 MHz for the ECO against the
|
|
110
|
+
49.152 MHz it declares at full span — because the analog filter is
|
|
111
|
+
already about 1 dB down at that edge. The
|
|
112
|
+
[quickstart](https://github.com/isaacbentley/sdr-aaronia-rs/blob/main/docs/QUICKSTART.md#4-troubleshooting)
|
|
113
|
+
has the measurements.
|
|
114
|
+
|
|
115
|
+
`sample_rates()` returns the ladder for a SPECTRAN V6 ECO — 61.44 MHz
|
|
116
|
+
down to 120 kHz — which is measured, rung by rung. A full V6 has a
|
|
117
|
+
selectable receiver clock and can go higher, and exactly how much
|
|
118
|
+
higher is not settled; see
|
|
119
|
+
[the note in HTTPSPEC](https://github.com/isaacbentley/sdr-aaronia-rs/blob/main/docs/HTTPSPEC.md#unresolved-the-full-v6s-top-rate).
|
|
120
|
+
On that hardware, take the rate the device reports over the computed
|
|
121
|
+
ladder: it arrives in the stream metadata, and `diagnose()` prints it.
|
|
122
|
+
|
|
123
|
+
## Choosing a wire format
|
|
124
|
+
|
|
125
|
+
`format` decides what crosses the network, and it matters more than it
|
|
126
|
+
looks. Measured against a live server at 15.36 MS/s over a LAN:
|
|
127
|
+
|
|
128
|
+
| format | bytes/sample | delivered | drops |
|
|
129
|
+
| --- | --- | --- | --- |
|
|
130
|
+
| `F32` (default) | 8 | 6.5 MS/s | 290 |
|
|
131
|
+
| `F16` | 4 | 15.1 MS/s | 9 |
|
|
132
|
+
| `I16` | 4 | 15.1 MS/s | 12 |
|
|
133
|
+
|
|
134
|
+
`F32` needs 123 MB/s at that rate and the link could not carry it, so
|
|
135
|
+
most of the capture was dropped. Either half-width format fits.
|
|
136
|
+
|
|
137
|
+
`I16` has one trap: the server sends `round(value * scale)`, so the
|
|
138
|
+
quantisation step is `1 / scale`, and the default of 16384 gives a step
|
|
139
|
+
of 6.1e-5. A quiet band's noise floor is smaller than that — on the
|
|
140
|
+
same server, **68% of `I16` samples came back exactly zero** while
|
|
141
|
+
`F32` had none. Pass `scale=`, or lower `reference_level` for more
|
|
142
|
+
gain:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
aaronia.open(url, freq=2.44e9, rate=15.36e6, format="I16", scale=1e6)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
At `scale=1e6` the zero fraction measured 0.0% and the amplitude
|
|
149
|
+
matched `F32`. `F16` needs no such tuning, which makes it the simpler
|
|
150
|
+
choice when the link is the constraint.
|
|
102
151
|
|
|
103
152
|
## Configuration (`AaroniaConfig`)
|
|
104
153
|
|
|
@@ -113,6 +162,7 @@ Every field is readable and writable.
|
|
|
113
162
|
| `sample_rate` | IQ sample rate, Hz (the Aaronia "span") |
|
|
114
163
|
| `reference_level` | Reference level, dBm |
|
|
115
164
|
| `format` | HTTP wire format: `"F32"`, `"F16"` or `"I16"`. `I16` is the low-bandwidth network mode |
|
|
165
|
+
| `scale` | Integer encode multiplier for `I16` (see below). None uses the server default |
|
|
116
166
|
| `receiver_channel` | `"Rx1"` (default), `"Rx2"`, or `"Rx1And2"` (native SDK, full V6) |
|
|
117
167
|
| `read_timeout` | Seconds a blocking read waits before `AaroniaTimeoutError` (default `30.0`) |
|
|
118
168
|
| `auto_reconnect` | Reconnect the HTTP stream after a drop (default `True`) |
|
|
@@ -169,8 +219,8 @@ silently defaulting.
|
|
|
169
219
|
|
|
170
220
|
| Function | Purpose |
|
|
171
221
|
| --- | --- |
|
|
172
|
-
| `open(url=None, *, freq, rate, bandwidth, ref_level, file, format, read_timeout)` | Configure, connect and start streaming in one call |
|
|
173
|
-
| `sample_rates()` |
|
|
222
|
+
| `open(url=None, *, freq, rate, bandwidth, ref_level, file, format, scale, read_timeout)` | Configure, connect and start streaming in one call |
|
|
223
|
+
| `sample_rates()` | The V6 ECO's sample rates, highest first (see [Sample rates](#sample-rates)) |
|
|
174
224
|
| `sample_rate_for_bandwidth(hz)` | Lowest rate covering that much spectrum |
|
|
175
225
|
| `diagnose(url)` | `(ok, message, fix)` for each setup check; what `aaronia-doctor` prints |
|
|
176
226
|
|
|
@@ -50,9 +50,12 @@ driver=aaronia,url=http://localhost:54664
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
Select the sample rate from GQRX's list. The plugin advertises the
|
|
53
|
-
device's real ladder
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
device's real ladder rather than round numbers it cannot produce: on a
|
|
54
|
+
V6 ECO that is 61.44 MHz halved down to 120 kHz, which the RTSA GUI
|
|
55
|
+
labels Full through 1/512. Nothing in between exists, so a request
|
|
56
|
+
between rungs snaps to the nearest and logs a warning. A full V6 has a
|
|
57
|
+
selectable receiver clock and reaches higher; the plugin advertises the
|
|
58
|
+
ECO ladder, so check the rate the device reports if you are on one.
|
|
56
59
|
|
|
57
60
|
The single gain element is `REF`, the Aaronia reference level in dBm. It
|
|
58
61
|
is not an amplifier gain: raising it reduces sensitivity. Start near
|
|
@@ -101,8 +104,13 @@ directly and skip the SoapySDR layer.
|
|
|
101
104
|
|
|
102
105
|
## General notes
|
|
103
106
|
|
|
104
|
-
- Sample rate is the Aaronia "span", not the usable RF bandwidth.
|
|
105
|
-
|
|
107
|
+
- Sample rate is the Aaronia "span", not the usable RF bandwidth. Every
|
|
108
|
+
sample reaches the application, so the display spans the full rate,
|
|
109
|
+
but only the middle 80% is flat and calibrated — RTSA reports exactly
|
|
110
|
+
0.8 x Fs as the packet's frequency range. Set the rate whose 80%
|
|
111
|
+
covers the span you want to see: 61.44 MHz of sampling gives roughly
|
|
112
|
+
49 MHz of calibrated spectrum. The edges are real data, just rolled
|
|
113
|
+
off.
|
|
106
114
|
- `readStream` honours `timeoutUs` and returns partial reads within the
|
|
107
115
|
deadline, per the SoapySDR contract.
|
|
108
116
|
- Retuning mid-stream is safe and requires no Aaronia licence.
|
|
@@ -161,6 +161,43 @@ http://localhost:54664/stream?format=int16&scale=1000000
|
|
|
161
161
|
http://localhost:54664/stream?rate_reduction=10
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
### An unknown `format` is not an error
|
|
165
|
+
|
|
166
|
+
`format=` accepts `json`, `int16`, `float16` and `float32`. `raw16` is
|
|
167
|
+
an accepted alias for `int16` — Aaronia's own Qt reference client
|
|
168
|
+
defaults to it — and produces byte-identical framing.
|
|
169
|
+
|
|
170
|
+
Anything the server does not recognise serves **the RTSA file format**
|
|
171
|
+
instead: a `DSFH` header followed by `STRM`/`ANTA` chunks, with HTTP
|
|
172
|
+
200 and no warning. A typo in `format=` therefore yields a completely
|
|
173
|
+
different wire format rather than an error, and a parser expecting
|
|
174
|
+
JSON-plus-binary will fail somewhere well past the point that would
|
|
175
|
+
have identified the cause. Verified against a live server: `format=`
|
|
176
|
+
with a nonsense value returned `DSFH`.
|
|
177
|
+
|
|
178
|
+
This crate builds the string from an enum, so it cannot typo, and the
|
|
179
|
+
SoapySDR plugin now warns on an unrecognised `format=` device argument
|
|
180
|
+
rather than silently falling back to the default.
|
|
181
|
+
|
|
182
|
+
### When the server drops data
|
|
183
|
+
|
|
184
|
+
The HTTP server block starts dropping data once its outbound TCP
|
|
185
|
+
buffer passes **8 MB**. Nothing announces it; the loss shows up as a
|
|
186
|
+
gap between the timestamps of two adjacent packets, which is what
|
|
187
|
+
`DropDetector` watches for. A slow consumer, a slow link, or a rate
|
|
188
|
+
the network cannot carry all end here, so reducing the wire format
|
|
189
|
+
(`format=int16`) or the rate (`rate_reduction=n`) is the fix rather
|
|
190
|
+
than a larger client-side buffer.
|
|
191
|
+
|
|
192
|
+
### Liveness
|
|
193
|
+
|
|
194
|
+
There is no status endpoint. Aaronia's own remote control notes probe
|
|
195
|
+
with `curl -v http://127.0.0.1:54664/api/status` and treat the `404 Not
|
|
196
|
+
Found` as the answer: a reply of any kind proves the port is open, the
|
|
197
|
+
mission is loaded and the HTTP Server block is running. A connection
|
|
198
|
+
refused or a timeout means it is not. `/info` serves the same purpose
|
|
199
|
+
and returns something useful, which is what this crate uses.
|
|
200
|
+
|
|
164
201
|
### Application Process Control
|
|
165
202
|
**URL**: `/app/process`
|
|
166
203
|
**Method**: PUT
|
|
@@ -250,6 +287,42 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
250
287
|
`stepFrequency` (the sample rate) is optional; `push: true` marks the packet for immediate transmission.
|
|
251
288
|
|
|
252
289
|
### Control Endpoint
|
|
290
|
+
|
|
291
|
+
`/control` accepts **PUT only**. A GET — which is what a browser does
|
|
292
|
+
with the URL — is not supported.
|
|
293
|
+
|
|
294
|
+
**A command reaches every block that understands it, unless you scope
|
|
295
|
+
it.** Aaronia's 2022 specification says control commands "are not
|
|
296
|
+
addressed to a specific RTSA block and will be processed from all RTSA
|
|
297
|
+
blocks in the block graph", which is true as a default. Their support
|
|
298
|
+
staff refined it in 2024: `receiverUUID` and `receiverName` "can be
|
|
299
|
+
used to limit the requested setting to individual blocks", and "in some
|
|
300
|
+
cases it may be necessary to specify them". Read the names or UUIDs
|
|
301
|
+
from `/remoteconfig` first if you need to target one block.
|
|
302
|
+
|
|
303
|
+
These are one-off requests. Aaronia's own words: if they cannot be
|
|
304
|
+
executed, or conflict with the local configuration, the behaviour is
|
|
305
|
+
undefined — nothing reports the conflict.
|
|
306
|
+
|
|
307
|
+
**The settings each `type` accepts**, per Aaronia support (January
|
|
308
|
+
2024), which goes well beyond the published specification:
|
|
309
|
+
|
|
310
|
+
| `type` | Settings |
|
|
311
|
+
| :--- | :--- |
|
|
312
|
+
| `capture` | `receiverUUID`, `receiverName`, `frequencyCenter`, `frequencyStart`, `frequencyBins`, `referenceLevel`, `start` — plus `frequencySpan` and `frequencyEnd`, which the specification documents and this crate uses |
|
|
313
|
+
| `deviceconnect` | `receiverUUID`, `receiverName`, `start` — connect or disconnect a device |
|
|
314
|
+
| `streaming` | `receiverUUID`, `receiverName`, `filename`, `start` |
|
|
315
|
+
| `recording` | `receiverUUID`, `receiverName`, `filename`, `start` |
|
|
316
|
+
| `mission` | `save`, `reload`, `load` (with the path in `file`) |
|
|
317
|
+
| `antenna` | `receiverUUID`, `receiverName`, `latitude`+`longitude` (both or neither), `azimuth`+`declination`, `rotate` |
|
|
318
|
+
| `camera` | `receiverUUID`, `receiverName`, `latitude`+`longitude`, `azimuth`+`declination`, `aperture`, `channel`, `altitude`, `start` |
|
|
319
|
+
|
|
320
|
+
`start`, `rotate`, `save`, `load` and `reload` are booleans. A
|
|
321
|
+
`filename` must be an absolute path **on the machine running
|
|
322
|
+
RTSA-Suite**, with forward slashes.
|
|
323
|
+
|
|
324
|
+
Zones cannot be configured this way; Aaronia state that remote
|
|
325
|
+
configuration of zones is not supported.
|
|
253
326
|
**URL**: `/control`
|
|
254
327
|
**Method**: PUT
|
|
255
328
|
**Description**: Send commands to the RTSA suite
|
|
@@ -284,7 +357,17 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
284
357
|
```
|
|
285
358
|
|
|
286
359
|
> **`frequencySpan` is a usable-bandwidth request, not a sample rate.**
|
|
287
|
-
> The device
|
|
360
|
+
> The device halves its top rate down a ladder — 61.44 MHz on the V6
|
|
361
|
+
> ECO this was measured against. The usable RF bandwidth it declares in
|
|
362
|
+
> `startFrequency..endFrequency` is exactly 0.8 x Fs at every rate,
|
|
363
|
+
> checked at 61.44, 15.36, 7.68 and 3.84 MHz; every sample still
|
|
364
|
+
> arrives, so an FFT spans the whole rate, but only that 80% is flat
|
|
365
|
+
> and calibrated. Sweeping the receiver's own noise floor puts the real
|
|
366
|
+
> filter at or beyond the declared edge at decimated rates (flat within
|
|
367
|
+
> 0.5 dB across 0.80 x Fs at 15.36 MHz, 0.89 at 7.68 MHz) and just
|
|
368
|
+
> inside it at full span (about 1 dB down at the declared edge, 3 dB at
|
|
369
|
+
> 0.84 x Fs), which is where Aaronia's 44 MHz data-sheet figure for the
|
|
370
|
+
> ECO comes from. Given a span
|
|
288
371
|
> it cannot produce, it selects the rate whose alias-free bandwidth
|
|
289
372
|
> (0.8 x Fs) is nearest the request: 2.5 MHz yields Fs = 3.84 MHz,
|
|
290
373
|
> 1.3 MHz yields 1.92 MHz, and 10 MHz yields 15.36 MHz. A value that is
|
|
@@ -320,7 +403,7 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
320
403
|
}
|
|
321
404
|
```
|
|
322
405
|
|
|
323
|
-
#### Save/Reload Mission
|
|
406
|
+
#### Save/Reload/Load Mission
|
|
324
407
|
```json
|
|
325
408
|
{
|
|
326
409
|
"save": true,
|
|
@@ -328,6 +411,28 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
328
411
|
}
|
|
329
412
|
```
|
|
330
413
|
|
|
414
|
+
A mission can also be swapped outright, which is how a headless station
|
|
415
|
+
changes what it is running. `load` takes an absolute path to an `.rmix`
|
|
416
|
+
on the machine running RTSA-Suite, not on the client:
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"type": "mission",
|
|
420
|
+
"load": true,
|
|
421
|
+
"file": "C:/Aaronia/Recordings/HTTP streaming/http_test.rmix"
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
A mission carrying an HTTP Server block must already be running, or
|
|
425
|
+
there is nothing listening to accept the request. Aaronia's remote
|
|
426
|
+
control notes (rev 4, May 2026) give this as a `curl` invocation from
|
|
427
|
+
an elevated prompt. This crate does not expose it: swapping the mission
|
|
428
|
+
out from under a running capture is the caller's decision to make
|
|
429
|
+
deliberately, not a side effect of a library call.
|
|
430
|
+
|
|
431
|
+
**Every `/control` payload needs its `type`.** A body without one is
|
|
432
|
+
rejected with `400` and the text `Type argument missing`, so a
|
|
433
|
+
hand-built payload that omits it fails loudly rather than silently —
|
|
434
|
+
unlike a partial capture request, which is accepted and ignored.
|
|
435
|
+
|
|
331
436
|
### Configuration Endpoints
|
|
332
437
|
|
|
333
438
|
#### Server Info (`/info`)
|
|
@@ -430,8 +535,18 @@ Notes verified against a live V6 ECO:
|
|
|
430
535
|
and `frequencySpan` are both present.
|
|
431
536
|
- **The `receiverName` key is case-tolerant.** `receivername` was accepted
|
|
432
537
|
identically.
|
|
433
|
-
- Enum-valued settings
|
|
434
|
-
`decimation` as `"1 / 128"
|
|
538
|
+
- **Enum-valued settings take either the label string or the index.**
|
|
539
|
+
Aaronia's example writes `decimation` as `"1 / 128"`; writing
|
|
540
|
+
`decimation0` as the integer `3` was accepted and applied identically
|
|
541
|
+
(verified against `/sample`: index 3 gave 7.68 MHz, index 2 gave
|
|
542
|
+
15.36 MHz). Indices are positions in the `values` list that
|
|
543
|
+
`GET /remoteconfig` reports for that item.
|
|
544
|
+
- **Groups other than `main` work**, and several can go in one PUT:
|
|
545
|
+
`{"main": {...}, "calibration": {"preamp": 1}}` applied both.
|
|
546
|
+
- **An unknown block name is a silent no-op.** A `simpleconfig` PUT
|
|
547
|
+
naming a block that is not in the mission returns HTTP 200 and changes
|
|
548
|
+
nothing. There is no error to catch, so read the value back if it
|
|
549
|
+
matters.
|
|
435
550
|
- Other keys the example writes: `run` (bool), `preamp` (`"Auto"`),
|
|
436
551
|
`reflevel` (float), and `filerecord` (bool) plus a filename template on
|
|
437
552
|
a FileWriter block, which is how it starts and stops recording.
|
|
@@ -445,7 +560,15 @@ Notes verified against a live V6 ECO:
|
|
|
445
560
|
}
|
|
446
561
|
}
|
|
447
562
|
```
|
|
448
|
-
`receiverName` must match a block in the running mission;
|
|
563
|
+
`receiverName` must match a block in the running mission;
|
|
564
|
+
`HttpEndpointsClient::find_iq_demodulator_block_name` auto-discovers it.
|
|
565
|
+
A name that matches nothing still returns 200 (see above).
|
|
566
|
+
|
|
567
|
+
In the full `{request, config}` form the receiver name is ignored
|
|
568
|
+
altogether: the write is routed by `config.name`. Sending
|
|
569
|
+
`receivername`, `receiverName` or no name at all applied the same
|
|
570
|
+
change. Nothing depends on getting that key right, and nothing warns
|
|
571
|
+
when it is wrong.
|
|
449
572
|
|
|
450
573
|
**Configuration Item Fields**:
|
|
451
574
|
| Field | Description |
|
|
@@ -469,6 +592,28 @@ configuration tree (`HealthStatus` is an alias for `ConfigItem`) rather
|
|
|
469
592
|
than the typed shape below, which describes the upstream block-health
|
|
470
593
|
fields.
|
|
471
594
|
|
|
595
|
+
**Subgroups** per health-aware block, per Aaronia's specification:
|
|
596
|
+
`info`, `status`, `health`, `settings`, and `components` — the last a
|
|
597
|
+
recursive list of sub-blocks when satellites are attached over HTTP,
|
|
598
|
+
which is how a remote station's devices show up in a local tree.
|
|
599
|
+
|
|
600
|
+
**What a V6 ECO actually reports** under `status` and `health`,
|
|
601
|
+
alongside the `info` fields below:
|
|
602
|
+
|
|
603
|
+
| Item | Example | Meaning |
|
|
604
|
+
| :--- | :--- | :--- |
|
|
605
|
+
| `status/iqsamples` | 61439323 | **Native** IQ rate in Hz, not the delivered one. Measured constant at 61.44 MHz while the same device delivered 15.36, then 7.68, then 61.44 MS/s — it does not follow the decimation setting |
|
|
606
|
+
| `status/usbbuffer` | 0.0625 | USB buffer fill, fraction |
|
|
607
|
+
| `status/adcrange` | 17.16 | ADC headroom in dB |
|
|
608
|
+
| `status/strmtimedist` | 5.96e-06 | Stream time distance in seconds |
|
|
609
|
+
| `health/fronttemp` | 64.7 | Frontend temperature, °C |
|
|
610
|
+
| `health/fpgatemp` | 52.5 | FPGA temperature, °C |
|
|
611
|
+
| `health/boardpower` | 8.975 | Board power draw, W |
|
|
612
|
+
|
|
613
|
+
`iqsamples` is the one to be careful with: it looks like a sample rate
|
|
614
|
+
and is not the one your stream is running at. Read `sampleFrequency`
|
|
615
|
+
from packet metadata for that.
|
|
616
|
+
|
|
472
617
|
**Health Status Fields**:
|
|
473
618
|
| Field | Description |
|
|
474
619
|
| :--- | :--- |
|
|
@@ -577,7 +722,35 @@ The samples in a category ordered packet have one measurement per category.
|
|
|
577
722
|
}
|
|
578
723
|
```
|
|
579
724
|
|
|
580
|
-
As with histograms, category samples are a flat number array (one value
|
|
725
|
+
As with histograms, category samples are a flat number array (one value
|
|
726
|
+
per category) — that is what a live server produced.
|
|
727
|
+
|
|
728
|
+
**A marker stream is not a categories packet.** Aaronia's remote
|
|
729
|
+
control notes show the Spectrum block's `Marker` output wired to an
|
|
730
|
+
HTTP Server, and the result looks like this:
|
|
731
|
+
|
|
732
|
+
```json
|
|
733
|
+
{
|
|
734
|
+
"payload": "spectra",
|
|
735
|
+
"startFrequency": 0, "endFrequency": 0, "sampleFrequency": 0,
|
|
736
|
+
"sampleSize": 4, "sampleDepth": 1,
|
|
737
|
+
"categories": [
|
|
738
|
+
{ "name": "Slow Sine(R)", "startFrequency": 2403000000, "endFrequency": 2405000000 },
|
|
739
|
+
{ "name": "Marker", "startFrequency": 2412852325.3, "endFrequency": 2412852335.3 }
|
|
740
|
+
],
|
|
741
|
+
"samples": [[-78.6, -57.26, -47.28, -114.25]]
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
It carries a `categories` array but declares `payload: "spectra"`, and
|
|
746
|
+
spectra samples are a 2D array — one row per frame — so the nesting is
|
|
747
|
+
correct for what it says it is, not a contradiction of the flat
|
|
748
|
+
categories form above. `sampleSize` is the number of values in a row,
|
|
749
|
+
here one per category.
|
|
750
|
+
|
|
751
|
+
What is worth knowing before parsing one: all three frequency fields
|
|
752
|
+
are `0`, so nothing can be derived from them, and the category names
|
|
753
|
+
and ranges are the only description of what the numbers mean.
|
|
581
754
|
|
|
582
755
|
### Antenna Data
|
|
583
756
|
Data captured using antennas with location or directional information.
|
|
@@ -614,7 +787,44 @@ and `Location and Time`; `sclksource` offers `Consumer`, `Oscillator`,
|
|
|
614
787
|
|
|
615
788
|
`decimation0` takes the same labels as the SDK's `main/decimation`,
|
|
616
789
|
`"Full"` through `"1 / 512"`, verified by writing one over HTTP. Both
|
|
617
|
-
transports drive the same divider.
|
|
790
|
+
transports drive the same divider. Over HTTP the index works as well as
|
|
791
|
+
the label: writing `3` and reading `/sample` back gave 7.68 MHz, and `2`
|
|
792
|
+
gave 15.36 MHz, which is 61.44 MHz halved that many times on a V6 ECO.
|
|
793
|
+
|
|
794
|
+
#### Unresolved: the full V6's top rate
|
|
795
|
+
|
|
796
|
+
The SDK constrains `spanfreq * 1.5 <= receiverclock`, and a V6 ECO
|
|
797
|
+
follows it: its top IQ rate measures 61.44 MHz against the 92.16 MHz
|
|
798
|
+
clock the crate assumes for it. `iq_sample_rates_for_clock` takes that
|
|
799
|
+
ratio as the rule, so it puts a full V6 on the `245MHz` clock at
|
|
800
|
+
163.84 MHz.
|
|
801
|
+
|
|
802
|
+
Aaronia's own figures do not agree. Their endpoint specification closes
|
|
803
|
+
with a note about achieving "the full 250M samples of IQ data", their
|
|
804
|
+
block documentation advertises "a real-time bandwidth of up to 245MHz
|
|
805
|
+
each" for the V6's two inputs, and both round to the 245.76 MHz that
|
|
806
|
+
the `245MHz` clock label denotes — the clock itself, not two thirds of
|
|
807
|
+
it.
|
|
808
|
+
|
|
809
|
+
There is a reading that reconciles them. The clock list runs past
|
|
810
|
+
`245MHz` to `492MHz` (491.52 MHz), and 245.76 MHz of span satisfies the
|
|
811
|
+
1.5 rule against that clock with room to spare. On that reading 245 MHz
|
|
812
|
+
of real-time bandwidth is real but needs the fastest clock, and the
|
|
813
|
+
`245MHz` label tops out at 163.84 MHz exactly as the crate computes.
|
|
814
|
+
|
|
815
|
+
What does not fit either reading is the Remote Config panel Aaronia
|
|
816
|
+
published in 2021 and again in 2022: a full V6 with the clock set to
|
|
817
|
+
`92MHz` reporting roughly 92.16 MHz of IQ samples per second. That
|
|
818
|
+
counter is not the delivered rate — measured on an ECO, `iqsamples`
|
|
819
|
+
held at 61.44 MHz while the device was actually delivering 15.36 and
|
|
820
|
+
then 7.68 MHz, so it reports the native undecimated rate. Taken at face
|
|
821
|
+
value it says a full V6 at a 92 MHz clock has a native rate of
|
|
822
|
+
92.16 MHz, which the 1.5 rule forbids.
|
|
823
|
+
|
|
824
|
+
No full V6 has been available to measure, so this stays open. The
|
|
825
|
+
practical guidance is unchanged: the ECO path is verified rung by rung,
|
|
826
|
+
and for anything else read the rate the device reports in its stream
|
|
827
|
+
metadata rather than trusting a computed ladder.
|
|
618
828
|
|
|
619
829
|
### Two things that do not carry over
|
|
620
830
|
|
|
@@ -638,6 +848,20 @@ transports drive the same divider.
|
|
|
638
848
|
- `/info` - Device information
|
|
639
849
|
- `/healthstatus` - Device health monitoring
|
|
640
850
|
|
|
851
|
+
**HTTP Server and Client blocks are themselves licensed, and the free
|
|
852
|
+
tier is one of each.** Aaronia's staff put it plainly in the HTTP
|
|
853
|
+
Server thread: only one HTTP Server instance is included in the free
|
|
854
|
+
RTSA-Suite PRO licence and additional instances must be licensed
|
|
855
|
+
separately, the same going for the HTTP Client, where only one
|
|
856
|
+
connection is free. Stream Merger and Stream Splitter, the blocks that
|
|
857
|
+
would otherwise let several streams share one connection, are not in
|
|
858
|
+
the free licence either.
|
|
859
|
+
|
|
860
|
+
This is the licence limit most likely to be met in practice: running
|
|
861
|
+
this crate and a second client — a SoapySDR application, say — against
|
|
862
|
+
one server at the same time is a second connection. It has nothing to
|
|
863
|
+
do with the Remote Config licence discussed below.
|
|
864
|
+
|
|
641
865
|
**Remote Configuration** (`/remoteconfig`):
|
|
642
866
|
- Device parameter configuration.
|
|
643
867
|
- Aaronia sells a "Remote Config" license, and this document previously
|
|
@@ -655,10 +879,17 @@ transports drive the same divider.
|
|
|
655
879
|
and may be what actually matters.
|
|
656
880
|
- This crate does not depend on the answer: it tunes through `/control`,
|
|
657
881
|
which needs no license.
|
|
882
|
+
- Re-confirmed 2026-08-12 against RTSA-Suite PRO and a V6 ECO on the
|
|
883
|
+
same unlicensed system, in both payload forms, for `centerfreq0`,
|
|
884
|
+
`decimation0`, `reflevel0` and `calibration/preamp`.
|
|
658
885
|
|
|
659
886
|
**License Detection Methods**:
|
|
660
887
|
|
|
661
|
-
**Important**:
|
|
888
|
+
**Important**: reads of `/remoteconfig` are license-free, so a read-only
|
|
889
|
+
check cannot prove write capability either way — only a write test can.
|
|
890
|
+
It cannot prove the *license* either: writes succeeded on an unlicensed
|
|
891
|
+
system (see above), so a successful probe means "this server accepts
|
|
892
|
+
writes", not "this server is licensed".
|
|
662
893
|
|
|
663
894
|
The client therefore exposes two methods:
|
|
664
895
|
|
|
@@ -793,8 +1024,10 @@ fn parse_iq_int16(data: &[u8], metadata_scale: f32) -> Vec<Complex32> {
|
|
|
793
1024
|
|---------|------|---------|
|
|
794
1025
|
| 1.0 | 2025-01-11 | Initial HTTP specification from original documentation |
|
|
795
1026
|
| 2.0 | 2025-01-11 | Enhanced with comprehensive streaming protocol specification and implementation guidelines |
|
|
1027
|
+
| 2.4 | 2026-08-12 | Added Aaronia support's full `/control` settings list (`deviceconnect`, `camera`, per-type fields, `receiverUUID`/`receiverName` scoping), which supersedes the specification's claim that commands cannot be addressed to a block; documented that an unrecognised `format=` silently serves the RTSA file format and that `raw16` aliases `int16`, both verified live |
|
|
1028
|
+
| 2.3 | 2026-08-12 | Folded in Aaronia's endpoint specification (rev 11) and the block forum threads: `/control` broadcasts to every block and is PUT-only, the server drops data past an 8 MB outbound buffer, `/healthstatus` subgroups and the fields a V6 ECO reports, and the one-server/one-client free-licence limit. Measured that `status/iqsamples` is the native rate, not the delivered one. Corrected the marker-stream entry: it declares `payload: "spectra"`, so its nested samples are the spectra form and not a counter-example to flat categories |
|
|
1029
|
+
| 2.2 | 2026-08-12 | Verified Aaronia's V6 remote control notes (rev 4) against hardware: enum writes by index, multi-group and non-`main` `simpleconfig` PUTs, the silent no-op on an unknown block name, the ignored receiver name in the config-tree form; documented mission loading and the `type` requirement on `/control`, the absence of a status endpoint, and the unresolved conflict over what "Full" means on a full V6; resolved a contradiction over what the Remote Config licence gates |
|
|
796
1030
|
| 2.1 | 2026-08-06 | Live-hardware corrections folded in (two-byte separator, spectra frame counting, `scale` inversion); documented `/samples`, the `/sample` TX push, and the `simpleconfig` PUT form; corrected `limit` semantics, field types, and flat histogram/categories sample arrays; removed decorative icons |
|
|
797
|
-
|
|
798
1031
|
---
|
|
799
1032
|
|
|
800
1033
|
## Sources and Attribution
|