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
29 changes: 28 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,33 @@ because it also leads with a `Time` column, and only matches a first column of e
by name (lowercased) in `find_gps_channels` (`src/ui/widgets/track_map.rs`); renaming or
suffixing them silently disables the Track Map for MSL logs.

**Emerald parser load-bearing behaviors** (`src/parsers/emerald.rs`):

- **Channel IDs come from EM Soft's variable list, and most are unconfirmed** - the `[chanN]`
value in the `.lg2` is an ID into EM Soft's logger variable list, which Emerald does not
document. Only IDs 1, 15, 20, 24, 26, 31, 32 and 41 are confirmed (issue #93, reporter's EM
Soft labels). The rest are original guesses. ID 1 is the K6 lambda input in mV (not TPS),
41 is TPS, 31 is the 0-15 load site (not injector duty). Confirm an ID against an EM Soft
screenshot before renaming it. A rename must also go into the `source_names` of
`adapters/emerald/emerald-lg.adapter.yaml` in the ClassicMiniDIY/OECUASpecs repo (fetched
by `build.rs` into the gitignored `spec/OECUASpecs/`); spec normalization matches on those
names. Its `internal_id` field documents the IDs but UltraLog does not read it.
- **MAP scaling depends on `[ValU]`** - the second `[ValU]` value is the logger's pressure unit:
`1` stores MAP in whole kPa, `2` in mbar. Hardcoding either one makes the other log read 10x
wrong. Unknown codes keep the mbar scaling.
- **ID 1 is named "Lambda Sensor Voltage" on purpose** - it is a 0-1 V narrowband input where
a higher reading is richer. The `"volt"` veto on `ChannelRole::Lambda` in
`channel_map.rs` depends on that word, so the table generators never map it as lambda.
- **Repeated names get the channel ID appended** - IDs 21/24 and 45/46 share a name, and name
lookups return the first match. `disambiguate_names` keeps the plain name on a
`CONFIRMED_IDS` entry (else the first by slot) and appends ` (ID n)` to the others, so a
formula bound to `Ignition Advance` does not change meaning with slot order.
- **The value column is the slot number, not the list position** - `[chanN]` reads bytes
`8 + (N-1)*2` of the record. `parse_lg2` drops slots outside 1-8 and repeated slots, so an
unreadable or missing `[chanN]` cannot shift later channels onto the wrong data.
- **Ign Adv (ID 24) offset is inferred** - `raw * 0.1 - 50` gives 5.5° at idle and 14-17° at
230 kPa boost. It is not from Emerald documentation.

**Haltech parser load-bearing behaviors** (`src/parsers/haltech.rs`, added for wall-clock-timestamped exports):

