python-aaronia 0.6.0__tar.gz → 0.6.2__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.6.0 → python_aaronia-0.6.2}/CHANGELOG.md +86 -3
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/Cargo.lock +2 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/Cargo.toml +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/DESIGN.md +2 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/PKG-INFO +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/PLUGINS.md +9 -1
- python_aaronia-0.6.2/README.md +212 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/APPS.md +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/HTTPSPEC.md +105 -5
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/QUICKSTART.md +32 -13
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/SDKSPEC.md +58 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/USAGE.md +28 -2
- python_aaronia-0.6.2/docs/VERIFICATION.md +59 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/python-aaronia/Cargo.toml +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/soapy-aaronia/AaroniaSoapyDevice.cpp +48 -7
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/http_endpoints.rs +21 -7
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/lib.rs +3 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/native_sdk.rs +244 -12
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/unified_sink.rs +5 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/unified_source.rs +80 -4
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/utils.rs +196 -7
- python_aaronia-0.6.0/README.md +0 -291
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/.cargo/config.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/.gitattributes +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/.gitignore +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/CONTRIBUTING.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/LICENSE +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/benches/decompress_block.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/benches/deinterleave_dual_iq.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/benches/parse_int16_packet.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/benches/rtsa_open_and_read.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/deny.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/docs/FILESPEC.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/channel_hopping.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/device_control.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/dump_metadata.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/http_iq_quickstart.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/native_sdk_basic.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/native_sdk_transmit.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/noaa_scanner.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/python_arrow_example.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/read_rtsa_file.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/examples/soapy_python_example.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/include/aaronia.h +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/pyproject.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/python-aaronia/README.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/python-aaronia/aaronia.pyi +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/python-aaronia/src/lib.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/python-aaronia/test_basic.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/scripts/ci-local.sh +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/soapy-aaronia/AaroniaSoapyDevice.hpp +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/soapy-aaronia/CMakeLists.txt +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/soapy-aaronia/README.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/soapy-aaronia/Registration.cpp +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/c_api.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/decompression.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/detection.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/error.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/file_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/http_sink.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/http_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/http_streaming.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/sdk_sink.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/sdk_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/sdr_source_impl.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/src/seify_impl.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/c_api_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/http_mock_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/http_resilience_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/http_sink_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/integration_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/live_smoke.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/native_sdk_load.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/properties.proptest-regressions +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/properties.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/rtsa_negative_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/sdr_source_impl_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/spec_coverage.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/test_cw_mag.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.2}/tests/test_cw_meta.rs +0 -0
|
@@ -4,6 +4,89 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.6.2] - 2026-08-12
|
|
8
|
+
|
|
9
|
+
## [v0.6.1] - 2026-08-12
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **The SoapySDR plugin advertised sample rates the hardware cannot
|
|
13
|
+
produce.** Seven of the ten rates it listed, 1, 2, 5, 10 and 20 MHz
|
|
14
|
+
among them, do not exist on the device, which runs at 61.44 MHz
|
|
15
|
+
divided by a power of two. Applications build their rate dropdowns
|
|
16
|
+
from that list, so choosing 10 MHz ran the device at a different rate
|
|
17
|
+
while the application went on displaying 10. The list is now the real
|
|
18
|
+
ladder, 61.44 MHz down to 120 kHz, and `setSampleRate` snaps to the
|
|
19
|
+
nearest one and logs when it has to.
|
|
20
|
+
- **The crate reported the requested sample rate rather than the one in
|
|
21
|
+
use.** The device adjusts a rate it cannot produce, so
|
|
22
|
+
`get_source_info()` described a capture that was not happening. HTTP
|
|
23
|
+
sources now report the rate, centre frequency and usable bandwidth
|
|
24
|
+
from the stream's own metadata once packets arrive.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `iq_sample_rates`, `usable_bandwidth_hz`, `nearest_iq_sample_rate` and
|
|
28
|
+
`iq_sample_rate_for_bandwidth` in `utils`, with the constants
|
|
29
|
+
`IQ_CLOCK_HZ` and `USABLE_BANDWIDTH_RATIO`. The device samples at
|
|
30
|
+
61.44 MHz divided by a power of two and delivers 0.8 of that as
|
|
31
|
+
alias-free bandwidth. Callers were deriving their own rates from
|
|
32
|
+
guesses, so the relationships now live in one tested place. Wanting
|
|
33
|
+
8 MHz of spectrum needs 10 MHz of sampling, and
|
|
34
|
+
`iq_sample_rate_for_bandwidth` returns the 15.36 MHz that provides it.
|
|
35
|
+
`iq_sample_rates_for_clock` covers devices whose receiver clock is not
|
|
36
|
+
the default: a V6 ECO has a fixed clock and gives the measured ladder,
|
|
37
|
+
while a full V6 can select a faster one and reach further. Aaronia's
|
|
38
|
+
samples set `device/receiverclock` to "92MHz" or "245MHz"; only the
|
|
39
|
+
default has been checked against hardware.
|
|
40
|
+
|
|
41
|
+
### Added (native SDK)
|
|
42
|
+
- **Device-family auto-detection.** `detect_device_family` and
|
|
43
|
+
`open_detected_device` try each known family in turn, so an ECO owner
|
|
44
|
+
no longer has to know that the default `spectranv6` will not find
|
|
45
|
+
their device and that `spectranv6eco` is the string they needed.
|
|
46
|
+
- **`read_spectra`**, with the stream index taken from the open mode
|
|
47
|
+
rather than assumed. `spectranv6/raw` carries spectra on stream 2 and
|
|
48
|
+
IQ on stream 0; every other mode uses stream 0. Hardware-unverified.
|
|
49
|
+
- **`receiver_clock_hz`** on the native source, and
|
|
50
|
+
`spectranv6eco/rtsa` added to the known open modes. The clock sets the
|
|
51
|
+
rate ladder's ceiling, so callers that need to know which rates exist
|
|
52
|
+
can now ask instead of assuming.
|
|
53
|
+
|
|
54
|
+
### Fixed (native SDK)
|
|
55
|
+
- **The V6 ECO's fixed receiver clock was recorded as 61.44 MHz.** It is
|
|
56
|
+
92.16 MHz: an ECO streams at 61.44 MHz sampling, measured against real
|
|
57
|
+
hardware, and the constraint checked at configuration time is
|
|
58
|
+
`span * 1.5 <= clock`. The old value rejected every span above
|
|
59
|
+
40.96 MHz, including the device's own maximum.
|
|
60
|
+
- **Dual-channel capture selected the wrong mode and would have
|
|
61
|
+
returned corrupted samples.** `RxChannel::Rx1And2` wrote
|
|
62
|
+
`device/receiverchannel = "Rx1+Rx2"`, which delivers the two inputs as
|
|
63
|
+
two independent streams at indices 0 and 1. This crate reads a single
|
|
64
|
+
stream and deinterleaves it, which is the contract of the other mode,
|
|
65
|
+
`"Rx12"`. On a two-input V6 the result would have been Rx1's samples
|
|
66
|
+
split into two bogus channels, with no error anywhere. It now writes
|
|
67
|
+
`"Rx12"`. Aaronia's `RawIQ2RX` and `RawIQ2RXInterleave` samples
|
|
68
|
+
demonstrate one mode each. Still hardware-unverified.
|
|
69
|
+
- **Sweep mode set the wrong resolution-bandwidth key.** It sent
|
|
70
|
+
`main/rbw`, which no Aaronia sample uses; the key is `main/rbwfreq`.
|
|
71
|
+
Checked against Aaronia's published `SweepSpectrumEco` sample, which
|
|
72
|
+
also confirms `main/startfreq`, `main/stopfreq`, `main/reflevel` and
|
|
73
|
+
the `spectranv6eco/sweepsa` open string that this crate already used.
|
|
74
|
+
The sweep path remains hardware-unverified.
|
|
75
|
+
|
|
76
|
+
### Documentation
|
|
77
|
+
- **GPS time needs GPS switched on, and the crate does not do it.**
|
|
78
|
+
Devices ship with `device/gpsmode` disabled, so `get_gps_time` would
|
|
79
|
+
return `None` indefinitely and appear broken. Aaronia's `GPSTime`
|
|
80
|
+
sample sets `device/gpsmode` to "Location and Time" and
|
|
81
|
+
`device/sclksource` to "GPS Provider" before starting the device;
|
|
82
|
+
`get_gps_time` now says so.
|
|
83
|
+
- Documented what `/control`'s `frequencySpan` actually means. It is a
|
|
84
|
+
request for usable bandwidth, not a sample rate: the device picks the
|
|
85
|
+
rate whose alias-free span is nearest, so 2.5 MHz yields 3.84 MHz and
|
|
86
|
+
10 MHz yields 15.36 MHz. Values on the rate ladder round-trip exactly,
|
|
87
|
+
which is why the field looks like a sample rate in ordinary use.
|
|
88
|
+
Verified across nine requests on a V6 ECO.
|
|
89
|
+
|
|
7
90
|
## [v0.6.0] - 2026-08-11
|
|
8
91
|
|
|
9
92
|
Reliability and documentation release. The HTTP backend now handles a
|
|
@@ -80,9 +163,9 @@ hardware and which have not.
|
|
|
80
163
|
### Release
|
|
81
164
|
- Release archives now carry the module, install instructions and the
|
|
82
165
|
licence, and their names state the version, OS and architecture.
|
|
83
|
-
- The Linux module ships stripped of debug symbols, down
|
|
84
|
-
|
|
85
|
-
publishing it.
|
|
166
|
+
- The Linux module ships stripped of debug symbols, 19.1 MB down to
|
|
167
|
+
15.4 MB. The rest is statically linked Rust, not symbols. The release
|
|
168
|
+
job checks the stripped module still loads before publishing it.
|
|
86
169
|
- GitHub releases now use this file's entry for the tag as their
|
|
87
170
|
description, with the generated commit list below it.
|
|
88
171
|
|
|
@@ -3027,7 +3027,7 @@ dependencies = [
|
|
|
3027
3027
|
|
|
3028
3028
|
[[package]]
|
|
3029
3029
|
name = "python-aaronia"
|
|
3030
|
-
version = "0.6.
|
|
3030
|
+
version = "0.6.2"
|
|
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.6.
|
|
3559
|
+
version = "0.6.2"
|
|
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.6.
|
|
5
|
+
version = "0.6.2"
|
|
6
6
|
edition = "2024"
|
|
7
7
|
repository = "https://github.com/isaacbentley/sdr-aaronia-rs"
|
|
8
8
|
readme = "README.md"
|
|
@@ -51,7 +51,7 @@ Available on Windows and Linux only, behind the non-default `native-sdk` feature
|
|
|
51
51
|
- **IQ mode constraint**: enforces `span * 1.5 <= receiverclock` by reading the live device clock before streaming.
|
|
52
52
|
- **Data path (RX)**: polls `GetPacket` on the C++ side every 5 ms. Where the packet stride allows, the raw sample buffer is reinterpreted in place as `Complex32` (no per-sample conversion) before being copied into the caller's buffer.
|
|
53
53
|
- **Data path (TX)**: provides an experimental `TxStream` API built on `SendPacket`. This path is hardware-unverified pending full V6 testing, as the ECO model lacks TX.
|
|
54
|
-
- **Sweep-spectra mode**: configures non-IQ sweeping via `SweepsaConfig` by modifying `"main/startfreq"`, `"main/stopfreq"`, `"main/
|
|
54
|
+
- **Sweep-spectra mode**: configures non-IQ sweeping via `SweepsaConfig` by modifying `"main/startfreq"`, `"main/stopfreq"`, `"main/rbwfreq"` and `"main/reflevel"`. These key names match Aaronia's own `SweepSpectrumEco` sample; the code previously sent `"main/rbw"`, which no sample uses. Supports the `"spectranv6/sweepsa"` and `"spectranv6eco/sweepsa"` open strings, both of which the samples confirm. The path itself remains hardware-unverified.
|
|
55
55
|
- **Health and GPS telemetry**: exposes `HealthState` and `GpsState` structs populated by a recursive configuration tree-walker (`walk_health_tree`) that fetches device diagnostics dynamically. Not yet exercised against live hardware.
|
|
56
56
|
|
|
57
57
|
### 4.2 HTTP Streaming
|
|
@@ -72,7 +72,7 @@ Available on Windows and Linux only, behind the non-default `native-sdk` feature
|
|
|
72
72
|
Under the `sdr-source` feature, `AaroniaSdrSource` (in `sdr_source_impl.rs`) implements the `SdrSource` trait from the external `orecchiette-sdr-source-rs` crate, which the crate re-exports as `sdr_source`. Automatic mid-stream retuning via `SourceConfig.channels_hz` is supported per backend:
|
|
73
73
|
|
|
74
74
|
- **Native SDK**: re-issues `configure_iq_receiver` with the new center frequency, applying the change to the open device handle via the `main/centerfreq` configuration key. No stream restart is required.
|
|
75
|
-
- **HTTP**: `AaroniaSource::set_center_frequency` wraps `HttpEndpointsClient::configure_capture` as a `PUT` to the license-free `/control` endpoint, always sending the complete capture tuple (center, span, reference level) with unchanged values filled from the cached config. The full tuple is required: RTSA servers silently ignore a capture `PUT` carrying only one of the two frequency fields (it returns `{"success":true}` but the device stays put — live-verified).
|
|
75
|
+
- **HTTP**: `AaroniaSource::set_center_frequency` wraps `HttpEndpointsClient::configure_capture` as a `PUT` to the license-free `/control` endpoint, always sending the complete capture tuple (center, span, reference level) with unchanged values filled from the cached config. The full tuple is required: RTSA servers silently ignore a capture `PUT` carrying only one of the two frequency fields (it returns `{"success":true}` but the device stays put — live-verified). Hopping deliberately avoids `/remoteconfig` and is not gated on the license probe. (Whether that path needs the "Remote Config" license at all is unconfirmed: a system without the license accepted `/remoteconfig` writes in testing. See docs/HTTPSPEC.md.)
|
|
76
76
|
- **File**: not supported.
|
|
77
77
|
|
|
78
78
|
Hop dwell deadlines come from `sdr_source::DwellController`; per-hop pacing additionally inserts a ~75 ms settle after every retune (`RETUNE_SETTLE` in `sdr_source_impl.rs`) to ride out the RTSA's apply-config latency and flush stale samples from the pipeline.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aaronia
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
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+)
|
|
@@ -122,7 +122,15 @@ Two different knobs, easy to confuse:
|
|
|
122
122
|
sdr = SoapySDR.Device("driver=aaronia,url=http://atc.local:54664,format=I16")
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
## 3.
|
|
125
|
+
## 3. Transmit
|
|
126
|
+
|
|
127
|
+
TX is available through the SoapySDR plugin only when it is built
|
|
128
|
+
against the native SDK on Windows or Linux. Elsewhere `setupStream` for
|
|
129
|
+
TX fails with a descriptive error. Bursts are sent for immediate
|
|
130
|
+
transmission; timed TX is not supported. The whole TX path is
|
|
131
|
+
hardware-unverified. See [docs/VERIFICATION.md](docs/VERIFICATION.md).
|
|
132
|
+
|
|
133
|
+
## 4. Metrics and Error Handling
|
|
126
134
|
|
|
127
135
|
Both the native Rust API and the Python/C++ bindings expose critical metrics to monitor the health of your RTSA stream:
|
|
128
136
|
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# sdr-aaronia-rs
|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/sdr-aaronia-rs)
|
|
4
|
+
[](https://docs.rs/sdr-aaronia-rs)
|
|
5
|
+
[](https://github.com/isaacbentley/sdr-aaronia-rs/actions/workflows/ci.yml)
|
|
6
|
+
[](https://choosealicense.com/licenses/gpl-3.0/)
|
|
7
|
+
|
|
8
|
+
One API for Aaronia SPECTRAN analyzers and SDRs, whether the samples
|
|
9
|
+
come from the native SDK, an RTSA-Suite HTTP server, or a recorded file.
|
|
10
|
+
Python bindings and a SoapySDR plugin come from the same engine.
|
|
11
|
+
|
|
12
|
+
*Disclaimer: This project is not affiliated with Aaronia AG. Aaronia, SPECTRAN, and RTSA-Suite PRO are trademarks of Aaronia AG.*
|
|
13
|
+
|
|
14
|
+
Working with a SPECTRAN usually means choosing a transport first and
|
|
15
|
+
then writing against whatever API that transport exposes. `AaroniaSource`
|
|
16
|
+
removes the choice: point it at a file, a URL, or nothing at all, and it
|
|
17
|
+
selects a backend and presents the same interface either way.
|
|
18
|
+
|
|
19
|
+
| What you configure | What it uses |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `file_path` | Buffered playback of an RTSA capture file |
|
|
22
|
+
| `http_base_url` | HTTP streaming from an RTSA-Suite server block |
|
|
23
|
+
| Neither | The native SDK, falling back to `localhost:54664` |
|
|
24
|
+
| `force_source_type` | Exactly the backend you name |
|
|
25
|
+
|
|
26
|
+
## What it does
|
|
27
|
+
|
|
28
|
+
- **Streams IQ and spectra over HTTP** in JSON, Int16, Float16 or
|
|
29
|
+
Float32, with Basic and token authentication. Retuning mid-stream
|
|
30
|
+
needs no Aaronia licence, and dropped streams reconnect on their own.
|
|
31
|
+
- **Reads `.rtsa` capture files** through buffered I/O, with metadata
|
|
32
|
+
extraction and multi-stream support.
|
|
33
|
+
- **Talks to the hardware directly** through the Aaronia SDK on Windows
|
|
34
|
+
and Linux, including transmit. The SDK is not available for macOS.
|
|
35
|
+
- **Controls and monitors the device**: streaming parameters, health,
|
|
36
|
+
input enumeration and the configuration tree.
|
|
37
|
+
- **Plugs into FutureSDR** with `HttpSource` and `HttpSink` flowgraph
|
|
38
|
+
blocks.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Add the following to your `Cargo.toml`:
|
|
43
|
+
|
|
44
|
+
```toml
|
|
45
|
+
[dependencies]
|
|
46
|
+
# By default, includes HTTP, File, native sdr-source trait, and C FFI backend support
|
|
47
|
+
sdr-aaronia-rs = "0.6"
|
|
48
|
+
tokio = { version = "1.43", features = ["rt-multi-thread", "macros"] }
|
|
49
|
+
|
|
50
|
+
# To enable additional backends, opt into their features (e.g. native-sdk, futuresdr)
|
|
51
|
+
# sdr-aaronia-rs = { version = "0.6", features = ["native-sdk", "futuresdr"] }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
Set the RF parameters and read:
|
|
57
|
+
|
|
58
|
+
```rust,no_run
|
|
59
|
+
use sdr_aaronia_rs::{AaroniaSource, AaroniaConfig};
|
|
60
|
+
use anyhow::Result;
|
|
61
|
+
|
|
62
|
+
#[tokio::main]
|
|
63
|
+
async fn main() -> Result<()> {
|
|
64
|
+
let config = AaroniaConfig::default()
|
|
65
|
+
.center_frequency(446.0e6) // 446 MHz
|
|
66
|
+
.span_frequency(10.0e6) // 10 MHz span
|
|
67
|
+
.reference_level(-30.0); // -30 dBm
|
|
68
|
+
|
|
69
|
+
let mut source = AaroniaSource::new(config).await?;
|
|
70
|
+
|
|
71
|
+
let mut buffer = Vec::with_capacity(1024);
|
|
72
|
+
let n = source.read_samples(&mut buffer, 1024).await?;
|
|
73
|
+
println!("Received {} IQ samples", n);
|
|
74
|
+
|
|
75
|
+
Ok(())
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
[docs/USAGE.md](docs/USAGE.md) has worked examples for everything the
|
|
82
|
+
quickstart leaves out: the builder pattern, explicit backend selection,
|
|
83
|
+
wire formats and network bandwidth, configuration profiles, device
|
|
84
|
+
control, FutureSDR integration, authentication and low-level stream
|
|
85
|
+
access. Its Rust snippets are compiled as doctests, and it indexes the
|
|
86
|
+
runnable programs in [`examples/`](examples/).
|
|
87
|
+
|
|
88
|
+
[docs/QUICKSTART.md](docs/QUICKSTART.md) covers configuring the
|
|
89
|
+
RTSA-Suite HTTP Server block, which all of the above depends on.
|
|
90
|
+
|
|
91
|
+
## Using it from other tools
|
|
92
|
+
|
|
93
|
+
The Rust crate is the engine. The same code drives three other
|
|
94
|
+
surfaces, so an Aaronia device works in the tools people already use.
|
|
95
|
+
|
|
96
|
+
### Python
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install python-aaronia
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import aaronia
|
|
104
|
+
|
|
105
|
+
cfg = aaronia.AaroniaConfig()
|
|
106
|
+
cfg.http_base_url = "http://localhost:54664"
|
|
107
|
+
cfg.center_freq = 2.44e9
|
|
108
|
+
cfg.sample_rate = 15.36e6
|
|
109
|
+
|
|
110
|
+
src = aaronia.AaroniaSource()
|
|
111
|
+
src.start_streaming(cfg)
|
|
112
|
+
samples = src.read_samples_numpy(65536) # numpy complex64
|
|
113
|
+
src.stop_streaming()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Reads land in NumPy or PyArrow with one copy out of the receive buffer.
|
|
117
|
+
Blocking calls release the GIL, errors arrive as typed exceptions, and
|
|
118
|
+
the package ships type stubs. Wheels are abi3 for CPython 3.9 and
|
|
119
|
+
later. Full reference: [python-aaronia/README.md](python-aaronia/README.md).
|
|
120
|
+
|
|
121
|
+
### SoapySDR: GQRX, SDR++, GNU Radio and others
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
SoapySDRUtil --probe="driver=aaronia,url=http://localhost:54664"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Every release attaches a prebuilt plugin for Linux, macOS and Windows,
|
|
128
|
+
so no toolchain is needed. The plugin streams CF32 and CS16, honours
|
|
129
|
+
`timeoutUs` with partial reads, stays safe to retune while streaming,
|
|
130
|
+
and reports timestamps and dropped-block counts.
|
|
131
|
+
|
|
132
|
+
Per-application setup is in [docs/APPS.md](docs/APPS.md). Installation,
|
|
133
|
+
building from source and the wire-format trade-offs are in
|
|
134
|
+
[PLUGINS.md](PLUGINS.md); pass `format=I16` to halve network bandwidth,
|
|
135
|
+
which is a real wire-format change rather than a client-side conversion.
|
|
136
|
+
|
|
137
|
+
### seify (Rust-native)
|
|
138
|
+
|
|
139
|
+
Enable the `seify` feature and construct the device with
|
|
140
|
+
`AaroniaSeifyDevice::from_args`. It is not part of seify's built-in
|
|
141
|
+
enumeration, so it will not appear in `seify::enumerate()`. See
|
|
142
|
+
[PLUGINS.md](PLUGINS.md).
|
|
143
|
+
|
|
144
|
+
### What has been tested
|
|
145
|
+
|
|
146
|
+
Not every path has run against hardware. Transmit, dual-channel capture
|
|
147
|
+
and the native-SDK backend have not.
|
|
148
|
+
[docs/VERIFICATION.md](docs/VERIFICATION.md) gives the status of each
|
|
149
|
+
feature and how it was checked.
|
|
150
|
+
|
|
151
|
+
## Connection Resilience
|
|
152
|
+
|
|
153
|
+
Connecting (the `/info` probe and initial tuning PUT) retries transient
|
|
154
|
+
failures up to 4 times with exponential backoff, bounded by a 10 second
|
|
155
|
+
total budget. Refused connections, unresolved DNS and 5xx/408/429
|
|
156
|
+
responses are retried; 4xx and configuration errors fail on the first
|
|
157
|
+
attempt. This matters for `*.local` hostnames, which refuse the first
|
|
158
|
+
connection from a cold process while mDNS resolves.
|
|
159
|
+
|
|
160
|
+
`AaroniaConfig::read_timeout` (default 30 s) bounds `read_samples`.
|
|
161
|
+
`read_samples_deadline`, and therefore the SoapySDR and seify paths, uses
|
|
162
|
+
its caller's per-call deadline instead.
|
|
163
|
+
|
|
164
|
+
A dropped HTTP stream, from an RTSA restart or a network interruption,
|
|
165
|
+
reconnects automatically. This is `AaroniaConfig::auto_reconnect`,
|
|
166
|
+
enabled by default. The reader reopens the stream, re-applies the
|
|
167
|
+
current tuning (a restarted server returns to its mission's frequency),
|
|
168
|
+
and flags the first packet after the gap as an overrun so callers know
|
|
169
|
+
samples were missed. After 5 attempts, roughly 8 seconds of backoff, it
|
|
170
|
+
stops and reads report a closed stream, matching the behaviour of
|
|
171
|
+
`auto_reconnect(false)`.
|
|
172
|
+
|
|
173
|
+
## Feature Flags
|
|
174
|
+
|
|
175
|
+
Functionality is grouped behind Cargo features so unused dependencies stay out of your build:
|
|
176
|
+
|
|
177
|
+
| Feature | Description | Default |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `http` | HTTP streaming via `reqwest` and `tokio`. | **Yes** |
|
|
180
|
+
| `file` | Buffered RTSA file parsing. | **Yes** |
|
|
181
|
+
| `native-sdk` | Links the proprietary Aaronia C++ SDK. Windows/Linux only. | No |
|
|
182
|
+
| `futuresdr` | Enables the FutureSDR block API: `HttpSource`, `HttpSink`, and their builders. Implies `http`. | No |
|
|
183
|
+
| `sdr-source` | Integrates `AaroniaSdrSource` implementing the native `SdrSource` traits. | **Yes** |
|
|
184
|
+
| `ffi` | Builds the C-API export layer. | **Yes** |
|
|
185
|
+
|
|
186
|
+
## Testing & Contributing
|
|
187
|
+
|
|
188
|
+
The test suite consists of unit tests, integration tests against LFS captures, and property tests enforcing specification invariants.
|
|
189
|
+
|
|
190
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for detailed instructions on running the test suite, generating coverage reports, and formatting your code before submitting a Pull Request.
|
|
191
|
+
|
|
192
|
+
## Documentation
|
|
193
|
+
|
|
194
|
+
Start here:
|
|
195
|
+
|
|
196
|
+
- [Quickstart](docs/QUICKSTART.md) — configuring an RTSA-Suite mission, first samples in Rust, Python and SoapySDR, and troubleshooting for common setup failures.
|
|
197
|
+
- [Usage](docs/USAGE.md) — worked examples for each part of the API, plus the `AARONIA_SDK_PATH` and `AARONIA_USER_AGENT` environment variables.
|
|
198
|
+
- [Using existing SDR apps](docs/APPS.md) — SDR++, GQRX, GNU Radio, SoapySDR from Python.
|
|
199
|
+
|
|
200
|
+
Reference:
|
|
201
|
+
|
|
202
|
+
- [Verification status](docs/VERIFICATION.md) — which features have been tested against real hardware.
|
|
203
|
+
- [SDR plugins](PLUGINS.md) — SoapySDR and seify setup, wire-format trade-offs, metrics.
|
|
204
|
+
- [Architecture & Design](DESIGN.md) — Internal architecture and execution flow.
|
|
205
|
+
- [RTSA File Format Specification](docs/FILESPEC.md) — On-disk `.rtsa` capture-file format and how this crate parses it.
|
|
206
|
+
- [HTTP API Specification](docs/HTTPSPEC.md) — The RTSA HTTP streaming and control API, including Remote Config licence detection.
|
|
207
|
+
- [Native SDK Specification](docs/SDKSPEC.md) — The Aaronia RTSA-Suite PRO SDK surface and the Rust binding notes.
|
|
208
|
+
- [Changelog](CHANGELOG.md) — Release history.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later) - see the LICENSE file for details.
|
|
@@ -73,7 +73,7 @@ from SoapySDR import SOAPY_SDR_RX, SOAPY_SDR_CF32
|
|
|
73
73
|
import numpy as np
|
|
74
74
|
|
|
75
75
|
sdr = SoapySDR.Device("driver=aaronia,url=http://localhost:54664")
|
|
76
|
-
sdr.setSampleRate(SOAPY_SDR_RX, 0,
|
|
76
|
+
sdr.setSampleRate(SOAPY_SDR_RX, 0, 15.36e6)
|
|
77
77
|
sdr.setFrequency(SOAPY_SDR_RX, 0, 2.44e9)
|
|
78
78
|
|
|
79
79
|
rx = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32)
|
|
@@ -283,6 +283,16 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
283
283
|
}
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
+
> **`frequencySpan` is a usable-bandwidth request, not a sample rate.**
|
|
287
|
+
> The device runs at 61.44 MHz divided by a power of two. Given a span
|
|
288
|
+
> it cannot produce, it selects the rate whose alias-free bandwidth
|
|
289
|
+
> (0.8 x Fs) is nearest the request: 2.5 MHz yields Fs = 3.84 MHz,
|
|
290
|
+
> 1.3 MHz yields 1.92 MHz, and 10 MHz yields 15.36 MHz. A value that is
|
|
291
|
+
> itself on the rate ladder round-trips exactly, which is why the field
|
|
292
|
+
> behaves like a sample rate in ordinary use. `sampleFrequency` in the
|
|
293
|
+
> packet metadata reports the rate in force; `startFrequency` and
|
|
294
|
+
> `endFrequency` bound the usable span. Verified on a V6 ECO.
|
|
295
|
+
|
|
286
296
|
> **Both frequency fields are required for a retune to apply.** Live
|
|
287
297
|
> testing against RTSA-Suite PRO (HTTP server block fed by a SPECTRAN
|
|
288
298
|
> V6 ECO) shows the server returns `{"success":true}` for a capture
|
|
@@ -388,6 +398,44 @@ packet-metadata object, so `payload`, `minPower`, `maxPower`, and
|
|
|
388
398
|
}
|
|
389
399
|
```
|
|
390
400
|
|
|
401
|
+
**Full PUT form** (config tree). Aaronia's own automation example uses this
|
|
402
|
+
shape for every setting it changes:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"request": 11,
|
|
407
|
+
"receiverName": "Block_Spectran_V6B_0",
|
|
408
|
+
"config": {
|
|
409
|
+
"type": "group",
|
|
410
|
+
"items": [
|
|
411
|
+
{
|
|
412
|
+
"type": "group",
|
|
413
|
+
"name": "main",
|
|
414
|
+
"items": [
|
|
415
|
+
{ "type": "float", "name": "centerfreq", "value": 2440000000 }
|
|
416
|
+
]
|
|
417
|
+
}
|
|
418
|
+
]
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Notes verified against a live V6 ECO:
|
|
424
|
+
|
|
425
|
+
- **Field names are model-specific.** The V6B example writes `centerfreq`;
|
|
426
|
+
a V6 ECO exposes `centerfreq0` and `centerfreq1` for its two channels.
|
|
427
|
+
Read `GET /remoteconfig` and use the names that device reports.
|
|
428
|
+
- **A frequency change needs only the frequency field.** This differs from
|
|
429
|
+
`/control`, where a capture request is ignored unless `frequencyCenter`
|
|
430
|
+
and `frequencySpan` are both present.
|
|
431
|
+
- **The `receiverName` key is case-tolerant.** `receivername` was accepted
|
|
432
|
+
identically.
|
|
433
|
+
- Enum-valued settings are written as their label string, for example
|
|
434
|
+
`decimation` as `"1 / 128"`, not as an index.
|
|
435
|
+
- Other keys the example writes: `run` (bool), `preamp` (`"Auto"`),
|
|
436
|
+
`reflevel` (float), and `filerecord` (bool) plus a filename template on
|
|
437
|
+
a FileWriter block, which is how it starts and stops recording.
|
|
438
|
+
|
|
391
439
|
**Simplified PUT form**: in addition to the full `{request, config}` shape, the server accepts a shorter per-block write (used by the SDR++ and SDRangel Aaronia plugins), exposed via `HttpEndpointsClient::simple_remote_config`:
|
|
392
440
|
```json
|
|
393
441
|
{
|
|
@@ -542,6 +590,44 @@ Data captured using antennas with location or directional information.
|
|
|
542
590
|
| `azimuth` | Azimuth of directional antenna |
|
|
543
591
|
| `declination` | Declination of directional antenna |
|
|
544
592
|
|
|
593
|
+
## What the native SDK samples tell us about this API
|
|
594
|
+
|
|
595
|
+
Aaronia's [C++ SDK samples](https://github.com/Aaronia-Open-source/RTSA-API-Samples)
|
|
596
|
+
drive the same hardware through a different transport, so some of what
|
|
597
|
+
they establish applies here and some does not.
|
|
598
|
+
|
|
599
|
+
### GPS needs enabling, over HTTP too
|
|
600
|
+
|
|
601
|
+
The `device/gpsmode` and `device/sclksource` keys the `GPSTime` sample
|
|
602
|
+
writes are the same keys this config tree exposes, so the prerequisite
|
|
603
|
+
is identical. A device ships with `gpsmode` set to `Disabled` and takes
|
|
604
|
+
its stream clock from whatever `sclksource` names, which on the system
|
|
605
|
+
tested here was `10MHz` rather than GPS. Timestamps are not
|
|
606
|
+
GPS-disciplined until both change, and nothing reports an error
|
|
607
|
+
meanwhile.
|
|
608
|
+
|
|
609
|
+
Observed on a V6 ECO: `gpsmode` offers `Disabled`, `Location`, `Time`
|
|
610
|
+
and `Location and Time`; `sclksource` offers `Consumer`, `Oscillator`,
|
|
611
|
+
`GPS`, `PPS`, `10MHz` and three `... Provider` variants.
|
|
612
|
+
|
|
613
|
+
### The rate ladder carries over
|
|
614
|
+
|
|
615
|
+
`decimation0` takes the same labels as the SDK's `main/decimation`,
|
|
616
|
+
`"Full"` through `"1 / 512"`, verified by writing one over HTTP. Both
|
|
617
|
+
transports drive the same divider.
|
|
618
|
+
|
|
619
|
+
### Two things that do not carry over
|
|
620
|
+
|
|
621
|
+
- **Receiver-channel selection.** There is no `device/receiverchannel`
|
|
622
|
+
here. The tree exposes per-channel settings instead — `centerfreq0`
|
|
623
|
+
and `centerfreq1`, `decimation0` and `decimation1`, `rfchsource0` and
|
|
624
|
+
`rfchsource1` — and which channels reach the stream is a property of
|
|
625
|
+
the RTSA mission graph. The SDK's `Rx12` versus `Rx1+Rx2` distinction
|
|
626
|
+
has no equivalent.
|
|
627
|
+
- **Stream indices.** The SDK separates IQ from spectra by packet stream
|
|
628
|
+
index. HTTP selects data with the `input=` parameter and reports the
|
|
629
|
+
kind in each packet's `payload` field.
|
|
630
|
+
|
|
545
631
|
## Authorization & Licensing
|
|
546
632
|
|
|
547
633
|
### HTTP Streaming vs Remote Configuration
|
|
@@ -552,10 +638,23 @@ Data captured using antennas with location or directional information.
|
|
|
552
638
|
- `/info` - Device information
|
|
553
639
|
- `/healthstatus` - Device health monitoring
|
|
554
640
|
|
|
555
|
-
**Remote Configuration** (
|
|
556
|
-
-
|
|
557
|
-
-
|
|
558
|
-
|
|
641
|
+
**Remote Configuration** (`/remoteconfig`):
|
|
642
|
+
- Device parameter configuration.
|
|
643
|
+
- Aaronia sells a "Remote Config" license, and this document previously
|
|
644
|
+
stated that `/remoteconfig` writes fail without it. **That is not what
|
|
645
|
+
a live system does.** On RTSA-Suite PRO driving a SPECTRAN V6 ECO whose
|
|
646
|
+
license list contains no Remote Config entry, `PUT /remoteconfig`
|
|
647
|
+
retuned the device repeatedly, in both the config-tree and simplified
|
|
648
|
+
forms, returning HTTP 200 with the change applied.
|
|
649
|
+
- What that license actually gates is therefore unconfirmed. It may
|
|
650
|
+
cover a different feature, or a different edition or version, or
|
|
651
|
+
parameters other than the ones tested. Treat any claim that
|
|
652
|
+
`/remoteconfig` writes require it as unverified.
|
|
653
|
+
- The system tested did hold "Block: HTTP Server" and "Block: HTTP
|
|
654
|
+
Client" licenses, so the HTTP surface itself is licensed separately
|
|
655
|
+
and may be what actually matters.
|
|
656
|
+
- This crate does not depend on the answer: it tunes through `/control`,
|
|
657
|
+
which needs no license.
|
|
559
658
|
|
|
560
659
|
**License Detection Methods**:
|
|
561
660
|
|
|
@@ -564,7 +663,7 @@ Data captured using antennas with location or directional information.
|
|
|
564
663
|
The client therefore exposes two methods:
|
|
565
664
|
|
|
566
665
|
- `detect_remote_config_license()` — **read-only**. Never touches device state. Classifies 401/403 responses; on read success it returns `Unknown` (write capability unproven).
|
|
567
|
-
- `probe_remote_config_write_license()` — **active probe**. Performs a read-modify-restore cycle on `reflevel` (+1 dB, restored best-effort) to positively verify write capability. Use only when you genuinely need proof of `/remoteconfig` write access. Frequency hopping does **not** need it — retuning goes through the license-free `/control` endpoint (see the capture-control note above; the silent-ignore behavior once attributed to licensing was traced to partial capture payloads).
|
|
666
|
+
- `probe_remote_config_write_license()` — **active probe**. Performs a read-modify-restore cycle on `reflevel` (+1 dB, restored best-effort) to positively verify write capability. Use only when you genuinely need proof of `/remoteconfig` write access. `AaroniaSource::probe_remote_config_license()` delegates to it for HTTP sources, and reports `Active` for the file and native-SDK backends, which do not need the licence. Frequency hopping does **not** need it — retuning goes through the license-free `/control` endpoint (see the capture-control note above; the silent-ignore behavior once attributed to licensing was traced to partial capture payloads).
|
|
568
667
|
|
|
569
668
|
**Practical Detection (Active Probe)**:
|
|
570
669
|
```rust
|
|
@@ -707,6 +806,7 @@ consult:
|
|
|
707
806
|
|
|
708
807
|
- **RTSA-Suite PRO HTTP streaming** (Aaronia V6 forum) — [v6-forum.aaronia.de/forum/topic/rtsa-suite-pro-http-streaming](https://v6-forum.aaronia.de/forum/topic/rtsa-suite-pro-http-streaming/)
|
|
709
808
|
- Aaronia RTSA-Suite PRO product documentation — <https://rtsa-manual.aaronia.com/>
|
|
809
|
+
- **Aaronia's own automation example** — [Aaronia-Open-source/python_RTSA_HTTP_API_Sequence_Example](https://github.com/Aaronia-Open-source/python_RTSA_HTTP_API_Sequence_Example). A Python script that runs a measurement sequence and records to disk, driving the device entirely through `PUT /remoteconfig`. It is the source of the config-tree write example below.
|
|
710
810
|
|
|
711
811
|
The content here is derived from the above plus empirical analysis of live
|
|
712
812
|
RTSA-Suite PRO streams. "Aaronia", "RTSA", and "Spectran" are the property of
|
|
@@ -60,7 +60,7 @@ use sdr_aaronia_rs::{AaroniaConfig, AaroniaSource};
|
|
|
60
60
|
async fn main() -> anyhow::Result<()> {
|
|
61
61
|
let config = AaroniaConfig::from_http("http://localhost:54664")
|
|
62
62
|
.center_frequency(2.44e9)
|
|
63
|
-
.span_frequency(
|
|
63
|
+
.span_frequency(15.36e6) // sample rate (Fs), not RF bandwidth
|
|
64
64
|
.reference_level(-20.0);
|
|
65
65
|
|
|
66
66
|
let mut source = AaroniaSource::new(config).await?;
|
|
@@ -77,7 +77,7 @@ async fn main() -> anyhow::Result<()> {
|
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
79
|
# args: <center-hz> <sample-rate-hz> <url>
|
|
80
|
-
cargo run --example http_iq_quickstart --features http -- 2440e6
|
|
80
|
+
cargo run --example http_iq_quickstart --features http -- 2440e6 15.36e6 http://localhost:54664
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
### Python
|
|
@@ -92,7 +92,7 @@ import aaronia
|
|
|
92
92
|
cfg = aaronia.AaroniaConfig()
|
|
93
93
|
cfg.http_base_url = "http://localhost:54664"
|
|
94
94
|
cfg.center_freq = 2.44e9
|
|
95
|
-
cfg.sample_rate =
|
|
95
|
+
cfg.sample_rate = 15.36e6
|
|
96
96
|
|
|
97
97
|
src = aaronia.AaroniaSource()
|
|
98
98
|
src.start_streaming(cfg)
|
|
@@ -124,22 +124,41 @@ Usually the missing connection from step 4: the device block's output is
|
|
|
124
124
|
not wired to the HTTP Server block. `curl /sample` shows whether data is
|
|
125
125
|
present.
|
|
126
126
|
|
|
127
|
-
**Span and
|
|
127
|
+
**Span, sample rate and the "1 / 4" in the GUI.**
|
|
128
128
|
`span_frequency` is the IQ sample rate (Fs). The name comes from the
|
|
129
|
-
Aaronia API.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
Aaronia API. Three numbers describe the same capture and they are all
|
|
130
|
+
different:
|
|
131
|
+
|
|
132
|
+
| Where you see it | Example | Meaning |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `span_frequency`, and `sampleFrequency` in packet metadata | 15.36 MHz | The sample rate, Fs |
|
|
135
|
+
| `startFrequency`..`endFrequency` in packet metadata | 12.288 MHz | Usable alias-free bandwidth, which measures 0.8 x Fs |
|
|
136
|
+
| The Span control in the RTSA GUI | `1 / 4` | Decimation of the 61.44 MHz clock, so Fs = 61.44 / 4 |
|
|
137
|
+
|
|
138
|
+
The device runs at 61.44 MHz divided by a power of two, ten rates from
|
|
139
|
+
61.44 MHz down to 120 kHz, shown in the GUI as Full through `1 / 512`.
|
|
140
|
+
|
|
141
|
+
**Pass one of those rates and you get it exactly.** Anything else is
|
|
142
|
+
silently adjusted, and not to the nearest rate: the server reads the
|
|
143
|
+
requested span as the *usable bandwidth* you want and picks the rate
|
|
144
|
+
whose usable span is closest. Asking for 2.5 MHz gives Fs = 3.84 MHz,
|
|
145
|
+
whose usable span is 3.07 MHz, rather than the numerically closer
|
|
146
|
+
1.92 MHz. Verified across nine requests on a V6 ECO.
|
|
147
|
+
|
|
148
|
+
`AaroniaSource::get_source_info()` reports the rate the server is
|
|
149
|
+
actually sending once packets are flowing, so read it back rather than
|
|
150
|
+
assuming.
|
|
151
|
+
|
|
152
|
+
The crate rejects a rate that would violate the IQ-mode constraint
|
|
153
|
+
before streaming starts, but it does not second-guess the ladder.
|
|
135
154
|
|
|
136
155
|
**Retuning has no effect.**
|
|
137
156
|
The `/control` endpoint applies a frequency change only when
|
|
138
157
|
`frequencyCenter` and `frequencySpan` are both present. A request
|
|
139
158
|
carrying one of them returns `{"success":true}` and is ignored. This
|
|
140
159
|
crate always sends the complete tuple, so use `set_center_frequency`
|
|
141
|
-
instead of issuing PUTs directly. No Aaronia licence is involved
|
|
142
|
-
|
|
160
|
+
instead of issuing PUTs directly. No Aaronia licence is involved: the
|
|
161
|
+
crate tunes through `/control`, which needs none.
|
|
143
162
|
|
|
144
163
|
**Network saturation at high sample rates.**
|
|
145
164
|
Float32 requires roughly 740 MB/s at 92 MSPS. On anything other than
|
|
@@ -165,7 +184,7 @@ use sdr_aaronia_rs::{AaroniaConfig, AaroniaSource};
|
|
|
165
184
|
let config = AaroniaConfig::default()
|
|
166
185
|
.force_native_sdk()
|
|
167
186
|
.center_frequency(2.44e9)
|
|
168
|
-
.span_frequency(
|
|
187
|
+
.span_frequency(15.36e6);
|
|
169
188
|
let mut source = AaroniaSource::new(config).await?;
|
|
170
189
|
# Ok(())
|
|
171
190
|
# }
|