Skip to content

Commit

Permalink
Merge pull request #21 from novalabsxyz/jsk/subsume-itm-rs
Browse files Browse the repository at this point in the history
Subsume ITM repo as a workspace member
  • Loading branch information
JayKickliter authored Nov 20, 2023
2 parents 3c2fdcd + c597516 commit 06bb10d
Show file tree
Hide file tree
Showing 16 changed files with 656 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:

- name: Setup | Checkout
uses: actions/checkout@v3
with:
submodules: true

- name: Setup | Rust toolchain
uses: dtolnay/[email protected]
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
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
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"geopath",
"itm",
"nasadem",
"propah",
"terrain",
Expand All @@ -10,12 +11,14 @@ resolver = "2"
[workspace.dependencies]
approx = "0.5.1"
byteorder = "1.4.3"
clap = { version = "4.4.2", features = ["derive"] }
criterion = { version = "0.5", features = ["html_reports"] }
dashmap = "5.5.3"
geo = "0.26.0"
log = "0.4.20"
memmap2 = "0.7.1"
num-traits = "0.2.16"
serde = { version = "1", features = ["derive"] }
thiserror = "1.0.48"

# We want meaninful stack traces when profiling/debugging
Expand Down
1 change: 1 addition & 0 deletions extern/itm
Submodule itm added at 31d068
33 changes: 33 additions & 0 deletions itm/Cargo.toml
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"
14 changes: 14 additions & 0 deletions itm/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
SOFTWARE DISCLAIMER / RELEASE

This software was developed by employees of the National Telecommunications and Information Administration (NTIA), an agency of the Federal Government and is provided to you as a public service. Pursuant to Title 15 United States Code Section 105, works of NTIA employees are not subject to copyright protection within the United States.

The software is provided by NTIA “AS IS.” NTIA MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NTIA does not warrant or make any representations regarding the use of the software or the results thereof, including but not limited to the correctness, accuracy, reliability or usefulness of the software.

To the extent that NTIA holds rights in countries other than the United States, you are hereby granted the non-exclusive irrevocable and unconditional right to print, publish, prepare derivative works and distribute the NTIA software, in any medium, or authorize others to do so on your behalf, on a royalty-free basis throughout the World.

You may improve, modify, and create derivative works of the software or any portion of the software, and you may copy and distribute such modifications or works. Modified works should carry a notice stating that you changed the software and should note the date and nature of any such change.

You are solely responsible for determining the appropriateness of using and distributing the software and you assume all risks associated with its use, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and the unavailability or interruption of operation. This software is not intended to be used in any situation where a failure could cause risk of injury or damage to property.

Please provide appropriate acknowledgments of NTIA’s creation of the software in any copies or derivative works of this software.

10 changes: 10 additions & 0 deletions itm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Irregular Terrain Model (ITM)

`itm` is a wrapper around NTIA's [library] for modeling radio frequency
propogation.

[library]: https://github.com/NTIA/itm

## License

See NTIA's [license](LICENSE.md) file.
44 changes: 44 additions & 0 deletions itm/build.rs
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");
}
78 changes: 78 additions & 0 deletions itm/examples/p2p/main.rs
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
}
53 changes: 53 additions & 0 deletions itm/examples/p2p/options.rs
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))
}
}
81 changes: 81 additions & 0 deletions itm/src/error.rs
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)
}
}
Loading

0 comments on commit 06bb10d

Please sign in to comment.