Skip to content

Commit 2e2ef40

Browse files
committed
Add support for generic altimeter with min/max samples
1 parent 7c45733 commit 2e2ef40

File tree

5 files changed

+123
-0
lines changed

5 files changed

+123
-0
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [unreleased]
99

10+
## [2.13.0]
11+
12+
### Added
13+
14+
- Support for generic altimeter with min/max samples
15+
16+
### Changed
17+
1018
- Remove unused dependencies found by `cargo-shear`
1119
- Update Rust version from `1.90` to `1.92`
1220

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use std::path::Path;
2+
3+
use chrono::{DateTime, NaiveDateTime, Utc};
4+
use hdf5::types::VarLenUnicode;
5+
use ndarray::Array1;
6+
use plotinator_log_if::{
7+
hdf5::SkytemHdf5,
8+
prelude::{GeoSpatialDataBuilder, Plotable},
9+
rawplot::RawPlot,
10+
};
11+
use serde::{Deserialize, Serialize};
12+
13+
use crate::util::read_any_attribute_to_string;
14+
15+
#[derive(Debug, Clone, Serialize, Deserialize)]
16+
pub struct AltimeterMinMax {
17+
starting_timestamp_utc: DateTime<Utc>,
18+
dataset_description: String,
19+
raw_plots: Vec<RawPlot>,
20+
metadata: Vec<(String, String)>,
21+
}
22+
23+
impl SkytemHdf5 for AltimeterMinMax {
24+
const DESCRIPTIVE_NAME: &str = "Generic Altimeter Min/Max";
25+
26+
fn from_path(path: impl AsRef<Path>) -> anyhow::Result<Self> {
27+
let h5 = hdf5::File::open(path)?;
28+
let sensor_count = h5.attr("sensor_count")?.read_scalar::<u8>()?;
29+
let sensor_type = h5
30+
.attr("sensor_type")?
31+
.read_scalar::<VarLenUnicode>()?
32+
.to_string();
33+
let starting_timestamp = h5
34+
.attr("timestamp")?
35+
.read_scalar::<VarLenUnicode>()?
36+
.to_string();
37+
let starting_timestamp_utc: DateTime<Utc> =
38+
NaiveDateTime::parse_from_str(&starting_timestamp, "%Y%m%d_%H%M%S")?.and_utc();
39+
40+
let attr_names = h5.attr_names()?;
41+
let mut metadata: Vec<(String, String)> = Vec::with_capacity(attr_names.len());
42+
for attr_name in attr_names {
43+
let attr = h5.attr(&attr_name)?;
44+
let attr_val = read_any_attribute_to_string(&attr)?;
45+
metadata.push((attr_name, attr_val));
46+
}
47+
48+
let mut raw_plots = vec![];
49+
for sensor_id in 1..=sensor_count {
50+
let height_min_ds_name = format!("height_min_{sensor_id}");
51+
let height_max_ds_name = format!("height_max_{sensor_id}");
52+
let timestamp_ds_name = format!("timestamp_{sensor_id}");
53+
let heights_min: Array1<f32> = h5.dataset(&height_min_ds_name)?.read_1d()?;
54+
let heights_max: Array1<f32> = h5.dataset(&height_max_ds_name)?.read_1d()?;
55+
let heights_min: Vec<f64> = heights_min.into_iter().map(|h| h.into()).collect();
56+
let heights_max: Vec<f64> = heights_max.into_iter().map(|h| h.into()).collect();
57+
let times: Vec<u64> = h5.dataset(&timestamp_ds_name)?.read_raw()?;
58+
let legend_name_min = format!("{sensor_type}-min-{sensor_id}");
59+
let legend_name_max = format!("{sensor_type}-max-{sensor_id}");
60+
if let Some(dataseries) = GeoSpatialDataBuilder::new(legend_name_min)
61+
.timestamp(&times)
62+
.altitude_from_laser(heights_min)
63+
.altitude_valid_range((0.0, 500.)) // Safe to say it's invalid if it's above 500m
64+
.build_into_rawplot()?
65+
{
66+
raw_plots.push(dataseries);
67+
}
68+
if let Some(dataseries) = GeoSpatialDataBuilder::new(legend_name_max)
69+
.timestamp(&times)
70+
.altitude_from_laser(heights_max)
71+
.altitude_valid_range((0.0, 500.)) // Safe to say it's invalid if it's above 500m
72+
.build_into_rawplot()?
73+
{
74+
raw_plots.push(dataseries);
75+
}
76+
}
77+
78+
Ok(Self {
79+
starting_timestamp_utc,
80+
dataset_description: "Generic Altimeter(s) min/max".to_owned(),
81+
raw_plots,
82+
metadata,
83+
})
84+
}
85+
}
86+
87+
impl Plotable for AltimeterMinMax {
88+
fn raw_plots(&self) -> &[RawPlot] {
89+
&self.raw_plots
90+
}
91+
92+
fn first_timestamp(&self) -> DateTime<Utc> {
93+
self.starting_timestamp_utc
94+
}
95+
96+
fn descriptive_name(&self) -> &str {
97+
Self::DESCRIPTIVE_NAME
98+
}
99+
100+
fn labels(&self) -> Option<&[plotinator_log_if::prelude::PlotLabels]> {
101+
None
102+
}
103+
104+
fn metadata(&self) -> Option<Vec<(String, String)>> {
105+
Some(self.metadata.clone())
106+
}
107+
}

crates/plotinator-hdf5/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod altimeter;
2+
pub mod altimeter_minmax;
23
pub mod bifrost;
34
pub mod frame_altimeters;
45
pub mod frame_gps;

crates/plotinator-supported-formats/src/hdf5.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@ define_supported_hdf5_formats! {
101101
NjordIns => plotinator_hdf5::njord_ins::NjordIns,
102102
Tsc => plotinator_hdf5::tsc::Tsc,
103103
Altimeter => plotinator_hdf5::altimeter::Altimeter,
104+
AltimeterMinMax => plotinator_hdf5::altimeter_minmax::AltimeterMinMax,
104105
}

src/app/plot_app/supported_formats_table.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ const HDF5_LOG_FORMATS: &[LogFormat<'_>] = &[
125125
link: "https://github.com/luftkode/frame-altimeter",
126126
subitems: None,
127127
},
128+
LogFormat {
129+
title: "Generic Altimeter Min/Max",
130+
description: "Any altimeter implemented with the generic HDF5 altimeter format with min/max readings",
131+
link: "https://github.com/luftkode/frame-altimeter",
132+
subitems: None,
133+
},
128134
];
129135

130136
/// Draws a single log format entry

0 commit comments

Comments
 (0)