plotter WIP
This commit is contained in:
Generated
+707
-41
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user