Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [2.13.0]

### Added

- Support for generic altimeter with min/max samples

### Changed

- Remove unused dependencies found by `cargo-shear`
- Update Rust version from `1.90` to `1.92`

Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ members = ["crates/*"]

[workspace.package]
authors = ["SkyTEM Surveys", "Marc Beck König"]
version = "2.12.0"
version = "2.13.0"
edition = "2024"
rust-version = "1.88.0"
license = "MIT OR Apache-2.0"
Expand Down
114 changes: 114 additions & 0 deletions crates/plotinator-hdf5/src/altimeter_minmax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use crate::util::read_any_attribute_to_string;
use chrono::{DateTime, NaiveDateTime, Utc};
use hdf5::types::VarLenUnicode;
use ndarray::Array1;
use plotinator_log_if::{
hdf5::SkytemHdf5,
prelude::{GeoSpatialDataBuilder, Plotable},
rawplot::RawPlot,
};
use serde::{Deserialize, Serialize};
use std::path::Path;

const ALTITUDE_VALID_RANGE: (f64, f64) = (0.0, 500.0);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AltimeterMinMax {
starting_timestamp_utc: DateTime<Utc>,
dataset_description: String,
raw_plots: Vec<RawPlot>,
metadata: Vec<(String, String)>,
}

impl AltimeterMinMax {
fn process_sensor(
h5: &hdf5::File,
sensor_id: u8,
sensor_type: &str,
raw_plots: &mut Vec<RawPlot>,
) -> anyhow::Result<()> {
let times: Vec<u64> = h5.dataset(&format!("timestamp_{sensor_id}"))?.read_raw()?;

for (suffix, dataset_prefix) in [("min", "height_min"), ("max", "height_max")] {
let heights: Array1<f32> = h5
.dataset(&format!("{dataset_prefix}_{sensor_id}"))?
.read_1d()?;
let heights: Vec<f64> = heights.into_iter().map(|h| h.into()).collect();
let legend_name = format!("{sensor_type}-{suffix}-{sensor_id}");

if let Some(plot) = GeoSpatialDataBuilder::new(legend_name)
.timestamp(&times)
.altitude_from_laser(heights)
.altitude_valid_range(ALTITUDE_VALID_RANGE)
.build_into_rawplot()?
{
raw_plots.push(plot);
}
}

Ok(())
}
}

impl SkytemHdf5 for AltimeterMinMax {
const DESCRIPTIVE_NAME: &str = "Generic Altimeter Min/Max";

fn from_path(path: impl AsRef<Path>) -> anyhow::Result<Self> {
let h5 = hdf5::File::open(path)?;
let sensor_count = h5.attr("sensor_count")?.read_scalar::<u8>()?;
let sensor_type = h5
.attr("sensor_type")?
.read_scalar::<VarLenUnicode>()?
.to_string();
let starting_timestamp = h5
.attr("timestamp")?
.read_scalar::<VarLenUnicode>()?
.to_string();
let starting_timestamp_utc: DateTime<Utc> =
NaiveDateTime::parse_from_str(&starting_timestamp, "%Y%m%d_%H%M%S")?.and_utc();

let metadata: Vec<(String, String)> = h5
.attr_names()?
.into_iter()
.filter_map(|attr_name| {
let attr = h5.attr(&attr_name).ok()?;
let attr_val = read_any_attribute_to_string(&attr).ok()?;
Some((attr_name, attr_val))
})
.collect();

let mut raw_plots = vec![];
for sensor_id in 1..=sensor_count {
Self::process_sensor(&h5, sensor_id, &sensor_type, &mut raw_plots)?;
}

Ok(Self {
starting_timestamp_utc,
dataset_description: "Generic Altimeter(s) min/max".to_owned(),
raw_plots,
metadata,
})
}
}

impl Plotable for AltimeterMinMax {
fn raw_plots(&self) -> &[RawPlot] {
&self.raw_plots
}

fn first_timestamp(&self) -> DateTime<Utc> {
self.starting_timestamp_utc
}

fn descriptive_name(&self) -> &str {
Self::DESCRIPTIVE_NAME
}

fn labels(&self) -> Option<&[plotinator_log_if::prelude::PlotLabels]> {
None
}

fn metadata(&self) -> Option<Vec<(String, String)>> {
Some(self.metadata.clone())
}
}
1 change: 1 addition & 0 deletions crates/plotinator-hdf5/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod altimeter;
pub mod altimeter_minmax;
pub mod bifrost;
pub mod frame_altimeters;
pub mod frame_gps;
Expand Down
1 change: 1 addition & 0 deletions crates/plotinator-supported-formats/src/hdf5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ define_supported_hdf5_formats! {
NjordIns => plotinator_hdf5::njord_ins::NjordIns,
Tsc => plotinator_hdf5::tsc::Tsc,
Altimeter => plotinator_hdf5::altimeter::Altimeter,
AltimeterMinMax => plotinator_hdf5::altimeter_minmax::AltimeterMinMax,
}
6 changes: 6 additions & 0 deletions src/app/plot_app/supported_formats_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ const HDF5_LOG_FORMATS: &[LogFormat<'_>] = &[
link: "https://github.com/luftkode/frame-altimeter",
subitems: None,
},
LogFormat {
title: "Generic Altimeter Min/Max",
description: "Any altimeter implemented with the generic HDF5 altimeter format with min/max readings",
link: "https://github.com/luftkode/frame-altimeter",
subitems: None,
},
];

/// Draws a single log format entry
Expand Down