Highlights
First stable release since v2.10.1. It carries the MHD Tuning support below plus everything that was staged for 2.11.0 but never shipped stable — a deep correctness and performance sweep, settings persistence, and a rewritten expression engine.
New: MHD Tuning log support
UltraLog now reads CSV datalogs exported from the MHD Tuning app — BMW N54, N55, S55, B58 and other platforms flashed with MHD.
- Units are read straight from the column headers, with MHD's degree notation translated:
(*C) → °C, (*) → °, (*CRK) → °CRK, and (-) for unitless channels
Time is read as seconds and normalized to start at zero
- The tune file name (e.g.
MHD 5.26 IJE0S ST2V7.bin) is kept as log metadata instead of cluttering the channel list
- Unparseable fields carry the last known value forward, so columns never shift out of alignment
Fixed: logs with # metadata headers loaded with no channels
This is the bug behind #78. MHD exports — and OBD-II app exports such as OBDLink — start with a block of #-prefixed metadata lines before the real CSV header:
#Encoding: UTF-8
#Ecu PRGID: 7616431
#VIN: ...
Time,Boost (Bar),Coolant (*C),RPM (RPM),...
Every format detector inspects the first line of a file. That line was a comment, so nothing matched, detection fell through to the default parser, and the log opened with zero channels — the app appeared to do nothing at all.
Text logs are now normalized before format detection: a leading UTF-8 BOM is stripped, and a leading block of # comment lines is dropped so the real header row is what detection sees. This fixes MHD and the OBDLink class of failure together, and any future exporter that writes a comment preamble.
Thanks to @Turbo-Tuning for the report and the sample log, and to @andris-silis for spotting the same failure with OBDLink and narrowing it to the # lines.
Also in this release (staged for 2.11.0)
Crash and data-correctness fixes
- Speeduino: truncated MLG v2 headers caused an index-out-of-bounds panic
- App: a parser panic on the loader thread left the UI stuck on the loading spinner forever — errors now surface as a toast
- Haltech: unparseable fields (blank,
N/A, text gears) shifted every later column left, misaligning the whole row
- Haltech: sessions crossing midnight produced non-monotonic timestamps, breaking time-shifted computed channels like
RPM@-0.1s
- Normalization: a channel named
Speed normalized to RPM or Vehicle Speed nondeterministically between launches
- App: opening a file while another was loading silently dropped the first result; multi-file drag-and-drop loaded only the first file
- App: analysis results were not reindexed when a file was removed, attaching cached analyses to the wrong file
- Adapters: spec cache filenames are sanitized against path traversal
Performance
- The MCP/IPC server no longer forces a 10 Hz repaint forever — the GUI wakes on demand, ending idle battery drain
- Chart downsampling is cached per channel and recomputed only when the viewport changes, instead of every channel every frame
- Scatter heatmaps are built once per axis selection rather than rebuilt per frame
Features
- Settings persistence — unit preferences, font scale, colorblind mode, field normalization, cursor tracking, auto-update preference, and custom channel mappings now survive restarts
Esc stops playback and Cmd/Ctrl+E exports the current view as PNG
Under the hood
- The
meval formula dependency was replaced with a built-in expression engine. meval was unmaintained and its transitive nom 1.2.4 dependency is rejected by future Rust versions. Formula behavior is unchanged; formulas now compile once and evaluate with no parsing or allocation per record.