plotter WIP

This commit is contained in:
2026-05-23 20:44:49 +02:00
parent ec0bc28929
commit d1bfb7702d
6 changed files with 857 additions and 45 deletions
Generated
+707 -41
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -5,7 +5,7 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["libsvld", "svldcli", "svldgui"] members = ["libsvld", "svldcli", "svldgui", "svldplotter"]
[profile.release] [profile.release]
lto = "fat" lto = "fat"
+2 -2
View File
@@ -1,6 +1,6 @@
mod asc; pub mod asc;
pub mod can_frame; pub mod can_frame;
mod motec; pub mod motec;
use std::{ use std::{
fs::File, fs::File,
+1 -1
View File
@@ -97,7 +97,7 @@ pub fn get_metadata_total_count(
/// `bytes_le`). Mirrors `calc_bits` + `calc_signed_bits` + factor/offset in /// `bytes_le`). Mirrors `calc_bits` + `calc_signed_bits` + factor/offset in
/// _ref_lib/src/process.cc. /// _ref_lib/src/process.cc.
#[inline] #[inline]
fn extract_signal(bytes_le: u64, sig: &Signal) -> f64 { pub fn extract_signal(bytes_le: u64, sig: &Signal) -> f64 {
let size = sig.size; let size = sig.size;
let shifted = match sig.byte_order { let shifted = match sig.byte_order {
ByteOrder::BigEndian => bytes_le.swap_bytes() >> (64 - size - sig.start_bit), ByteOrder::BigEndian => bytes_le.swap_bytes() >> (64 - size - sig.start_bit),
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "svldplotter"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "svldplotter"
test = false
bench = false
[dependencies]
libsvld = { path = "../libsvld" }
can-dbc = "9.1"
eframe = "0.34"
egui = "0.34"
egui_plot = "0.35"
+129
View File
@@ -0,0 +1,129 @@
use std::{fs, path::Path};
use can_dbc::Dbc;
use eframe::egui;
use egui::Vec2b;
use egui_plot::{Line, Plot, PlotPoint, PlotPoints};
use libsvld::DecodeResult;
fn main() {
let native_options = eframe::NativeOptions::default();
eframe::run_native("My egui App", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc))))).unwrap();
}
struct MyEguiApp {
data: DecodeResult,
points: Vec<PlotPoint>,
buffer: Vec<PlotPoint>,
}
impl MyEguiApp {
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
let args: Vec::<_> = std::env::args().collect();
let data =
fs::read_to_string(Path::new(&args[1])).expect("Unable to read input file");
let dbc = Dbc::try_from(data.as_str()).expect("Failed to parse dbc file");
let asc_path = Path::new(&args[2]);
let data = libsvld::decode(&dbc, asc_path);
let mut points = Vec::<PlotPoint>::new();
let apps_sig = data
.metadata
.get(&48)
.unwrap()
.signals
.iter()
.find(|x| x.name == "AS_Torque_Vector")
.unwrap();
for frame in data.data.get(&48).unwrap() {
let bytes = u64::from_le_bytes(frame.data);
points.push(PlotPoint {
x: frame.timestamp,
y: libsvld::motec::extract_signal(bytes, &apps_sig.signal)
});
}
Self {
data,
points,
buffer: Vec::new(),
}
}
}
// Min/max bucketing: for each horizontal pixel column in [x_min, x_max], emit
// the min-y and max-y point so spikes survive decimation. Pushed in x order
// (min/max swapped if needed) to keep the line monotonically increasing in x.
fn decimate(
points: &[PlotPoint],
x_min: f64,
x_max: f64,
buckets: usize,
out: &mut Vec<PlotPoint>,
) {
out.clear();
if points.is_empty() || buckets == 0 || !(x_max > x_min) {
return;
}
let lo = points.partition_point(|p| p.x < x_min).saturating_sub(1);
let hi = (points.partition_point(|p| p.x <= x_max) + 1).min(points.len());
let visible = &points[lo..hi];
if visible.len() <= buckets * 2 {
out.extend_from_slice(visible);
return;
}
let bucket_w = (x_max - x_min) / buckets as f64;
let mut i = 0;
for b in 0..buckets {
let b_end = x_min + (b as f64 + 1.0) * bucket_w;
let start = i;
while i < visible.len() && visible[i].x < b_end {
i += 1;
}
if start == i {
continue;
}
let slice = &visible[start..i];
let mut min_p = slice[0];
let mut max_p = slice[0];
for p in &slice[1..] {
if p.y < min_p.y { min_p = *p; }
if p.y > max_p.y { max_p = *p; }
}
if min_p.x <= max_p.x {
out.push(min_p);
out.push(max_p);
} else {
out.push(max_p);
out.push(min_p);
}
}
}
impl eframe::App for MyEguiApp {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show_inside(ui, |ui| {
let full_min = self.points.first().map(|p| p.x).unwrap_or(0.0);
let full_max = self.points.last().map(|p| p.x).unwrap_or(1.0);
let plot = Plot::new("APPS")
.include_x(full_min)
.include_x(full_max)
.allow_zoom(Vec2b{x: true, y: false});
plot.show(ui, |pui| {
let bounds = pui.plot_bounds();
let x_min = bounds.min()[0];
let x_max = bounds.max()[0];
let px_w = pui.transform().frame().width() as usize;
decimate(&self.points, x_min, x_max, px_w.max(1), &mut self.buffer);
let ppoints = PlotPoints::Borrowed(&self.buffer);
pui.line(Line::new("AS_Torque_Vector", ppoints));
});
});
}
}