diff --git a/CLAUDE.md b/CLAUDE.md index 7ab3ecc..e2612c9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,7 +84,17 @@ src/ │ ├── afr.rs # AFR/Lambda analysis (fuel trim drift CUSUM, rich/lean zones) │ ├── derived.rs # Derived metrics (Volumetric Efficiency, injector duty cycle, ...) │ ├── filters.rs # Signal-processing filters (moving average, etc.) -│ └── statistics.rs # Descriptive statistics and correlation analysis +│ ├── statistics.rs # Descriptive statistics and correlation analysis +│ └── tables/ # Table generators (issues #3, #4) — see Table Generators below +│ ├── mod.rs # TableAnalyzer trait, TableEvent/RejectReason/RunReport, TableAccumulator +│ ├── stats.rs # median/MAD/percentile, update instants, effective update interval, robust sigma +│ ├── binning.rs # AxisSpec (edge lists), CellStats, Confidence, TableGrid, uniform_bin +│ ├── channel_map.rs # ChannelRole, ChannelMapping, three-tier auto-suggestion +│ ├── events.rs # invalid-sample masking, steadiness, interpolated crossings, rate runs +│ ├── lambda_delay.rs # Lambda delay table generator (#4) +│ ├── accel_enrich.rs # Acceleration enrichment table generator (#3) +│ ├── export.rs # CSV / clipboard TSV rendering, delay unit conversion +│ └── synthetic.rs # Synthetic-log builder + xorshift RNG for ground-truth tests ├── ipc/ │ ├── mod.rs # IPC module exports, DEFAULT_IPC_PORT │ ├── commands.rs # IpcCommand/IpcResponse wire types shared with mcp/client.rs @@ -122,6 +132,8 @@ src/ ├── settings_panel.rs # Consolidated settings (display, units, normalization, updates) ├── tool_properties_panel.rs # Dynamic panel showing controls for the active tool (channels / histogram / scatter) ├── analysis_panel.rs # Window for running analysis algorithms (src/analysis) on the active log + ├── table_generator.rs # Table tools: Tool Properties setup + central heatmap / inspector view + ├── table_export.rs # PNG / PDF export for generated tables (pure renderers + app wrappers) ├── data_panel.rs # Right-side data panel hosting DataWidget panes (rail, header, hide/restore) ├── widgets/ │ ├── mod.rs # DataWidget trait + static widget registry @@ -134,7 +146,7 @@ src/ ├── toast.rs # Toast notification system ├── icons.rs # Custom icon drawing utilities ├── tab_bar.rs # Multi-file tab interface - ├── tool_switcher.rs # Switch between Log Viewer, Scatter Plot, and Histogram tools + ├── tool_switcher.rs # Switch between the five tools (ActiveTool::ALL) ├── scatter_plot.rs # XY scatter plot visualization ├── histogram.rs # 2D histogram/heatmap view for channel distributions ├── export.rs # PNG and PDF export functionality @@ -234,6 +246,78 @@ Trait-based framework (`Analyzer`) for algorithms that process log data and can The `analysis_panel.rs` UI module (see below) hosts these analyzers, with category tabs and configurable parameters per algorithm. +### Table Generators (src/analysis/tables/) + +Two generators mine events out of loaded logs into 2-D tuning tables (design doc: +`docs/plans/2026-07-16-tuning-table-generators.md`, revised per the 2026-09-17 review on issues #3/#4): + +- **`lambda_delay.rs`** (issue #4) - injector pulse-width steps → first wideband crossing of + `max(k·σ, min_delta)`, binned RPM × load (MAP or TPS). Primary value is dead time (ms); t63, + response magnitude and step size are alternate measures. Strict/relaxed gating profiles. +- **`accel_enrich.rs`** (issue #3) - tip-ins from a native throttle-rate channel (scale detected; + Haltech's parser already divides ×10) or a computed TPS/MAP derivative → delay-compensated + signed excursion vs target/baseline, binned RPM × peak rate. Measures: correction %, depth, + duration, area-based %, area, delay used. When an `AeActive` role is mapped the table kind is + *additional* (multiply the ECU's current value) and events without ECU activity are rejected + (`AeKindMismatch`) so one cell never mixes kinds. + +They implement the sibling `TableAnalyzer` trait (not `Analyzer`, whose result is one value per +timestamp). Every event, accepted or rejected with a `RejectReason`, stays in the list so the +inspector and the run report's rejection breakdown ("41 events found · 39 rejected: 30 unsteady, +9 no response") can tell the user what to log next. `TableAccumulator` keeps raw per-event values +(medians/MADs cannot be merged incrementally) so logs can be added and removed, and re-bins per +measure on demand. Cells are median + MAD + `Confidence` (Empty / Low / Medium / High); empty and +low cells are never interpolated and export blank. + +**Load-bearing behaviors:** + +- **Sample-and-hold interpolation** (`events::find_crossing`, `stats::update_instants`) - CAN + widebands often update at 10-20 Hz inside a 50-500 Hz log. Noise (`robust_sigma_diff`) and + crossings are computed over distinct-value *update instants*, never raw samples (most raw first + differences are exactly zero). The previous reading for interpolation is the last update, but no + earlier than `t - effective_update_interval`: a flat baseline is still being sampled every + interval, so interpolating from its last value change would place every crossing far too early. +- **Invalid-sample masking** (`events::mask_invalid`) - Haltech writes an i32 sentinel family + (`-2147483617`, `…637`, …) for "no reading"; those and out-of-band samples become `NaN` before any + math, and a window with >10 % masked lambda rejects as `InvalidSamples`. +- **Alignment** (`tables::mapped_column`) - `Log::get_channel_data` drops ragged rows, so a column + can be shorter than `times`; the generators refuse with `ComputationError` rather than index + misaligned pairs (same contract as `channel_series` in the IPC handler). +- **Log identity** - accumulated events key on `LoadedFile::load_id`, a per-load nonce, never the + file index (shifts when a tab closes) or the bare file name (every rusEFI install has a + `Log1.mlg`). The UI's mapping cache keys on the same nonce. +- **Lambda-delay → AE composition** - `GeneratorContext::delay_table` carries the session's + lambda-delay grid; an AE event uses the matching cell's median when that cell is Medium/High, + else `assumed_delay_ms`, and records which in `TableEvent::note` and the `delay_used_ms` measure. +- **Axis cap** - `binning::MAX_BINS_PER_AXIS` (64) keeps any future MCP payload well under the + 512 KiB response guard. `histogram.rs` now calls `binning::uniform_bin` for its cell math so both + tools agree on boundaries. +- **Tools, not a window** - `ActiveTool::LambdaDelay` / `ActiveTool::AccelEnrich` render like + Histogram: setup in the Tool Properties panel (`render_table_tool_properties`), results in the + central panel (`render_table_tool_view`). `ActiveTool::generator_kind()` is the one helper the + match sites use, and `ActiveTool::ALL` fixes the switcher / View-menu / Cmd+1..5 order, so a new + tool is appended there and nowhere else. Tool switches go through `UltraLogApp::set_active_tool` + (analytics + table selection reset), not direct assignment. +- **State is app-level, not per-tab** - unlike `Tab::histogram_state`, `UltraLogApp::table_generator` + holds the accumulators, mappings and export options for both generators. The tables are + multi-log by design, so the active tab only decides which file *Run / Add current file* reads; + switching tabs or tools does not lose a table. +- **PNG has no text** - the crate has no font rasterizer, so `table_export::render_table_png` + draws cells and grid lines only (same as the histogram PNG). `render_table_pdf` draws values, + counts and axis labels with built-in Helvetica, whose encoding turns `~` into an arrow, so the + confidence markers are `*` (medium) and `?` (low). Both go through `tables::export::cell_value` + so PNG, PDF, CSV and clipboard blank the same cells and convert delay units identically. +- **Auto-suggestion** (`channel_map::suggest_mapping`) - normalization hit (100) → strong name + hints (50) → spec category + hint (60) → generic hints (40), then a data-plausibility veto on the + channel median; `overall|avg|average` names lose 10 points so a single sensor beats an averaged + one (averaging sensors with different transport delays smears the rise). Ties within 10 points + are flagged ⚠ in the UI. New built-in normalization entries back this: injector on-time names → + `Pulse Width`, and `TPS DOT` / `Throttle Position Derivative` / `TPS Delta` → `TPS Rate`. + +Not yet implemented from the design: MCP tools for the generators, per-ECU mapping presets on +disk (mapping and accumulators are session-only), the windowed cross-correlation mode, and the +wiki page. + ### IPC + MCP System (src/ipc/, src/mcp/) UltraLog embeds an MCP (Model Context Protocol) HTTP server so Claude Desktop can drive the running GUI — select channels, add computed channels, and query log data. @@ -304,7 +388,7 @@ UI rendering is split into focused modules that implement methods on `UltraLogAp - **`toast.rs`** - Toast notification overlay for user feedback - **`icons.rs`** - Custom icon drawing (upload icon for drop zone) - **`tab_bar.rs`** - Chrome-style tabs for multi-file support -- **`tool_switcher.rs`** - Switch between Log Viewer, Scatter Plot, and Histogram tools +- **`tool_switcher.rs`** - Switch between the five tools in `ActiveTool::ALL` (Log Viewer, Scatter Plot, Histogram, Lambda Delay, Accel Enrichment) - **`scatter_plot.rs`** - XY scatter plot for channel correlation analysis - **`histogram.rs`** - 2D histogram/heatmap view of channel distributions, with configurable cell coloring (average Z-value or hit count) - **`export.rs`** - PNG and PDF export with chart rendering @@ -485,6 +569,7 @@ The Track Map widget can draw map tile backgrounds. Tiles are **opt-in** (off by - **Multi-ECU Support** - Haltech, ECUMaster, RomRaider, Speeduino, rusEFI, AiM, Link, Emerald, MegaSquirt, TunerStudio MSL, MHD Tuning, Motorsport Electronics, RaceChrono, Woolich Racing Tuned, BlueDriver, DynamicEFI, and Locomotive log formats - **Computed Channels** - Create virtual channels from mathematical formulas with time-shifting (e.g., `RPM[-1]`, `Boost@-0.5s`) - **Analysis Algorithms** - AFR/Lambda drift and zone detection, derived metrics (VE, injector duty cycle), signal filters, and descriptive statistics (`src/analysis/`) +- **Table Generators** - Lambda delay and acceleration enrichment tuning tables mined from one or more logs, each a top-level tool beside Histogram, with auto-suggested channel roles, confidence-tiered cells, an event inspector, and CSV/clipboard/PNG/PDF export (`src/analysis/tables/`, `src/ui/table_generator.rs`, `src/ui/table_export.rs`) - **GPS Track Map** - Right-side data panel with a track map: lap detection, channel-colored polyline (Viridis/Turbo with editable range), hover-scrub/click-seek cursor sync, and opt-in Esri/OSM tile backgrounds (`src/ui/widgets/track_map.rs`, `src/tiles.rs`, `src/laps.rs`). GPS coordinate encodings are auto-detected and normalized to decimal degrees (`GpsCoordSpec` in `src/laps.rs`): NMEA `DDMM.mmmm`, milli/micro/1e-7-scaled integer degrees, and 0-360 longitude. Detection is conservative - values already in valid degree ranges are never transformed, and radians are deliberately not detected (ambiguous with genuine near-equator degree tracks). - **Claude Desktop / MCP Integration** - Embedded MCP server (`src/mcp/`) lets Claude control the running app over `http://localhost:52385/mcp` — select channels, add computed channels, query log data - **Unit Preferences** - Users can select display units for temperature, pressure, speed, distance, fuel economy, volume, flow rate, and acceleration @@ -509,7 +594,7 @@ Handled in `UltraLogApp::handle_keyboard_shortcuts` (`src/app.rs`); ignored whil - **Cmd/Ctrl+O** - Open file - **Cmd/Ctrl+W** - Close current tab - **Cmd/Ctrl+,** - Open Settings panel -- **Cmd/Ctrl+1/2/3** - Switch tool (Log Viewer / Scatter Plot / Histogram) +- **Cmd/Ctrl+1..5** - Switch tool, in `ActiveTool::ALL` order (Log Viewer / Scatter Plot / Histogram / Lambda Delay / Accel Enrichment) - **Cmd/Ctrl+Shift+F/C/T** - Switch side panel (Files / Tool Properties / Tools) - **Arrow Left/Right** - Step cursor one record (Shift = 10 records) - **Home/End** - Jump cursor to start/end of log diff --git a/Cargo.lock b/Cargo.lock index 808b0ac..8138730 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4578,7 +4578,7 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ultralog" -version = "2.14.1" +version = "2.15.0" dependencies = [ "anyhow", "arboard", diff --git a/Cargo.toml b/Cargo.toml index a22e105..a221b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ultralog" -version = "2.14.1" +version = "2.15.0" edition = "2024" # egui/eframe 0.36 is the binding constraint on the minimum supported Rust # version; edition 2024 itself only needs 1.85. diff --git a/README.md b/README.md index d1953d9..7c98168 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A high-performance, cross-platform ECU log viewer written in Rust.   - + --- @@ -79,6 +79,10 @@ Configurable units for 8 measurement categories: - **Scatter Plot** - XY scatter visualization for channel correlation analysis - **Histogram** - 2D heatmap visualization with configurable grid sizes (10x10 to 25x25) for analyzing channel distributions - **MCP Server** - Built-in Model Context Protocol server lets Claude Desktop (or any MCP client) control UltraLog — load files, select channels, get stats, create computed channels, and run analysis via `http://localhost:52453/mcp` +- **Table Generators** - Mine logged events into tuning tables. Each is a tool tab beside Histogram (⌘4 / ⌘5), with setup in the Tool Properties panel and CSV, clipboard, PNG and PDF export: + - **Lambda Delay Table** - time from injector pulse-width steps to the wideband response, binned by RPM × load, for closed-loop O2 delay tables (ms, engine cycles or ignition events) + - **Acceleration Enrichment Table** - tip-in lean/rich excursion depth, duration and a suggested starting correction, binned by RPM × throttle rate, with lambda-delay compensation from a table generated in the same session + - Auto-suggested channel roles, per-cell sample counts and confidence, multi-log accumulation, event inspector with jump-to-time, CSV export and tab-separated clipboard copy - **Analysis Tools** - Built-in signal processing and statistics: - **Filters** - Moving average, Kalman filter, and other signal processing tools - **Statistics** - Min/max, percentiles, standard deviation calculations diff --git a/docs/index.html b/docs/index.html index 8cd0fc8..2ec9e0a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -9,9 +9,9 @@