nyx_space/od/msr/
measurement.rs

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
/*
    Nyx, blazing fast astrodynamics
    Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

use super::MeasurementType;
use hifitime::Epoch;
use indexmap::IndexSet;
use nalgebra::{allocator::Allocator, DefaultAllocator, DimName, OVector};
use std::collections::HashMap;
use std::fmt;

/// A type-agnostic simultaneous measurement storage structure. Allows storing any number of simultaneous measurement of a given taker.
#[derive(Clone, Debug, PartialEq)]
pub struct Measurement {
    /// Tracker alias which made this measurement
    pub tracker: String,
    /// Epoch of the measurement
    pub epoch: Epoch,
    /// All measurements made simultaneously
    pub data: HashMap<MeasurementType, f64>,
}

impl Measurement {
    pub fn new(tracker: String, epoch: Epoch) -> Self {
        Self {
            tracker,
            epoch,
            data: HashMap::new(),
        }
    }

    pub fn push(&mut self, msr_type: MeasurementType, msr_value: f64) {
        self.data.insert(msr_type, msr_value);
    }

    pub fn with(mut self, msr_type: MeasurementType, msr_value: f64) -> Self {
        self.push(msr_type, msr_value);
        self
    }

    /// Builds an observation vector for this measurement provided a set of measurement types.
    /// If the requested measurement type is not available, then that specific row is set to zero.
    /// The caller must set the appropriate sensitivity matrix rows to zero.
    pub fn observation<S: DimName>(&self, types: &IndexSet<MeasurementType>) -> OVector<f64, S>
    where
        DefaultAllocator: Allocator<S>,
    {
        let mut obs = OVector::zeros();
        for (i, t) in types.iter().enumerate() {
            if let Some(msr_value) = self.data.get(t) {
                obs[i] = *msr_value;
            }
        }
        obs
    }

    /// Returns a vector specifying which measurement types are available.
    pub fn availability(&self, types: &IndexSet<MeasurementType>) -> Vec<bool> {
        let mut rtn = Vec::with_capacity(types.len());
        for (i, t) in types.iter().enumerate() {
            if self.data.contains_key(t) {
                rtn[i] = true;
            }
        }
        rtn
    }
}

impl fmt::Display for Measurement {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let msrs = self
            .data
            .iter()
            .map(|(msr_type, msr_value)| format!("{msr_type:?} = {msr_value} {}", msr_type.unit()))
            .collect::<Vec<String>>()
            .join(", ");

        write!(f, "{} measured {} on {}", self.tracker, msrs, self.epoch)
    }
}