-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Subsume novalabsxyz/itm-rs into workspace
- Loading branch information
Showing
16 changed files
with
665 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,8 @@ jobs: | |
|
||
- name: Setup | Checkout | ||
uses: actions/checkout@v3 | ||
with: | ||
submodules: true | ||
|
||
- name: Setup | Rust toolchain | ||
uses: dtolnay/[email protected] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "extern/itm"] | ||
path = extern/itm | ||
url = https://github.com/dirkcgrunwald/itm.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
[package] | ||
categories = ["science"] | ||
description = "A wrapper around NTIA's Irregular Terrain Model" | ||
edition = "2021" | ||
homepage = "https://github.com/jaykickliter/geoprof" | ||
keywords = ["rf", "radio", "modeling", "ntia"] | ||
license-file = "LICENSE.md" | ||
name = "itm" | ||
readme = "README.md" | ||
repository = "https://github.com/jaykickliter/geoprof" | ||
version = "0.1.0" | ||
|
||
[features] | ||
default = [] | ||
address_sanitizer = [] | ||
serde = ["serde/derive"] | ||
|
||
[dependencies] | ||
cxx = "1" | ||
serde = { workspace = true, optional = true } | ||
thiserror = { workspace = true } | ||
|
||
[build-dependencies] | ||
cxx-build = "1" | ||
|
||
[dev-dependencies] | ||
anyhow = "1" | ||
clap = { workspace = true } | ||
geo = { workspace = true } | ||
terrain = { path = "../terrain" } | ||
|
||
[[example]] | ||
name = "p2p" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
fn main() { | ||
let cxx_sources = [ | ||
"../extern/itm/src/ComputeDeltaH.cpp", | ||
"../extern/itm/src/DiffractionLoss.cpp", | ||
"../extern/itm/src/FindHorizons.cpp", | ||
"../extern/itm/src/FreeSpaceLoss.cpp", | ||
"../extern/itm/src/FresnelIntegral.cpp", | ||
"../extern/itm/src/H0Function.cpp", | ||
"../extern/itm/src/InitializeArea.cpp", | ||
"../extern/itm/src/InitializePointToPoint.cpp", | ||
"../extern/itm/src/InverseComplementaryCumulativeDistributionFunction.cpp", | ||
"../extern/itm/src/KnifeEdgeDiffraction.cpp", | ||
"../extern/itm/src/LineOfSightLoss.cpp", | ||
"../extern/itm/src/LinearLeastSquaresFit.cpp", | ||
"../extern/itm/src/LongleyRice.cpp", | ||
"../extern/itm/src/QuickPfl.cpp", | ||
"../extern/itm/src/SigmaHFunction.cpp", | ||
"../extern/itm/src/SmoothEarthDiffraction.cpp", | ||
"../extern/itm/src/TerrainRoughness.cpp", | ||
"../extern/itm/src/TroposcatterLoss.cpp", | ||
"../extern/itm/src/ValidateInputs.cpp", | ||
"../extern/itm/src/Variability.cpp", | ||
"../extern/itm/src/itm_area.cpp", | ||
"../extern/itm/src/itm_p2p.cpp", | ||
"wrapper/itm-wrapper.cpp", | ||
]; | ||
|
||
let mut bridge = cxx_build::bridge("src/lib.rs"); | ||
bridge.flag("-std=c++11"); | ||
bridge.include("../extern/itm/include"); | ||
#[cfg(feature = "address_sanitizer")] | ||
{ | ||
bridge.flag("-fno-omit-frame-pointer"); | ||
bridge.flag("-fsanitize=address"); | ||
bridge.flag("-ggdb"); | ||
} | ||
for path in &cxx_sources { | ||
bridge.file(path); | ||
} | ||
bridge.compile("itm_wrapper"); | ||
|
||
println!("cargo:rerun-if-changed=wrapper/itm-wrapper.cpp"); | ||
println!("cargo:rerun-if-changed=wrapper/itm-wrapper.h"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
mod options; | ||
|
||
use anyhow::Error as AnyErr; | ||
use clap::Parser; | ||
use itm::{Climate, ModeVariability, Polarization}; | ||
use options::{Cli, LatLonAlt}; | ||
use terrain::{Profile, TileMode, Tiles}; | ||
|
||
// Example for 900 MHz across black rock desert. | ||
// ``` | ||
// cargo run --example p2p -- --tile-dir=/path/to/nasadem/3-arcsecond/hgt/tiles/ --start=40.885629,-119.065844,10 --frequency=900e6 --end=40.904691,-119.043429,10 | ||
// ``` | ||
fn main() -> Result<(), AnyErr> { | ||
let Cli { | ||
tile_dir, | ||
max_step, | ||
start: LatLonAlt(start_coord, start_alt), | ||
end: LatLonAlt(end_coord, end_alt), | ||
frequency, | ||
} = Cli::parse(); | ||
|
||
let tiles = Tiles::new(tile_dir, TileMode::MemMap)?; | ||
let t0 = std::time::Instant::now(); | ||
let profile = Profile::<f32>::builder() | ||
.start(start_coord) | ||
.start_alt(start_alt) | ||
.max_step(max_step) | ||
.end(end_coord) | ||
.end_alt(end_alt) | ||
.build(&tiles)?; | ||
let profile_runtime = t0.elapsed(); | ||
|
||
let climate = Climate::Desert; | ||
let n0 = 301.; | ||
let f_hz = frequency; | ||
let pol = Polarization::Vertical; | ||
let epsilon = 15.; | ||
let sigma = 0.005; | ||
let mdvar = ModeVariability::Accidental; | ||
let time = 50.0; | ||
let location = 50.0; | ||
let situation = 50.0; | ||
let step_size_m = profile.distances_m[1]; | ||
let terrain = profile.terrain_elev_m; | ||
let t0 = std::time::Instant::now(); | ||
let attenuation_db = itm::p2p( | ||
start_alt.into(), | ||
end_alt.into(), | ||
step_size_m.into(), | ||
&terrain, | ||
climate, | ||
n0, | ||
f_hz.into(), | ||
pol, | ||
epsilon, | ||
sigma, | ||
mdvar, | ||
time, | ||
location, | ||
situation, | ||
)?; | ||
let itm_p2p_runtime = t0.elapsed(); | ||
|
||
let total_distance_m = profile.distances_m.last().unwrap(); | ||
let fspl = fspl(*total_distance_m, frequency); | ||
|
||
println!("profile runtime: {profile_runtime:?}"); | ||
println!("itm runtime: {itm_p2p_runtime:?}"); | ||
println!("distance: {total_distance_m} m"); | ||
println!("fspl: {fspl} dB"); | ||
println!("attenuation: {attenuation_db} dB"); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn fspl(meters: f32, freq: f32) -> f32 { | ||
20.0 * meters.log10() + 20.0 * freq.log10() - 147.55 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use anyhow::{anyhow, Error as AnyError}; | ||
use clap::Parser; | ||
use geo::geometry::Coord; | ||
use std::{path::PathBuf, str::FromStr}; | ||
|
||
/// Generate point-to-point terrain profiles. | ||
#[derive(Parser, Debug, Clone)] | ||
#[allow(clippy::struct_excessive_bools)] | ||
pub struct Cli { | ||
/// Directory elevation tiles. | ||
#[arg(short, long)] | ||
pub tile_dir: PathBuf, | ||
|
||
/// Maximum path incremental step size, in meters. | ||
#[arg(short, long, default_value_t = 90.0)] | ||
pub max_step: f32, | ||
|
||
/// Start "lat,lon,alt", where 'alt' is meters above ground. | ||
#[arg(long)] | ||
pub start: LatLonAlt, | ||
|
||
/// Destination "lat,lon,alt", where 'alt' is meters above ground. | ||
#[arg(long)] | ||
pub end: LatLonAlt, | ||
|
||
/// Signal frequency (Hz). | ||
#[arg(long, short)] | ||
pub frequency: f32, | ||
} | ||
|
||
#[derive(Clone, Debug, Copy)] | ||
pub struct LatLonAlt(pub Coord<f32>, pub f32); | ||
|
||
impl FromStr for LatLonAlt { | ||
type Err = AnyError; | ||
fn from_str(s: &str) -> Result<Self, AnyError> { | ||
let (lat_str, lon_str, alt_str) = { | ||
let idx = s | ||
.find(',') | ||
.ok_or_else(|| anyhow!("not a valid lat,lon,alt"))?; | ||
let (lat_str, lon_alt_str) = s.split_at(idx); | ||
let idx = lon_alt_str[1..] | ||
.find(',') | ||
.ok_or_else(|| anyhow!("not a valid lat,lon,alt"))?; | ||
let (lon_str, alt_str) = lon_alt_str[1..].split_at(idx); | ||
(lat_str, lon_str, &alt_str[1..]) | ||
}; | ||
let lat = f32::from_str(lat_str)?; | ||
let lon = f32::from_str(lon_str)?; | ||
let alt = f32::from_str(alt_str)?; | ||
Ok(Self(Coord { y: lat, x: lon }, alt)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/// Antenna polarization. | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum Polarization { | ||
Horizontal = 0, | ||
Vertical = 1, | ||
} | ||
|
||
/// Siting criteria. | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum SittingCriteria { | ||
Random = 0, | ||
Careful = 1, | ||
VeryCareful = 2, | ||
} | ||
|
||
/// Radio climate. | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum Climate { | ||
Equatorial = 1, | ||
ContinentalSubtropical = 2, | ||
MaritimeSubtropical = 3, | ||
Desert = 4, | ||
ContinentalTemperate = 5, | ||
MaritimeTemperateOverLand = 6, | ||
MaritimeTemperateOverSea = 7, | ||
} | ||
|
||
/// Propagation mode. | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum Mode { | ||
NotSet = 0, | ||
LineOfSight = 1, | ||
Diffraction = 2, | ||
Troposcatter = 3, | ||
} | ||
|
||
/// Mode of variability. | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum ModeVariability { | ||
SingleMessage = 0, | ||
Accidental = 1, | ||
Mobile = 2, | ||
Broadcast = 3, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use std::ffi::c_int; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum ItmErrCode { | ||
#[error("TX terminal height is out of range")] | ||
TxTerminalHeight, | ||
#[error("RX terminal height is out of range")] | ||
RxTerminalHeight, | ||
#[error("Invalid value for radio climate")] | ||
InvalidRadioClimate, | ||
#[error("Time percentage is out of range")] | ||
InvalidTime, | ||
#[error("Location percentage is out of range")] | ||
InvalidLocation, | ||
#[error("Situation percentage is out of range")] | ||
InvalidSituation, | ||
#[error("Confidence percentage is out of range")] | ||
InvalidConfidence, | ||
#[error("Reliability percentage is out of range")] | ||
InvalidReliability, | ||
#[error("Refractivity is out of range")] | ||
Refractivity, | ||
#[error("Frequency is out of range")] | ||
Frequency, | ||
#[error("Invalid value for polarization")] | ||
Polarization, | ||
#[error("Epsilon is out of range")] | ||
Epsilon, | ||
#[error("Sigma is out of range")] | ||
Sigma, | ||
#[error("The imaginary portion of the complex impedance is larger than the real portion")] | ||
GroundImpedance, | ||
#[error("Invalid value for mode of variability")] | ||
Mdvar, | ||
#[error("Internally computed effective earth radius is invalid")] | ||
EffectiveEarth, | ||
#[error("Path distance is out of range")] | ||
PathDistance, | ||
#[error("Delta H (terrain irregularity parameter) is out of range")] | ||
DeltaH, | ||
#[error("Invalid value for TX siting criteria")] | ||
TxSitingCriteria, | ||
#[error("Invalid value for RX siting criteria")] | ||
RxSitingCriteria, | ||
#[error("Internally computed surface refractivity value is too small")] | ||
SurfaceRefractivitySmall, | ||
#[error("Internally computed surface refractivity value is too large")] | ||
SurfaceRefractivityLarge, | ||
} | ||
|
||
impl ItmErrCode { | ||
pub fn from_retcode<T>(err_code: c_int, val: T) -> Result<T, ItmErrCode> { | ||
let err = match err_code { | ||
0 | 1 => return Ok(val), | ||
1000 => ItmErrCode::TxTerminalHeight, | ||
1001 => ItmErrCode::RxTerminalHeight, | ||
1002 => ItmErrCode::InvalidRadioClimate, | ||
1003 => ItmErrCode::InvalidTime, | ||
1004 => ItmErrCode::InvalidLocation, | ||
1005 => ItmErrCode::InvalidSituation, | ||
1006 => ItmErrCode::InvalidConfidence, | ||
1007 => ItmErrCode::InvalidReliability, | ||
1008 => ItmErrCode::Refractivity, | ||
1009 => ItmErrCode::Frequency, | ||
1010 => ItmErrCode::Polarization, | ||
1011 => ItmErrCode::Epsilon, | ||
1012 => ItmErrCode::Sigma, | ||
1013 => ItmErrCode::GroundImpedance, | ||
1014 => ItmErrCode::Mdvar, | ||
1016 => ItmErrCode::EffectiveEarth, | ||
1017 => ItmErrCode::PathDistance, | ||
1018 => ItmErrCode::DeltaH, | ||
1019 => ItmErrCode::TxSitingCriteria, | ||
1020 => ItmErrCode::RxSitingCriteria, | ||
1021 => ItmErrCode::SurfaceRefractivitySmall, | ||
1022 => ItmErrCode::SurfaceRefractivityLarge, | ||
_ => unreachable!(), | ||
}; | ||
Err(err) | ||
} | ||
} |
Oops, something went wrong.