python-aaronia 0.6.0__tar.gz → 0.6.1__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.1}/CHANGELOG.md +52 -3
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/Cargo.lock +2 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/Cargo.toml +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/DESIGN.md +2 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/PKG-INFO +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/PLUGINS.md +9 -1
- python_aaronia-0.6.1/README.md +212 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/APPS.md +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/HTTPSPEC.md +67 -5
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/QUICKSTART.md +32 -13
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/USAGE.md +28 -2
- python_aaronia-0.6.1/docs/VERIFICATION.md +47 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/python-aaronia/Cargo.toml +1 -1
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/soapy-aaronia/AaroniaSoapyDevice.cpp +48 -7
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/http_endpoints.rs +21 -7
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/lib.rs +3 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/native_sdk.rs +8 -2
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/unified_source.rs +80 -4
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/utils.rs +134 -0
- python_aaronia-0.6.0/README.md +0 -291
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/.cargo/config.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/.gitattributes +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/.gitignore +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/CONTRIBUTING.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/LICENSE +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/benches/decompress_block.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/benches/deinterleave_dual_iq.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/benches/parse_int16_packet.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/benches/rtsa_open_and_read.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/deny.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/FILESPEC.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/docs/SDKSPEC.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/channel_hopping.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/device_control.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/dump_metadata.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/http_iq_quickstart.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/native_sdk_basic.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/native_sdk_transmit.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/noaa_scanner.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/python_arrow_example.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/read_rtsa_file.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/examples/soapy_python_example.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/include/aaronia.h +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/pyproject.toml +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/python-aaronia/README.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/python-aaronia/aaronia.pyi +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/python-aaronia/src/lib.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/python-aaronia/test_basic.py +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/scripts/ci-local.sh +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/soapy-aaronia/AaroniaSoapyDevice.hpp +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/soapy-aaronia/CMakeLists.txt +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/soapy-aaronia/README.md +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/soapy-aaronia/Registration.cpp +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/c_api.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/decompression.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/detection.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/error.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/file_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/http_sink.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/http_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/http_streaming.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/sdk_sink.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/sdk_source.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/sdr_source_impl.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/seify_impl.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/src/unified_sink.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/c_api_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/http_mock_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/http_resilience_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/http_sink_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/integration_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/live_smoke.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/native_sdk_load.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/properties.proptest-regressions +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/properties.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/rtsa_negative_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/sdr_source_impl_test.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/spec_coverage.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/test_cw_mag.rs +0 -0
- {python_aaronia-0.6.0 → python_aaronia-0.6.1}/tests/test_cw_meta.rs +0 -0
|
@@ -4,6 +4,55 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.6.1] - 2026-08-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **The SoapySDR plugin advertised sample rates the hardware cannot
|
|
11
|
+
produce.** Seven of the ten rates it listed, 1, 2, 5, 10 and 20 MHz
|
|
12
|
+
among them, do not exist on the device, which runs at 61.44 MHz
|
|
13
|
+
divided by a power of two. Applications build their rate dropdowns
|
|
14
|
+
from that list, so choosing 10 MHz ran the device at a different rate
|
|
15
|
+
while the application went on displaying 10. The list is now the real
|
|
16
|
+
ladder, 61.44 MHz down to 120 kHz, and `setSampleRate` snaps to the
|
|
17
|
+
nearest one and logs when it has to.
|
|
18
|
+
- **The crate reported the requested sample rate rather than the one in
|
|
19
|
+
use.** The device adjusts a rate it cannot produce, so
|
|
20
|
+
`get_source_info()` described a capture that was not happening. HTTP
|
|
21
|
+
sources now report the rate, centre frequency and usable bandwidth
|
|
22
|
+
from the stream's own metadata once packets arrive.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `iq_sample_rates`, `usable_bandwidth_hz`, `nearest_iq_sample_rate` and
|
|
26
|
+
`iq_sample_rate_for_bandwidth` in `utils`, with the constants
|
|
27
|
+
`IQ_CLOCK_HZ` and `USABLE_BANDWIDTH_RATIO`. The device samples at
|
|
28
|
+
61.44 MHz divided by a power of two and delivers 0.8 of that as
|
|
29
|
+
alias-free bandwidth. Callers were deriving their own rates from
|
|
30
|
+
guesses, so the relationships now live in one tested place. Wanting
|
|
31
|
+
8 MHz of spectrum needs 10 MHz of sampling, and
|
|
32
|
+
`iq_sample_rate_for_bandwidth` returns the 15.36 MHz that provides it.
|
|
33
|
+
|
|
34
|
+
### Fixed (native SDK)
|
|
35
|
+
- **Sweep mode set the wrong resolution-bandwidth key.** It sent
|
|
36
|
+
`main/rbw`, which no Aaronia sample uses; the key is `main/rbwfreq`.
|
|
37
|
+
Checked against Aaronia's published `SweepSpectrumEco` sample, which
|
|
38
|
+
also confirms `main/startfreq`, `main/stopfreq`, `main/reflevel` and
|
|
39
|
+
the `spectranv6eco/sweepsa` open string that this crate already used.
|
|
40
|
+
The sweep path remains hardware-unverified.
|
|
41
|
+
|
|
42
|
+
### Documentation
|
|
43
|
+
- **GPS time needs GPS switched on, and the crate does not do it.**
|
|
44
|
+
Devices ship with `device/gpsmode` disabled, so `get_gps_time` would
|
|
45
|
+
return `None` indefinitely and appear broken. Aaronia's `GPSTime`
|
|
46
|
+
sample sets `device/gpsmode` to "Location and Time" and
|
|
47
|
+
`device/sclksource` to "GPS Provider" before starting the device;
|
|
48
|
+
`get_gps_time` now says so.
|
|
49
|
+
- Documented what `/control`'s `frequencySpan` actually means. It is a
|
|
50
|
+
request for usable bandwidth, not a sample rate: the device picks the
|
|
51
|
+
rate whose alias-free span is nearest, so 2.5 MHz yields 3.84 MHz and
|
|
52
|
+
10 MHz yields 15.36 MHz. Values on the rate ladder round-trip exactly,
|
|
53
|
+
which is why the field looks like a sample rate in ordinary use.
|
|
54
|
+
Verified across nine requests on a V6 ECO.
|
|
55
|
+
|
|
7
56
|
## [v0.6.0] - 2026-08-11
|
|
8
57
|
|
|
9
58
|
Reliability and documentation release. The HTTP backend now handles a
|
|
@@ -80,9 +129,9 @@ hardware and which have not.
|
|
|
80
129
|
### Release
|
|
81
130
|
- Release archives now carry the module, install instructions and the
|
|
82
131
|
licence, and their names state the version, OS and architecture.
|
|
83
|
-
- The Linux module ships stripped of debug symbols, down
|
|
84
|
-
|
|
85
|
-
publishing it.
|
|
132
|
+
- The Linux module ships stripped of debug symbols, 19.1 MB down to
|
|
133
|
+
15.4 MB. The rest is statically linked Rust, not symbols. The release
|
|
134
|
+
job checks the stripped module still loads before publishing it.
|
|
86
135
|
- GitHub releases now use this file's entry for the tag as their
|
|
87
136
|
description, with the generated commit list below it.
|
|
88
137
|
|
|
@@ -3027,7 +3027,7 @@ dependencies = [
|
|
|
3027
3027
|
|
|
3028
3028
|
[[package]]
|
|
3029
3029
|
name = "python-aaronia"
|
|
3030
|
-
version = "0.6.
|
|
3030
|
+
version = "0.6.1"
|
|
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.1"
|
|
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.1"
|
|
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.1
|
|
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
|
{
|
|
@@ -552,10 +600,23 @@ Data captured using antennas with location or directional information.
|
|
|
552
600
|
- `/info` - Device information
|
|
553
601
|
- `/healthstatus` - Device health monitoring
|
|
554
602
|
|
|
555
|
-
**Remote Configuration** (
|
|
556
|
-
-
|
|
557
|
-
-
|
|
558
|
-
|
|
603
|
+
**Remote Configuration** (`/remoteconfig`):
|
|
604
|
+
- Device parameter configuration.
|
|
605
|
+
- Aaronia sells a "Remote Config" license, and this document previously
|
|
606
|
+
stated that `/remoteconfig` writes fail without it. **That is not what
|
|
607
|
+
a live system does.** On RTSA-Suite PRO driving a SPECTRAN V6 ECO whose
|
|
608
|
+
license list contains no Remote Config entry, `PUT /remoteconfig`
|
|
609
|
+
retuned the device repeatedly, in both the config-tree and simplified
|
|
610
|
+
forms, returning HTTP 200 with the change applied.
|
|
611
|
+
- What that license actually gates is therefore unconfirmed. It may
|
|
612
|
+
cover a different feature, or a different edition or version, or
|
|
613
|
+
parameters other than the ones tested. Treat any claim that
|
|
614
|
+
`/remoteconfig` writes require it as unverified.
|
|
615
|
+
- The system tested did hold "Block: HTTP Server" and "Block: HTTP
|
|
616
|
+
Client" licenses, so the HTTP surface itself is licensed separately
|
|
617
|
+
and may be what actually matters.
|
|
618
|
+
- This crate does not depend on the answer: it tunes through `/control`,
|
|
619
|
+
which needs no license.
|
|
559
620
|
|
|
560
621
|
**License Detection Methods**:
|
|
561
622
|
|
|
@@ -564,7 +625,7 @@ Data captured using antennas with location or directional information.
|
|
|
564
625
|
The client therefore exposes two methods:
|
|
565
626
|
|
|
566
627
|
- `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).
|
|
628
|
+
- `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
629
|
|
|
569
630
|
**Practical Detection (Active Probe)**:
|
|
570
631
|
```rust
|
|
@@ -707,6 +768,7 @@ consult:
|
|
|
707
768
|
|
|
708
769
|
- **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
770
|
- Aaronia RTSA-Suite PRO product documentation — <https://rtsa-manual.aaronia.com/>
|
|
771
|
+
- **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
772
|
|
|
711
773
|
The content here is derived from the above plus empirical analysis of live
|
|
712
774
|
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
|
# }
|
|
@@ -5,7 +5,7 @@ compiled as a doctest, so it stays valid as the API changes.
|
|
|
5
5
|
|
|
6
6
|
For first-time setup of the RTSA-Suite HTTP Server block, see
|
|
7
7
|
[QUICKSTART.md](QUICKSTART.md). For runnable programs, see
|
|
8
|
-
[
|
|
8
|
+
[Runnable examples](#runnable-examples) below.
|
|
9
9
|
|
|
10
10
|
## Unified API with auto-detection
|
|
11
11
|
|
|
@@ -303,9 +303,35 @@ async fn main() -> Result<()> {
|
|
|
303
303
|
}
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
+
## Environment variables
|
|
307
|
+
|
|
308
|
+
| Variable | Effect |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `AARONIA_SDK_PATH` | Overrides the RTSA-Suite installation directory used for SDK / `RTSAFileTool` detection. Works on every platform. On macOS — which has no default install path and no native SDK build — it only affects `RTSAFileTool` and XML-config detection. |
|
|
311
|
+
| `AARONIA_USER_AGENT` | Overrides the HTTP `User-Agent` string sent by every outbound request (default: `sdr-aaronia-rs/<version>`). |
|
|
312
|
+
|
|
313
|
+
## Runnable examples
|
|
314
|
+
|
|
315
|
+
The programs in `examples/` cover the same ground as this document, as
|
|
316
|
+
code you can run. CI builds them, so they stay current:
|
|
317
|
+
|
|
318
|
+
| Task | Example |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| HTTP IQ streaming, first samples | [`http_iq_quickstart.rs`](../examples/http_iq_quickstart.rs) |
|
|
321
|
+
| Health checks, input enumeration, recording control, license probing | [`device_control.rs`](../examples/device_control.rs) |
|
|
322
|
+
| Frequency hopping via the `sdr-source` traits | [`channel_hopping.rs`](../examples/channel_hopping.rs) |
|
|
323
|
+
| FutureSDR flowgraph with FM demodulation | [`noaa_scanner.rs`](../examples/noaa_scanner.rs) |
|
|
324
|
+
| Native SDK capture and transmit | [`native_sdk_basic.rs`](../examples/native_sdk_basic.rs), [`native_sdk_transmit.rs`](../examples/native_sdk_transmit.rs) |
|
|
325
|
+
| RTSA file playback and metadata inspection | [`read_rtsa_file.rs`](../examples/read_rtsa_file.rs), [`dump_metadata.rs`](../examples/dump_metadata.rs) |
|
|
326
|
+
| Python (NumPy and Arrow), SoapySDR from Python | [`python_arrow_example.py`](../examples/python_arrow_example.py), [`soapy_python_example.py`](../examples/soapy_python_example.py) |
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# args: <center-hz> <sample-rate-hz> <url>
|
|
330
|
+
cargo run --example http_iq_quickstart --features http -- 2440e6 15.36e6 http://localhost:54664
|
|
331
|
+
```
|
|
332
|
+
|
|
306
333
|
## Related
|
|
307
334
|
|
|
308
335
|
- [QUICKSTART.md](QUICKSTART.md) — RTSA-Suite mission setup and troubleshooting.
|
|
309
336
|
- [APPS.md](APPS.md) — SDR++, GQRX, GNU Radio and SoapySDR from Python.
|
|
310
337
|
- [HTTPSPEC.md](HTTPSPEC.md) — the RTSA HTTP API this crate speaks.
|
|
311
|
-
- [`examples/`](../examples/) — the same material as runnable programs.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Hardware verification status
|
|
2
|
+
|
|
3
|
+
Not every code path has been exercised against hardware. The
|
|
4
|
+
development device is a SPECTRAN V6 ECO with a single RX channel and no
|
|
5
|
+
TX licence, driven through RTSA-Suite PRO over HTTP from macOS. Paths
|
|
6
|
+
requiring a second RX input, a transmitter, or the Windows/Linux native
|
|
7
|
+
SDK are marked unverified.
|
|
8
|
+
|
|
9
|
+
| Capability | Backend | Status |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| IQ streaming, all four wire formats (F32 / F16 / I16 / JSON) | HTTP | **Live-verified** |
|
|
12
|
+
| Spectra streaming | HTTP | **Live-verified** |
|
|
13
|
+
| Mid-stream retuning (centre, span) | HTTP | **Live-verified** |
|
|
14
|
+
| Mid-stream reference-level change | HTTP | Confirmed manually against the device; no automated live assertion |
|
|
15
|
+
| Auto-reconnect after a dropped stream | HTTP | Streaming live-verified; the drop-and-recover path is mock-tested |
|
|
16
|
+
| Drop/overrun detection, rate reduction, `scale=N` | HTTP | **Live-verified** |
|
|
17
|
+
| Long-run stability (>120 s continuous) | HTTP | **Live-verified** |
|
|
18
|
+
| Connect retry | HTTP | Mock-tested; the mDNS race it addresses did not reproduce on demand |
|
|
19
|
+
| `.rtsa` playback and metadata | File | **Verified against real captures**, byte-compared with the official format specification |
|
|
20
|
+
| seify backend | HTTP | **Live-verified** |
|
|
21
|
+
| SoapySDR plugin RX | HTTP | Verified manually (~9.7 Msps via `SoapySDRUtil`); no automated live test, as a `soapysdr` dev-dependency would make `cargo test` unbuildable without system SoapySDR |
|
|
22
|
+
| Python bindings RX | HTTP | Verified manually (NumPy and Arrow); no automated live test |
|
|
23
|
+
| TX (`UnifiedSink`, `aaronia_sink_*`, SoapySDR TX) | Native SDK | **Hardware-unverified**. No TX-licensed device available |
|
|
24
|
+
| Dual-channel RX (`Rx1And2`, `read_samples_dual`) | Native SDK | **Hardware-unverified**. Requires a full V6 |
|
|
25
|
+
| GPS hardware time | Native SDK | **Hardware-unverified** |
|
|
26
|
+
| Native SDK capture generally | Native SDK | **Hardware-unverified**; compiled and unit-tested in a Linux VM each release |
|
|
27
|
+
| HTTP TX push (`/sample`) | HTTP | Endpoint exercised live; RF output not measured |
|
|
28
|
+
|
|
29
|
+
"Live-verified" means an `#[ignore]`d test in
|
|
30
|
+
[`tests/live_smoke.rs`](../tests/live_smoke.rs) asserts the behaviour
|
|
31
|
+
against hardware, and is reproducible by anyone with a device. Entries
|
|
32
|
+
marked "verified manually" were observed working but have no automated
|
|
33
|
+
assertion and can regress without detection. Run the automated set
|
|
34
|
+
with:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cargo test --all-features --test live_smoke -- --ignored --nocapture
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Contributions that convert an unverified row, particularly from users
|
|
41
|
+
with a full V6 or a TX licence, are welcome.
|
|
42
|
+
|
|
43
|
+
## Related
|
|
44
|
+
|
|
45
|
+
- [QUICKSTART.md](QUICKSTART.md) — setting up an RTSA-Suite mission.
|
|
46
|
+
- [USAGE.md](USAGE.md) — worked examples for each part of the API.
|
|
47
|
+
- [../CHANGELOG.md](../CHANGELOG.md) — what changed in each release.
|