1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use std::convert::TryFrom;

use common::distance;

use signal_models::propagation;
use signal_models::radiation::GenericRadiationPattern;

use uav::UavState;

/// A pattern used for adjusting the signal gain
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum GainPattern {
    /// A simple model where every direction has equal gain
    Uniform = 0,

    /// A model where the gain is strongest at the front and back, and weakest at the sides
    Figure8 = 1,
}

impl TryFrom<i32> for GainPattern {
    type Err = &'static str;

    fn try_from(value: i32) -> Result<GainPattern, &'static str> {
        match value {
            0 => Ok(GainPattern::Uniform),
            1 => Ok(GainPattern::Figure8),
            _ => Err("Invalid gain pattern"),
        }
    }
}

/// The model used for calculating the base signal strength from distance
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum PropagationModel {
    /// A simple free space model
    FreeSpace = 0,

    /// A propagation model that considers the direct wave and a ground reflection
    TwoWave = 1,
}

impl TryFrom<i32> for PropagationModel {
    type Err = &'static str;

    fn try_from(value: i32) -> Result<PropagationModel, &'static str> {
        match value {
            0 => Ok(PropagationModel::FreeSpace),
            1 => Ok(PropagationModel::TwoWave),
            _ => Err("Invalid propagation model"),
        }
    }
}

/// Configuration options for a signal model
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct SignalConfig {
    /// The amount of noise to add when generating new measurements with this model
    pub signal_noise: f32,

    /// The pattern used for adjusting the signal gain
    pub gain_pattern: GainPattern,

    /// The model used for calculating the base signal strength from distance
    pub propagation_model: PropagationModel,
}

impl SignalConfig {
    /// Create a new signal model using the specified parameters
    pub fn new(signal_noise: f32, gain_pattern: GainPattern, propagation_model: PropagationModel)
        -> SignalConfig {
        SignalConfig {
            signal_noise: signal_noise,
            gain_pattern: gain_pattern,
            propagation_model: propagation_model,
        }
    }
}

/// A simple direct wave path loss model
pub fn path_loss_model(state: UavState, position: [f32; 3]) -> f32 {
    propagation::path_loss_model(distance(state.position, position), 2.7, 0.03)
}

/// A model based on a direct wave and a single ground reflection
pub fn two_wave_model(state: UavState, position: [f32; 3]) -> f32 {
    propagation::two_wave_model(distance(state.position, position), 2.7, 0.03, 150e6, 1.0, 50.0)
}

/// Generates a radiation model where every direction returns the same gain.
pub fn omni_directional_model() -> GenericRadiationPattern {
    let keypoints = vec![(0.0, 1.0)];
    GenericRadiationPattern::new(keypoints.clone(), keypoints.clone(), keypoints)
}

/// Generates a basic radiation model
pub fn basic_radiation_model() -> GenericRadiationPattern {
    // TODO: Load this from a file.
    let simple_model: Vec<(u32, f32)> = vec![
        (0, 160.0),
        (15, 154.8),
        (30, 145.6),
        (45, 123.8),
        (60, 104.3),
        (75, 73.5),
        (90, 44.0),
        (105, 73.5),
        (120, 104.3),
        (135, 123.8),
        (150, 145.6),
        (165, 154.8),
        (180, 160.0),
        (195, 154.8),
        (210, 145.6),
        (225, 123.8),
        (240, 104.3),
        (255, 73.5),
        (270, 44.0),
        (285, 73.5),
        (300, 104.3),
        (315, 123.8),
        (330, 145.6),
        (345, 154.8),
    ];

    let max = 160.0;
    let keypoints: Vec<_> = simple_model.into_iter()
        .map(|(angle, magnitude)| ((angle as f32).to_radians(), magnitude / max))
        .collect();

    GenericRadiationPattern::new(keypoints.clone(), keypoints.clone(), keypoints)
}

/// A model for computing the expected RSSI value based on the state of the UAV and the position of
/// the beacon.
pub struct RssiModel<T> {
    radiation_pattern: GenericRadiationPattern,
    propagation_model: T,
}

impl<T> RssiModel<T>
    where T: Fn(UavState, [f32; 3]) -> f32
{
    /// Create a new RSSI mode given a radiation pattern and a propagation model.
    pub fn new(radiation_pattern: GenericRadiationPattern, propagation_model: T) -> RssiModel<T> {
        RssiModel {
            radiation_pattern: radiation_pattern,
            propagation_model: propagation_model,
        }
    }

    /// Compute the expected RSSI value for a signal source at a set position
    pub fn rssi(&self, uav_state: UavState, position: [f32; 3]) -> f32 {
        let (x_angle, y_angle, z_angle) = planar_angles(uav_state, position);
        let gain = self.radiation_pattern.calculate_gain(x_angle, y_angle, z_angle);
        gain * (self.propagation_model)(uav_state, position)
    }
}

/// Calculates the planar rotation angles from a uav state to a point. (x angle, y angle, z angle).
#[doc(hidden)]
pub fn planar_angles(state: UavState, point: [f32; 3]) -> (f32, f32, f32) {
    use cgmath::prelude::*;
    use cgmath::Vector2;

    let yz = Vector2::new(point[1], point[2]) - Vector2::new(state.position[1], state.position[2]);
    let xz = Vector2::new(point[0], point[2]) - Vector2::new(state.position[0], state.position[2]);
    let xy = Vector2::new(point[0], point[1]) - Vector2::new(state.position[0], state.position[1]);

    (
        yz.angle([state.orientation[1], state.orientation[2]].into()).0,
        xz.angle([state.orientation[0], state.orientation[2]].into()).0,
        xy.angle([state.orientation[0], state.orientation[1]].into()).0
     )
}