rfcalcs 0.1.1

Radio Frequency equations and calculations
Documentation
//! rust-rfcalcs Fresnel calculations
//! https://en.wikipedia.org/wiki/Fresnel_zone#Fresnel_zone_clearance
//!
//! Copyright 2017 Ryan Kurte
//!

use types::*;
use consts::*;

/// MIN_DISTANCE_WAVELENGTH_RATIO is the minimum ratio of distance:wavelength for viable
/// calculations. This is used as a programattic sanity check for distance >> wavelength
const MIN_DISTANCE_WAVELENGTH_RATIO: f64 = 0.1;

/// fresnel_point calculates the fresnel zone radius d for a given wavelength
/// and order at a point P between endpoints
pub fn fresnel_point(freq: Frequency, d1: Distance, d2: Distance, order: i64) -> Result<f64, &'static str> {
    let wavelength = freq.to_wavelength();

    if ((d1.0 * MIN_DISTANCE_WAVELENGTH_RATIO) < wavelength.0) || ((d2.0 * MIN_DISTANCE_WAVELENGTH_RATIO) < wavelength.0) {
        return Err("Fresnel calculation valid only for distances >> wavelength");
    }

    return Ok((((order as f64) * wavelength.0 * d1.0 * d2.0) / (d1.0 + d2.0)).sqrt());
}

/// fresnel_first_zone calculates the maximum fresnel zone radius for a given frequency
pub fn fresnel_first_zone(freq: Frequency, dist: Distance) -> Result<f64, &'static str> {
    let wavelength = freq.to_wavelength();

    if (dist.0 * MIN_DISTANCE_WAVELENGTH_RATIO) < wavelength.0 {
        return Err("Fresnel calculation valid only for distance >> wavelength");
    }

    return Ok(0.5_f64 * (C * dist.0 / freq.0).sqrt());
}

/// fresnel_kirchoff_diffraction_param Calculates the Fresnel-Kirchoff Diffraction parameter
/// d1 and d2 are the distances between the "knife edge" impingement and the transmitter/receiver
/// h is the impingement, where -ve is below LoS and +ve is above LoS
/// https://en.wikipedia.org/wiki/Kirchhoff%27s_diffraction_formula
/// https://s.campbellsci.com/documents/au/technical-papers/line-of-sight-obstruction.pdf
pub fn fresnel_kirchoff_diffraction_param(freq: Frequency, d1: Distance, d2: Distance, h: Distance) -> f64 {
    let wavelength = freq.to_wavelength();
    return h.0 * ((2_f64 * (d1.0 + d2.0)) / (wavelength.0 * (d1.0 * d2.0))).sqrt();
}

/// CalculateFresnelKirchoffLossApprox Calculates approximate loss due to diffraction using
/// the Fresnel-Kirchoff Diffraction parameter. This approximate is valid for values >= -0.7
/// https://s.campbellsci.com/documents/au/technical-papers/line-of-sight-obstruction.pdf
pub fn fresnel_kirchoff_loss_approx(v: f64) -> Result<Attenuation, &'static str> {
    if v < -0.7 {
        return Err("Fresnel-Kirchoff loss approximation only valid for v >= -0.7");
    }
    let loss = 6.9_f64 + 20_f64 * (((v - 0.1_f64).powi(2) + 1_f64).sqrt() + v - 0.1_f64).log10();
    return Ok(Attenuation(loss));
}


#[cfg(test)]
mod tests {

    extern crate assert_approx_eq;
    use super::*;

    #[test]
    fn test_fresnel_first_zone() {
        /// Magic Numbers from: http://www.wirelessconnections.net/calcs/FresnelZone.asp
        let mut zone: f64;

        zone = fresnel_first_zone(ghz(2.4), m(10e3)).unwrap();
        assert_approx_eq::assert_approx_eq!(17.671776, zone, zone.abs() / 1000_f64);

        zone = fresnel_first_zone(ghz(2.4), m(100e3)).unwrap();
        assert_approx_eq::assert_approx_eq!(55.883, zone, zone.abs() / 1000_f64);
    }

    #[test]
    fn test_fresnel_kirchoff_diffraction_param() {
        let param: f64 = fresnel_kirchoff_diffraction_param(mhz(900_f64), km(8_f64), km(12_f64), m(-0.334_f64));
        assert_approx_eq::assert_approx_eq!(-0.011812, param, param.abs() / 1000_f64);
    }

    #[test]
    fn test_fresnel_kirchoff_loss_approx() {
        let loss: Attenuation = fresnel_kirchoff_loss_approx(-0.012_f64).unwrap();
        assert_approx_eq::assert_approx_eq!(5.93, loss.0, loss.0.abs() / 1000_f64);
    }

}