Wire-antenna planning for ham radio operators — fast, precise, and scriptable.
If Rusty Wire is useful to you, consider supporting its development:
Rusty Wire computes resonant and non-resonant wire lengths across ITU-region-aware amateur and shortwave bands. It covers six antenna models, recommends transformers automatically, and fits comfortably into shell scripts as well as interactive planning sessions.
2.6.0: expanded regression coverage for TUI key guards and antenna-model guidance · 3.x roadmap: desktop GUI (iced)
| Model | --antenna flag |
Key output |
|---|---|---|
| Half-wave dipole | dipole |
Half-wave length, per-leg |
| Inverted-V | inverted-v |
Total and per-leg, 90°/120° apex span |
| End-fed half-wave | efhw |
Total EFHW wire length |
| Full-wave loop | loop |
Circumference and square-side estimate |
| Off-center-fed dipole | ocfd |
33/67 and 20/80 leg splits, optimised feedpoint |
| Trap dipole | trap-dipole |
Trap total/per-element plus build guidance notes |
Omit --antenna to show all six models at once.
Resonant (default) — per-band resonant lengths, in-window harmonic analysis, shared compromise lengths across multiple bands.
Non-resonant — finds the single best wire length that minimises proximity to resonance across all selected bands, with full local-optima listing and equal-tie support.
--quiet— suppresses the results table; non-resonant mode prints one compact recommendation line, resonant mode exits silently. Designed for shell scripting.--verbose— print the resolved run configuration before executing.--dry-run— validate inputs and print the resolved run without calculating or exporting.--bands-preset <name>— load a named band set from a TOML config file.--bands-config <path>— override preset file path (default:bands.toml).--advise— print ranked wire + balun/unun candidates with efficiency-style metrics.--validate-with-fnec— when used with--advise, run optional cross-tool validation viafnec-rust(if available inPATH) and print per-candidate status notes.--fnec-pass-max-mismatchand--fnec-reject-min-mismatch— configure pass/warn/reject thresholds for advise validation when--validate-with-fnecis enabled.- Advise exports (
--advise --export ...) now includevalidated,validation_status, andvalidation_notefields in CSV/JSON/Markdown/TXT output. --freq <MHz>— compute wire lengths for a single explicit frequency without touching the band database.--freq-list <f1,f2,...>— compute wire lengths for multiple explicit frequencies in a single invocation; each produces its own labelled result.--velocity-sweep <v1,v2,...>— run the same configuration at multiple velocity factors and print a side-by-side comparison table.--height 7|10|12— select standard antenna height model (meters) used for height-aware skip-distance estimates.--ground poor|average|good— select ground-class model used for skip-distance scaling (default:average).--conductor-mm <1.0..4.0>— set conductor diameter in millimeters for first-order impedance/length correction (default:2.0).--step <meters>— control non-resonant search resolution (default 0.05 m).--velocity <value>— velocity factor from 0.50 to 1.00 (default 0.95).--transformer recommended|1:1|1:4|1:9|1:49|1:56|...— auto-resolved per mode and antenna model by default.--units m|ft|both— metric, imperial, or both systems in one run.--export csv,json,markdown,txt— export any combination of formats.
- ITU Region-aware band tables (Region 1 default;
--region 1|2|3). - Named-band selection and ranges:
--bands 40m,20m,10m-15m. --list-bandsto inspect the full band table for any region.
# Build a release binary
cargo build --release
# Show all options
./target/release/rusty-wire --help
# Interactive planning session
./target/release/rusty-wire --interactive
# During development
cargo run -- [OPTIONS]Resonant lengths for 40 m and 20 m (all antenna models):
rusty-wire --bands 40m,20m --velocity 0.95EFHW planning for 40 m / 20 m with transformer auto-selection:
rusty-wire --mode resonant --bands 40m,20m --antenna efhw --transformer recommendedOCFD resonant analysis:
rusty-wire --mode resonant --bands 40m,20m --antenna ocfd --height 10 --ground average --conductor-mm 2.5Find the best non-resonant wire for a 10–35 m garden:
rusty-wire --mode non-resonant --bands 40m,20m,15m,10m --wire-min 10 --wire-max 35Same run, imperial window, metric+imperial output:
rusty-wire --mode non-resonant --bands 40m,20m,15m --wire-min-ft 30 --wire-max-ft 90 --units bothSingle explicit frequency (no band selection needed):
rusty-wire --freq 7.074 --antenna dipoleMultiple explicit frequencies in one run:
rusty-wire --freq-list 3.650,7.074,14.074 --antenna dipoleCompare three velocity factors side by side:
rusty-wire --mode non-resonant --bands 40m,20m --wire-min 10 --wire-max 35 \
--velocity-sweep 0.85,0.95,1.00Use a named custom band preset from bands.toml:
[presets]
portable = ["40m", "20m", "15m", "10m"]
fieldday = ["80m", "40m", "20m", "15m", "10m"]rusty-wire --bands-preset portable
rusty-wire --bands-preset fieldday --bands-config ./profiles/bands.tomlGet ranked advise candidates (wire + balun/unun):
rusty-wire --advise --bands 40m,20m,15m --antenna efhw
rusty-wire --advise --bands-preset portable --bands-config ./profiles/bands.toml
# Export advise report as Markdown
rusty-wire --advise --bands 40m,20m --antenna efhw --export markdown --output advise.mdScript-friendly one-liner (non-resonant recommendation only):
rusty-wire --mode non-resonant --bands 40m,20m --wire-min 10 --wire-max 35 --quiet
# → e.g.: 20.35 m (66.76 ft)Inspect the resolved run without executing it:
rusty-wire --mode non-resonant --bands 40m,20m --wire-min 10 --wire-max 35 --dry-run
rusty-wire --bands 40m --verboseExport to CSV and Markdown for a field notebook:
rusty-wire --mode non-resonant --bands 20m,15m,10m --wire-min 8 --wire-max 25 \
--export csv,markdown --output my-antennaList all bands for ITU Region 2:
rusty-wire --list-bands --region 2For a complete option reference see docs/cli-guide.md. For formulas and optimizer objective functions see docs/math.md.
| Document | Contents |
|---|---|
| docs/cli-guide.md | Full option reference and worked examples |
| docs/math.md | Formula definitions and optimizer objective functions (KaTeX) |
| docs/nec-calibration.md | Workflow for fitting practical-model constants from NEC/reference sweeps |
| docs/architecture.md | Module design, execution flow, app-layer API |
| docs/roadmap.md | Milestone plan (2.x TUI, 3.x GUI) |
| docs/backlog.md | Unconfirmed ideas under consideration |
| docs/tui-screenshots.md | Canonical TUI screenshot capture and placement checklist |
| docs/testing.md | Test strategy and regression scripts |
| docs/CHANGELOG.md | Full release history |
# Full suite — format gate, compile gate, unit tests, all regression scripts
./scripts/test-all.sh
# Unit + integration suite only (296 tests)
cargo test
# Individual regression scripts
./scripts/test-multi-optima.sh
./scripts/test-itu-region-bands.sh
./scripts/test-nec-calibration.shSee docs/testing.md for the complete test strategy.
# Launch the keyboard-driven terminal UI
cargo run --bin tui
# or, after cargo build --release:
./target/release/rusty-wire-tui
# load named presets from an alternate file:
cargo run --bin tui -- --bands-config ./profiles/bands.tomlThe TUI provides a two-panel layout — configuration on the left, results on the
right — and requires no command-line flags for the default experience.
Configuration fields cycle through presets with ←/→; results scroll with ↑↓ or
PgUp/PgDn. If a bands.toml file is present in the current working directory,
its named presets are added to the Bands field automatically. You can also
override the preset file at startup with --bands-config <path>. Press r to
recalculate, q to quit.
Screenshot capture and placement checklist: docs/tui-screenshots.md.
| Key | Action |
|---|---|
↑ / ↓ or j / k |
Select config field / scroll results |
← / → or h / l |
Change selected value |
r / Enter |
Run calculation |
Tab |
Toggle focus (config ↔ results) |
PgUp / PgDn |
Scroll results by 10 lines |
q / Esc / Ctrl-C |
Quit |
Rusty Wire is also a library crate. External front-ends (TUI, future GUI)
consume rusty_wire::app::* directly without pulling in CLI logic:
use rusty_wire::app::{AppRequest, AppResponse, execute_request_checked};AppConfig → AppResults is the stable calculation boundary. AppError covers
all validation paths with typed variants. AppState / AppAction /
apply_action power the pure UI state machine. The app layer now also exposes
optimize_transformer_candidates(&AppConfig) as the balun/unun optimizer
foundation for upcoming advise candidate ranking.
Supply-chain transparency is tracked via committed SBOM files.
# Install the Cargo SBOM tool (once)
cargo install cargo-sbom
# Generate SPDX JSON 2.3 (default)
cargo sbom
# Generate CycloneDX JSON
cargo sbom-cdx
# Or via the helper script
./scripts/generate-sbom.sh # SPDX
./scripts/generate-sbom.sh cyclonedxCommitted outputs: sbom/rusty-wire.spdx.json and sbom/rusty-wire.cdx.json.
A pre-push hook at .githooks/pre-push runs cargo fmt --check, cargo check,
cargo test, and SBOM regeneration before every push. Enable it with:
git config core.hooksPath .githooksRequires jq or jaq for deterministic SBOM normalisation. If the SBOM changes
during the hook run it blocks the push until the updated file is committed.
GPL-2.0-or-later — see LICENSE.

