Anyone who has spent time in the world of software-defined radio knows that there are a dizzying number of transceivers available across the commercial and open source landscape. These range from hobbyist devices like the RTL-SDR and HackRF One, to prosumer radios such as the Ettus USRP, to full-scale enterprise digital receivers and waveform generators used for military applications. Despite these devices sharing a large number of common parameters, almost all of them feature proprietary formats for writing data and issuing commands. This creates the need for bespoke interfaces for each device despite most software-defined radios having relatively similar underlying architectures.
VITA49 is a data standard that aims to provide a unified network interface that is generic to work enough most software-defined radio platforms. It defines a series of standard packet formats that designate how data should be distributed from radios. These include:
- Signal Packets – These contain complex arbitrary IQ (Inphase and Quadrature) or real samples measuring the voltage of the analog to digital converter in the radio.
- Context Packets – These specify the state of the radio, providing common meta-parameters such as center frequency, sample rate, and gain stage information.
- Control Packets – Added in the latest VITA49.2 specification, these packets include directives on tasking and controlling radios beyond just passively receiving data.
VITA49 is a lengthy and complicated standard, but has gained some adoption in industry across multiple players. National Instruments has utilized it as the packet format used with the Ettus USRP and many of their other radios. Keysight adopted it on their recent N6831A HF receiver. Smaller SDR companies like Epiq solutions and Per Vices now use it as the interface standard for most of their product offerings. It has also seen some adoption from players in the government and defense sector as the standard continues to evolve.
One reason that adoption has been slow is that there is not currently robust software support. A true standards-based technology should be trivial to pick up and start using, with libraries and example code available across most major languages to streamline adoption by a large audience.
This is not the case with VITA49 right now. Doing a quick search through github shows only a few abandoned repositories from many years ago. There is no go-to library solution for popular languages such as C, C++, or Python. As a result, anyone who adopts the standard is stuck “rolling their own” software solution which requires reading through the hundreds of pages of documentation and manually working through fields at the byte level. This is a large burden to place on individual developers or even organizations, who would much prefer to think about problems at the application level instead of worrying about the byte-level ordering of how radio packets should be assembled.
To try and ease this burden I recently published a small C++ library called Vitality on github. This is a header-only library which attempts to provide access to the minimal parts of the VITA49 standard that are most likely to be used. It includes creating and decoding signal and context packets along with practical examples that show workflows of sending and receiving VITA49 packets over a socket. The goal of the library is to be simple and readable while still maintaining performance by using view-only access to data structures instead of relying on making copies. Some code examples are shown below:
Creating a signal packet:
// Define a vector to hold IQ samples
std::vector<std::complex<float>> tx_samples = {
{1.0f, -1.0f},
{2.5f, 0.25f},
};
// Create a signal packet and assign the payload
vita::packet::signal signal_packet;
signal_packet.set_stream_id(0x12345678u);
signal_packet.set_payload_view(vita::as_bytes_view(tx_samples));
// Send these bytes on your socket
const auto signal_wire_bytes = signal_packet.to_bytes();
Creating a context packet:
// Create a context packet and set metadata fields vita::packet::context context_packet; context_packet.set_stream_id(0xABCDEF01u); context_packet.set_change_indicator(true); context_packet.set_sample_rate_sps(30.72e6); context_packet.set_temperature_celsius(41.5); // Send these bytes on your socket const auto context_wire_bytes = context_packet.to_bytes();
Handling signal and context packets received over a socket:
// Define a handler for signal packets
const auto signal_handler = [](const vita::view::signal& view) {
std::vector<std::complex<float>> samples(view.payload().size() / sizeof(std::complex<float>));
std::memcpy(samples.data(), view.payload().data(), view.payload().size());
std::cout << "signal stream id: 0x" << std::hex << view.stream_id().value_or(0u) << std::dec << "\n";
for (const auto& sample : samples) {
std::cout << " " << sample.real() << ", " << sample.imag() << "\n";
}
};
// Define a handler for context packets
const auto context_handler = [](const vita::view::context& view) {
std::cout << "context stream id: 0x" << std::hex << view.stream_id().value_or(0u) << std::dec
<< ", sample-rate=" << (view.has_sample_rate_sps() ? "present" : "missing")
<< ", temperature=" << (view.has_temperature_celsius() ? "present" : "missing") << "\n";
};
// Receive raw bytes on a socket
std::vector<vita::byte> recv_buffer(2048);
const auto received = ::recv(rx_fd, recv_buffer.data(), recv_buffer.size(), 0);
if (received < 0) {
throw std::system_error(errno, std::generic_category(), "recv");
}
recv_buffer.resize(static_cast<std::size_t>(received));
// Access the raw bytes as views based on the handlers provided
vita::packet::dispatch(vita::as_bytes_view(recv_buffer), signal_handler, context_handler);
The library is still relatively immature and missing large parts of the VITA49 standard; still, it may be useful for basic operations that most developers use such as receiving IQ and interpreting status. Application developers should not have to worry about the manual byte-ordering or wire syntax of a standard in order to use it in their applications.
Ideally this simplifies real-time development when working with VITA49 radios and provides a simple, header-only solution to use as a starting point. Give it a try and let me know your thoughts at code@jmont.net.
jmont
A software engineer with a passion for computer science and software-defined radio.
Related Posts
March 25, 2026
Modern Trends in Software-defined Radio
I'm old enough to remember the "good old days" of when radios and RF technology…
March 23, 2026
Ollama-hpp, header-only language models in C++
The democratization of open-weight, locally-run language models has been one of…
March 21, 2026
Language Models and Artificial Intelligence
"Gradient descent can do it." These were the words of Sam Altman following the…




