Skip to main content
Glama
zazzn

emublog2mlv-mcp

by zazzn

emublog2mlv

Convert ECU Master EMU Black .emublog binary logs into a format MegaLogViewer HD can open — a MegaSquirt .msl (tab-delimited) or plain .csv.

CI Release License: MIT

The EMU Client stores logs in an undocumented gzip-compressed binary format that only its own software can open. This tool decodes that format directly, so you can analyse EMU logs in MegaLogViewer HD (overlay channels, histograms, VE/AFR analysis) instead of being locked into the EMU Client viewer.


Three ways to use it

1. Standalone .exe (no Python needed) — easiest

Download emublog2mlv.exe from the Releases page, then drag a log onto it, or from a terminal:

emublog2mlv.exe yourlog.emublog

This writes yourlog.msl next to it. Open that in MegaLogViewer HD.

2. Python package (CLI + library)

pip install git+https://github.com/zazzn/emublog2mlv
emublog2mlv yourlog.emublog              # -> yourlog.msl
emublog2mlv yourlog.emublog --csv        # -> yourlog.csv
emublog2mlv *.emublog                     # batch convert
emublog2mlv yourlog.emublog --summary     # print channel ranges as JSON (no file written)

As a library:

from emublog2mlv import convert, summarize
convert("yourlog.emublog", fmt="msl")     # returns stats dict
summarize("yourlog.emublog")              # per-channel min/max/mean

3. MCP server (let an AI use it as a tool)

emublog2mlv ships an MCP server so an AI assistant (Claude Desktop / Claude Code, etc.) can convert and inspect logs for you.

pip install "emublog2mlv[mcp] @ git+https://github.com/zazzn/emublog2mlv"
claude mcp add emublog2mlv -- emublog2mlv-mcp

Tools exposed:

Tool

What it does

convert_emublog(input_path, output_format="msl", output_path=None)

Convert a log; returns stats

emublog_summary(input_path)

Per-channel min/max/mean/first/last (no file written)

list_channels()

The decoded channel map (offsets/scales)

Then just ask: "Convert C:\logs\drive.emublog and tell me where lambda goes lean."


Related MCP server: Log Analyzer MCP

Output

A .msl file has a name row, a units row, then one row per 25 Hz sample, with a Time column in seconds. Time is anchored to the device's frameStamp clock, so it stays accurate even though the real sample rate (~25.18 Hz) jitters — a naïve index / 25 drifts ~0.7% over a few minutes.

Decoded channels

24 measured channels plus 2 derived (AFR, AFRTarget = λ × 14.7):

RPM, MAP, TPS, VE, IgnitionAngle, IgnFromTable, InjectorsPW, InjectorsDC, InjCalTime, ShortTermTrim, Lambda, LambdaTarget, IAT, ChargeTemp, CLT, OilTemp, OilPressure, FuelPressure, IATCorrection, Ethanol, AirFlow, CAM1valveDC, IdleTarget, FCustomCorrActive.

Channels that stay flat in a gentle cruise log (knock level, boost duty/target/correction, EGT, gear, vehicle speed, AFR2, BARO, engine power/torque, dwell, warmup) are not mapped yet — they need a dynamic drive to correlate. See Contributing.


How the format was reverse-engineered

ECU Master does not publish the .emublog layout. It was recovered by correlation:

  1. The file is gzip-compressed. Decompressed, it is a 12-byte header followed by fixed 308-byte records, one per sample (records = (size − 12) / 308).

  2. Export the same drive from EMU Client to CSV (it caps at 13 channels per export, so several exports are taken). These CSVs are row-aligned to the binary (sample N = record N).

  3. For each known CSV channel, Pearson-correlate its series against a u16 (or u8) value read at every byte offset; the offset with |r| ≈ 1.0 is the channel, and the linear fit gives the scale.

  4. Overlapping clusters (e.g. oil-pressure / oil-temp / fuel-pressure / coolant packed into bytes 111–115) were pinned by dumping raw bytes next to the CSV truth — temperatures turned out to be single-byte integers, pressures a dominant high byte.

Every decoded channel matches the EMU CSV export to the decimal. (The only discrepancies are ~0.8% of rows where EMU's own CSV export jitters ±1–2 frames against the binary — so this converter is actually more faithful than re-exporting CSV from the EMU Client.)

This was reverse-engineered for EMU Black V2 (firmware 2.169). EMU Black V3 writes a different .emublog3 layout, which is not supported.


Contributing: add a channel

The channel map lives in src/emublog2mlv/core.py as the CHANNELS table: (name, unit, offset, kind, scale, extra). To map a new channel, get a log that exercises it (e.g. a boost pull for boost/knock/EGT), export it to CSV from EMU Client, correlate each byte offset against the CSV column, verify the decode against the CSV, then add a row. PRs welcome.

Run the tests:

pip install -e ".[dev]"
pytest -q

License

MIT © 2026 Eric W

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zazzn/emublog2mlv'

If you have feedback or need assistance with the MCP directory API, please join our Discord server