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
93 changes: 89 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion 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
@@ -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.
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A high-performance, cross-platform ECU log viewer written in Rust.

![CI](https://github.com/ClassicMiniDIY/UltraLog/actions/workflows/ci.yml/badge.svg)
![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)
![Version](https://img.shields.io/badge/version-2.14.1-green.svg)
![Version](https://img.shields.io/badge/version-2.15.0-green.svg)

---

Expand Down Expand Up @@ -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
Expand Down
Loading
Loading