Expand description
§VITA 49
[!WARNING] APIs should not be considered stable until the 0.1.0 release.
A crate for parsing and creating packets compatible with the ANSI/VITA-49.2-2017 standard.
The ANSI/VITA 49.2 standard “defines a signal/spectrum protocol that expresses spectrum observation, spectrum operations, and capabilities of RF devices.”
In general, it’s difficult to write software for VITA 49 as software defined radios (SDRs) often choose different “flavors” of VITA 49.
This crate is:
-
Flexible: No need to recompile your program for different flavors of VITA 49.
-
Fast: Parsing is quick even while supporting various packet types.
-
Easy: Simply pass in some bytes and this crate will give you a data structure you can query, mutate, and write.
§Install
Add vita49
to your project with:
cargo add vita49
§Examples
Here’s some code to ingest VRT packets from a UDP socket. If it’s a signal data packet, it’ll just print the length and stream ID. If it’s a context packet, it’ll print all the fields that are present.
use std::net::UdpSocket;
use vita49::prelude::*;
fn main() -> Result<(), std::io::Error> {
// Bind to a UDP socket
let socket = UdpSocket::bind("0.0.0.0:4991")?;
let mut buf = [0; 40960];
println!("Entering receive loop...");
loop {
// Read in data from the socket
let (bytes_read, _src) = socket.recv_from(&mut buf)?;
// Try to parse it as a VRT packet
let packet = Vrt::try_from(&buf[..bytes_read])?;
// Do different things depending on the type of packet
match packet.header().packet_type() {
// If it's a signal data packet, just print the payload length
PacketType::SignalData => {
println!(
"Got signal data packet with stream ID 0x{:X} and a payload of length {}",
&packet.stream_id().unwrap(),
&packet.payload().signal_data().unwrap().payload_size_bytes()
);
}
// If it's a context packet, print the fields (using the pre-
// implemented Display trait)
PacketType::Context => {
println!(
"Got context packet:\n{}",
&packet.payload().context().unwrap()
);
}
PacketType::Command => {
println!(
"Got command packet:\n{}",
&packet.payload().command().unwrap()
);
}
// Other packet types are not covered in this example
_ => unimplemented!(),
}
}
}
See udp_recv.rs
for the full example.
Here’s another example of generating and sending VRT packets:
use std::net::UdpSocket;
use vita49::prelude::*;
fn main() -> Result<(), std::io::Error> {
// Bind to a UDP socket
let socket = UdpSocket::bind("0.0.0.0:0")?;
// Create a context packet with RF freq set to 100 MHz and
// bandwidth set to 8 MHz.
let mut packet = Vrt::new_context_packet();
packet.set_stream_id(Some(0xDEADBEEF));
let context = packet.payload_mut().context_mut().unwrap();
context.set_rf_ref_freq_hz(Some(100e6));
context.set_bandwidth_hz(Some(8e6));
packet.update_packet_size();
// Send the packet
socket.send_to(&packet.to_bytes()?, "127.0.0.1:4991")?;
// Create a signal data packet with some dummy data.
let mut sig_packet = Vrt::new_signal_data_packet();
sig_packet.set_stream_id(Some(0xDEADBEEF));
sig_packet
.set_signal_payload(&vec![1, 2, 3, 4, 5, 6, 7, 8])
.unwrap();
// Send the packet
socket.send_to(&sig_packet.to_bytes()?, "127.0.0.1:4991")?;
Ok(())
}
See udp_send.rs
for the full example.
You can actually run these two examples locally to see the output. In one terminal window, run:
cargo run --example udp_recv
Then, in another window, run:
cargo run --example udp_send
On the receive end, you should see something like:
Entering receive loop...
Got context packet:
CIF0:
Context field change indicator: false
Reference point identifier: false
Bandwidth: true
IF reference frequency: false
RF reference frequency: true
RF reference frequency offset: false
IF band offset: false
Reference level: false
Gain: false
Over-range count: false
Sample rate: false
Timestamp adjustment: false
Timestamp calibration time: false
Temperature: false
Device identifier: false
State/event indicators: false
Signal data format: false
Formatted GPS: false
Formatted INS: false
ECEF ephemeris: false
Relative ephemeris: false
Ephemeris ref ID: false
GPS ASCII: false
Context association lists: false
CIF7: false
CIF3: false
CIF2: false
CIF1: false
Bandwidth: 8000000 Hz
RF reference frequency: 100000000 Hz
Got signal data packet with stream ID 0xDEADBEEF and a payload of length 8
§C++ Interoperability
For an example of how to use this crate from a C++ app, see
cxx_demo/README.md
.
§Crate features
By default, this crate does not enable any of its optional features, leaving them as “opt-in” by the user.
§cif7
This feature enables CIF7 support.
To use this feature, enable it in your Cargo.toml
:
vita49 = { version = "0.0.3", features = ["cif7"] }
CIF7, also known as “field attributes”, add an ability to provide descriptive statistics of various fields along with their current value. This does, however, add some parsing overhead as every field then includes up to 31 additional, optional fields
With benchmark tests (via cargo bench
), we can see as of the time of this
writing that enabling CIF7 support yields a 20% performance reduction
in signal data packets and a 63% performance reduction in context packets.
Since the feature isn’t used widely and impacts performance, it’s left disabled by default.
§serde
This feature enables serde support.
To use this feature, enable it in your Cargo.toml
:
vita49 = { version = "0.0.3", features = ["serde"] }
With this feature enabled, you can serialize/deserialize structures provided by this crate with serde. For example, to print a VRT packet as JSON:
use vita49::prelude::*;
#[cfg(feature = "serde")]
{
let mut packet = Vrt::new_context_packet();
let context = packet.payload_mut().context_mut().unwrap();
context.set_bandwidth_hz(Some(8e6));
packet.update_packet_size();
println!("{}", serde_json::to_string_pretty(&packet).unwrap())
}
This yields:
{
"header": {
"hword_1": 17005,
"packet_size": 29
},
"stream_id": 1,
"class_id": null,
"integer_timestamp": 60045,
"fractional_timestamp": 411360110,
"payload": {
"Context": {
"cif0": 673316866,
"cif1": 1032,
"etc": "...",
This repo has some test VRT packets stored as JSON strings for visibility.
An example program is provided to convert these to raw VRT files under
vita49/examples/json2vrt.rs
. You can
run this program via cargo
:
% cargo run --features=serde --example json2vrt vita49/tests/spectral_data_packet.json5
Compiling vita49 v0.0.3 (vita49/vita49)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.71s
Running `target/debug/examples/json2vrt vita49/tests/spectral_data_packet.json5`
Wrote VRT data to vita49/tests/spectral_data_packet.vrt
§TODO
According to Section 1.3 of ANSI/VITA-49.2-2017:
V49.2 provides millions of options for data types and packet structures that are likely impossible to implement as one application program interface (API) without culling down to required attributes.
While we’d love to cover every possible combination VITA 49 can offer, it has to be a community effort to get there. We’ve implemented full support for things that we use and things that we figure would be most likely to be used by others, but we don’t have 100% coverage.
If there’s a field you’d like to use and you don’t feel able to add support for it yourself (and hopefully contribute your code back!), feel free to file an issue and we’ll know what to prioritize.
There are basically three states of “doneness” for various fields:
- Full support
- Basic support
- No support
Fully supported fields have getters and setters that deliver a clean
interface to the user (e.g. fixed point fields are translated to
primitive f64
or a multi-word data structure is filled out in a
meaningful struct
).
Basic support means the field is there and can be parsed/set, but
it’d be better to implement a fully-featured struct. Using these
fields means you might need to do some bit masking/shifting yourself.
These fields are marked with a comment: // TODO: add full support
.
No support means the crate will panic!()
if the field is encountered.
This is usually because the field can be variable length and, until
support for the field is added, we can’t guarantee the packet will
be parsed correctly. These fields are marked with a macro:
todo_cif_field!()
. The getters/setters associated with these
fields are marked with a comment: // TODO: add basic support
.
§Minimum Rust Version Policy
This crate’s minimum supported rustc
version is 1.71.0
.
The minimum supported rustc
version may be increased in minor version
updates of this crate. For example, if vita49
1.2.0
requires Rust 1.60.0
,
versions 1.2.X
of vita49
will also require Rust 1.6.0
or newer. However,
vita49
version 1.3.0
may require a newer minimum version of Rust.
§Crate Versioning
This project adheres to Semantic Versioning.
After initial project release (0.1.0), any changes that would break API compatibility will require a major version number bump.
§License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
§Contributing
See CONTRIBUTING.md
for details.
Modules§
- prelude
- Standard imports for the most commonly used structures and traits in the vita49 crate.
Structs§
- Cif0
- Base data structure for the CIF0 single-bit indicators.
- Cif0
Fields - Structure for all cif0 data fields (not indicators)
- Cif1
- Base data structure for the CIF1 single-bit indicators
- Cif2
- Base data structure for the CIF2 single-bit indicators
- Cif3
- Base data structure for the CIF3 single-bit indicators
- Cif7
- Base data structure for the CIF7 single-bit indicators.
- Cif1
Fields - Structure for all cif1 data fields (not indicators)
- Cif2
Fields - Structure for all cif2 data fields (not indicators)
- Cif3
Fields - Structure for all cif3 data fields (not indicators)
- Class
Identifier - Base class identifier data structure.
- Command
- Main command payload structure.
- Command
Indicators - Command packet indicators.
- Context
- Context packet payload. Includes all CIFs and optional fields.
- Context
Association Lists - Base context association lists structure.
- Context
Indicators - Context packet indicator fields.
- Control
AckMode - Base CAM field data structure.
- Device
Id - Base device ID data structure.
- Ecef
Ephemeris - Base ECEF ephemeris data structure.
- Formatted
Gps - Base formatted GPS data structure.
- Gain
- Base gain data structure.
- GpsAscii
- Base ASCII GPS data structure.
- Packet
Header - Base packet header data structure.
- Signal
Data - Base signal data structure.
- Signal
Data Indicators - Signal data indicator fields.
- Spectrum
- Base spectrum field data structure.
- Threshold
- Base threshold data structure.
- Trailer
- Base trailer field data structure.
- Vrt
- The main VRT data structure that encapsulates all types of VRT packets.
- Window
Time Delta - Window time delta structure.
Enums§
- Action
Mode - Control action mode.
- Averaging
Type - Type of averaging being performed.
- EmsOrganization
Relationship - Enum to describe the various EMS device relationships. See ANSI/VITA-49.2-2017 section 9.8.9 for details.
- IdFormat
- Identification format (128-bit UUID or 32-bit ID).
- Indicators
- Indicator field enumeration. The three indicator bits have different meaning depending on if the packet is a signal data, context, or command packet.
- Packet
Type - The type of VRT packet being worked on.
- Payload
- Generic payload enumeration. The payload format will differ depending on the type of packet.
- Spectrum
Type - Type of spectral data being presented.
- Timestamp
Mode - Timestamp mode
- Timing
Control Mode - Timing control mode.
- Tsf
- TimeStamp-Fractional (TSF) field.
- Tsi
- TimeStamp-Integer (TSI) field.
- Vita
Error - Generic
vita49
crate error enumeration. - Window
Time Delta Interpretation - Interpretation options for the window time delta field.
- Window
Type - Window type enumeration.
Traits§
- Cif0
Manipulators - Trait for common CIF0 manipulation methods. Used by Context and Command packets.
- Cif1
Manipulators - Trait for common CIF1 manipulation methods. Used by Context and Command packets.
- Cif2
Manipulators - Trait for common CIF2 manipulation methods. Used by Context and Command packets.
- Cif3
Manipulators - Trait for common CIF3 manipulation methods. Used by Context and Command packets.