
Why Rust is a great fit for RF engineering tools, and a tour of open-source crates for conversions, touchstone files, gain lineups, and link budgets.
Ian Cleary
Most RF engineers reach for MATLAB, Python, or spreadsheets. These work, but they share a problem: no type safety, no compile-time guarantees, and painful distribution. You end up emailing .xlsx files or maintaining a Python environment that breaks every 6 months.
Rust fixes this:
cargo is a great package manager. Dependency management, testing, docs, and publishing are built in.These crates are available on crates.io and cover the core calculations RF engineers do daily.
Unit conversions that every RF engineer needs: dB ↔ linear, dBm ↔ watts, dBW ↔ watts, frequency ↔ wavelength, noise figure ↔ noise temperature, and P1dB compression.
use rfconversions::power;
use rfconversions::frequency;
use rfconversions::noise;
// Power conversions
let dbm = power::watts_to_dbm(1.0); // 30.0 dBm
let watts = power::dbm_to_watts(30.0); // 1.0 W
let dbw = power::watts_to_dbw(1.0); // 0.0 dBW
let linear = power::db_to_linear(30.0); // 1000.0
// Frequency ↔ Wavelength
let wavelength = frequency::frequency_to_wavelength(1.0e9); // 0.2998 m
let ghz = frequency::mhz_to_ghz(2400.0); // 2.4 GHz
// Noise figure ↔ Noise temperature
let nf_db = noise::noise_figure_from_noise_factor(2.0); // ~3.01 dB
let temp = noise::noise_temperature_from_noise_factor(2.0); // 290.0 K
Small, zero-dependency, and well-tested. Use it as a foundation for larger tools.
Parse, analyze, and manipulate Touchstone (.s1p, .s2p, .snp) files — the industry-standard format for S-parameter data from vector network analyzers. Supports any port count up to 32+.
use touchstone::Network;
// Load a 2-port network
let ntwk = Network::new("filter.s2p".to_string());
println!("Ports: {}", ntwk.rank);
println!("Format: {}", ntwk.format);
println!("Reference impedance: {} Ω", ntwk.z0);
// S21 (forward transmission) in dB
let s21_db = ntwk.s_db(2, 1);
for point in &s21_db {
println!("f={} : S21={} dB", point.frequency, point.s_db.decibel());
}
// S11 (return loss) in real-imaginary
let s11_ri = ntwk.s_ri(1, 1);
for point in &s11_ri {
println!("f={} : re={}, im={}", point.frequency, point.s_ri.real(), point.s_ri.imaginary());
}
// Cascade two 2-port networks
let net1 = Network::new("amp.s2p".to_string());
let net2 = Network::new("filter.s2p".to_string());
let cascaded = net1.cascade(&net2);
Supports Touchstone 1.0 and 2.0 formats, all parameter types (S, Y, Z, H, G), and all number formats (DB, MA, RI). Includes a CLI for plotting S-parameters as interactive HTML charts.
Model an RF signal chain as a sequence of blocks and cascade their effects on signal power, noise, and linearity. This is the spreadsheet-style RF lineup — but in Rust, with proper Friis equation cascading.
use gainlineup::{Block, Input, cascade_vector_return_vector};
// Define input signal
let input = Input {
power_dbm: -80.0,
frequency_hz: 6.0e9, // 6 GHz C-band
bandwidth_hz: 1.0e6, // 1 MHz channel
noise_temperature_k: Some(50.0),
};
// Define blocks in the chain
let lna = Block {
name: "Low Noise Amplifier".to_string(),
gain_db: 20.0,
noise_figure_db: 1.5,
output_p1db_dbm: Some(5.0),
output_ip3_dbm: Some(20.0),
};
let mixer = Block {
name: "Mixer".to_string(),
gain_db: -8.0,
noise_figure_db: 8.0,
output_p1db_dbm: Some(10.0),
output_ip3_dbm: Some(15.0),
};
let if_amp = Block {
name: "IF Amplifier".to_string(),
gain_db: 25.0,
noise_figure_db: 4.0,
output_p1db_dbm: Some(15.0),
output_ip3_dbm: Some(25.0),
};
// Run the cascade
let blocks = vec![lna, mixer, if_amp];
let nodes = cascade_vector_return_vector(&input, &blocks);
// Each node has cumulative gain, noise figure, signal power
let output = &nodes[nodes.len() - 1];
println!("System Gain: {:.1} dB", output.cumulative_gain_db);
println!("System NF: {:.2} dB", output.cumulative_noise_figure_db);
println!("Output Power: {:.1} dBm", output.signal_power_dbm);
End-to-end satellite link budget calculations: EIRP, path loss, atmospheric losses, G/T, C/N, margin, modulation, BER, and FEC coding.
use linkbudget::{LinkBudget, PathLoss, Transmitter, Receiver, Modulation};
use linkbudget::coding;
let budget = LinkBudget {
name: "Ka-band LEO downlink",
bandwidth: 36e6, // 36 MHz channel
transmitter: Transmitter {
output_power: 10.0, // dBm
gain: 35.0, // dBi
bandwidth: 36e6,
},
receiver: Receiver {
gain: 40.0, // dBi
temperature: 290.0, // K
noise_figure: 2.0, // dB
bandwidth: 36e6,
},
path_loss: PathLoss {
frequency: 20.0e9, // 20 GHz Ka-band
distance: 550.0e3, // 550 km LEO
},
frequency_dependent_loss: Some(3.0), // rain fade margin
};
// RF metrics
println!("EIRP: {:.1} dBm", budget.transmitter.eirp_dbm());
println!("G/T: {:.1} dB/K", budget.receiver.g_over_t_db());
println!("Path Loss: {:.1} dB", budget.path_loss());
println!("C/No: {:.1} dB·Hz", budget.c_over_no());
println!("SNR: {:.1} dB", budget.snr());
// BER with QPSK
println!("Eb/No (QPSK): {:.1} dB", budget.eb_no_db(&Modulation::Qpsk));
println!("BER (uncoded): {:.2e}", budget.ber(&Modulation::Qpsk));
println!("Shannon capacity: {:.1} Mbps", budget.phy_rate().mbps());
// With FEC — DVB-S2 QPSK rate 3/4 (LDPC)
let coded = coding::dvbs2_qpsk_r34();
println!("Coded BER: {:.2e}", budget.ber_coded(&coded));
println!("Throughput: {:.0} Mbps", budget.throughput_bps(&coded) / 1e6);
Includes modules for Doppler shift, orbital mechanics, power flux density, quantization noise, EVM, and receiver sensitivity.
If you're an RF engineer who's never used Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shcargo new my-rf-tool && cd my-rf-toolcargo add rfconversionscargo testThe learning curve is real — the borrow checker will fight you for a week. But once it clicks, you'll wonder why you ever trusted a spreadsheet with your link budget.
Python is fine for quick analysis. But if you're building tools that others will use, Rust gives you:
| Python | Rust | |
|---|---|---|
| Distribution | pip install + environment hell | Single binary |
| Performance | Adequate for small chains | Fast for Monte Carlo, large datasets |
| Correctness | Runtime errors | Compile-time errors |
| Testing | pytest (manual setup) | cargo test (built in) |
| Airgapped use | Package everything | Copy one file |
For one-off scripts, use Python. For tools you'll maintain and share, consider Rust.
Phased Array Fundamentals for Satellite Engineers
A practical primer on phased array beam steering, scan loss, grating lobes, and why hybrid architectures are winning in modern ground terminals.
How to Learn RF Engineering
An opinionated reading list and learning path for RF and satellite communications engineering — filtered through 11 years of actually doing it.