- **Last-known-value substitution** - Unparseable/blank fields are filled with the last successfully parsed value for that column (`0.0` before the first valid sample), matching the approach used in `parsers/ecumaster.rs`. This preserves column alignment instead of shifting subsequent columns when a field fails to parse.
Expand Down Expand Up @@ -646,7 +673,7 @@ Example log files are in `exampleLogs/` organized by ECU type:
- `exampleLogs/aim/` - AiM XRK/DRK files
- `exampleLogs/link/` - Link ECU LLG files
- `exampleLogs/woolich/` - Woolich Racing Tuned CSV exports
- `exampleLogs/emerald/` - Emerald K6/M3D `.lg1`/`.lg2` files
- `exampleLogs/emerald/` - Emerald K6/M3D `.lg1`/`.lg2` files (`EM Log MG ZS Turbo boost run` is the issue #93 log, `[ValU]` pressure unit kPa; the others use mbar)
- `exampleLogs/megasquirt/` - MegaSquirt TunerStudio CSV exports, plus a `_gps.mlg` fixture with synthetic GPS channels (generated by `examples/inject_fake_gps_mlg.rs`; the coordinates are a fake closed loop, not a real location)
- `exampleLogs/mhd/` - MHD Tuning CSV exports (VIN redacted)
- `exampleLogs/msl/` - TunerStudio MSL excerpt from issue #86 (RealDash logging a Speeduino; trimmed to 2000 data rows, keeping the leading no-GPS-fix rows where `GPS Date`/`GPS Time` are blank)
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.15.0"
version = "2.15.1"
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
5 changes: 3 additions & 2 deletions 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.15.0-green.svg)
![Version](https://img.shields.io/badge/version-2.15.1-green.svg)

---

Expand Down Expand Up @@ -139,7 +139,8 @@ Configurable units for 8 measurement categories:
- **File type:** Binary format (`.lg1` data + `.lg2` channel definitions)
- **Features:** Native binary format parser for Emerald K6/M3D ECUs
- **Supported devices:** Emerald K6, M3D, and compatible ECU models
- **Supported data:** TPS, Air Temp, MAP, Lambda, Oil/Fuel Pressure, Oil/Fuel Temp, Exhaust Temp, Boost Target/Duty, RPM, Coolant Temp, Battery Voltage, Ignition Advance, Injector Pulse Width, and more
- **Supported data:** RPM, TPS, MAP, lambda sensor, ignition advance, injector duration, boost PWM, load site, air and coolant temperature, and more
- **Units:** MAP is shown in kPa. The EM Soft logger pressure setting must be kPa or bar; other settings are not yet supported
- **Note:** Both `.lg1` (data) and `.lg2` (channel definitions) files must be in the same directory

### MegaSquirt / Honda Tuning Studio - Full Support
Expand Down
6 changes: 3 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@
"applicationCategory": "UtilitiesApplication",
"applicationSubCategory": "Automotive Software",
"operatingSystem": ["Windows 10", "Windows 11", "macOS", "Linux"],
"softwareVersion": "2.15.0",
"releaseNotes": "https://github.com/ClassicMiniDIY/UltraLog/releases/tag/v2.15.0",
"softwareVersion": "2.15.1",
"releaseNotes": "https://github.com/ClassicMiniDIY/UltraLog/releases/tag/v2.15.1",
"downloadUrl": "https://github.com/ClassicMiniDIY/UltraLog/releases/latest",
"installUrl": "https://github.com/ClassicMiniDIY/UltraLog/releases/latest",
"screenshot": [
Expand Down Expand Up @@ -1429,7 +1429,7 @@ <h1 id="hero-heading" class="tagline" itemprop="headline">Unlock Your Performanc
<div class="hero-badges">
<span class="version-badge">
<span class="new-tag">New</span>
v2.15.0
v2.15.1
</span>
<a href="https://github.com/ClassicMiniDIY/UltraLog" class="opensource-badge" target="_blank" rel="noopener noreferrer">
<i class="fa-brands fa-github" aria-hidden="true"></i> Open Source
Expand Down
2 changes: 1 addition & 1 deletion docs/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
<url>
<loc>https://ultralog.co/</loc>
<lastmod>2026-09-18</lastmod>
<lastmod>2026-09-27</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
<image:image>
Expand Down
Binary file not shown.
22 changes: 22 additions & 0 deletions exampleLogs/emerald/EM Log MG ZS Turbo boost run.lg2
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[chan1]
20
[chan2]
41
[chan3]
1
[chan4]
32
[chan5]
24
[chan6]
26
[chan7]
15
[chan8]
31
[ValU]
0
1
0
0
0
7 changes: 7 additions & 0 deletions src/analysis/tables/channel_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ impl ChannelRole {
"good",
"ready",
"protect",
// Sensor voltage (Emerald "Lambda Sensor Voltage" is a 0-1 V
// narrowband input): higher reads richer, so it is not lambda.
"volt",
],
Self::LambdaTarget => &[
"error", "airmass", "airflow", "boost", "idle", "rpm", "cam", "angle", "position",
Expand Down Expand Up @@ -566,6 +569,10 @@ mod tests {
90
);
assert_eq!(name_score(ChannelRole::Lambda, "Target Lambda", None), 0);
assert_eq!(
name_score(ChannelRole::Lambda, "Lambda Sensor Voltage", None),
0
);
assert!(name_score(ChannelRole::PulseWidth, "Injector 1 On Time", None) >= 40);
assert_eq!(
name_score(ChannelRole::PulseWidth, "Injection Stage 1 Dead Time", None),
Expand Down
Loading
Loading