v0.4 — half the memory on big logs, plus --out-dir, --format and --dpi

Flight logs you can trust,
in one line of Python.

MAVPose reads ArduPilot and PX4 logs into pandas DataFrames: every message type on one time axis, values in real units, and "value not provided" markers removed. Optional add-ons let AI agents read your logs and turn plain-English questions into plots. Like the Log Pose — it locks onto your data and charts the course.

Python 3.10+ MIT License CI Status PyPI version
python — a real 40-minute ArduPilot flight
>>> import mavpose >>> log = mavpose.load("flight.tlog") >>> log <FlightLog 'flight.tlog': mavlink, 38 message types, 2443.8 s> >>> alt = log.series("GLOBAL_POSITION_INT", "relative_alt") >>> alt.attrs {'unit': 'm', 'msg_type': 'GLOBAL_POSITION_INT'} # converted from mm >>> alt.head(3) time_s 8.345965 1.93 8.593081 1.92 8.999325 1.91 >>> log.unknown_counts {'GPS_RAW_INT': {'epv': 6338}} # sentinels → NaN, not fake spikes >>> log.stats {'messages': 163851, 'bad_data': 0, 'errors': 0, 'unknown_values': 6338} >>> log.to_parquet("telemetry.parquet") # one tidy file for other tools

What MAVPose does for you

Raw flight logs mix clocks, store scaled integers and hide "no reading" sentinels among real values. MAVPose fixes all of that, the same way for every format.

⏱️

One clock

Receive time, TimeUS since boot, epoch microseconds, ULog timestamps: every message type lands on one shared time_s axis, so series from different messages line up.

📏

Real units

Millimetres, degE7 and millivolts become metres, degrees and volts, using the autopilot's own unit definitions. Every column reports its unit.

🚫

No fake readings

Sentinels such as current_battery = -1 or eph = 65535 become NaN. The markers come from the official MAVLink (441 fields) and PX4 (31 fields) definitions, not guesses.

🩹

Damaged logs still load

Power loss and bad transfers happen. Corrupt packets are skipped and counted in log.stats instead of ending the parse.

🪶

A light core

pip install mavpose pulls in 10 packages and no AI libraries. The assistant and the MCP server are opt-in extras.

📜

A stable API

A small, documented public API with a written stability policy, so other tools can build on it.

ArduPilot and PX4, one API

mavpose.load() detects the format. The same methods, units and guarantees apply to each.

FormatExtensionAutopilot
MAVLink telemetry log.tlogArduPilot, PX4, any MAVLink vehicle
DataFlash binary / text log.bin, .logArduPilot
ULog.ulgPX4

For AI agents and plain-English plots

Both add-ons sit on top of the same data layer, so they inherit its guarantees. Neither ever sends a raw log to a model.

mavpose[mcp] — mavpose-mcp

MCP server for AI agents

🔌
Works with any MCP clientAssistants and agent frameworks that speak the Model Context Protocol launch it over stdio.
📁
Folder allowlistReads and writes only inside the --root folders. Symlinked logs and .. escapes are refused.
🧮
Built for a context windowSeries are downsampled to at most 5,000 points, but min, max and mean cover every sample, so a brief spike is never missed.
🔒
No code executionEvery tool calls the library directly. Six of the seven tools are marked read-only.

mavpose[chat] — mavpose

Plain-English plot assistant

🔌
Any AI providerOpenAI, Anthropic, Gemini, OpenRouter, Groq, DeepSeek, Mistral, Together, or local Ollama / LM Studio.
📋
Model sees a schema, not the logMessage types, columns, units and ranges. It picks the data it needs.
🧾
JSON plot specs, no generated codeThe model describes the plot; MAVPose checks it against the log and draws it, with units on every axis.
🔁
ReproducibleEvery plot is saved as plot.json; --spec redraws it on any log, no model or key needed.

What an agent can ask for

list_logs
Log files in the allowed folders, with format and size
summarize_flight
Duration; altitude, speed, battery and GPS ranges with source field and unit; parse stats; warnings
list_messages
Message types with row counts, field counts and time range
describe_message
One message type's fields: dtype, unit, min, max, unknown count
get_series
Fields over a time window on the shared time_s axis, with units
get_log_messages
The vehicle's text log (STATUSTEXT, MSG, PX4 log messages), filterable by severity
export_parquet
Writes message types to a .parquet file in an allowed folder; never overwrites unless asked
helpful errors
"No message type 'GLOBAL_POS'. Did you mean: GLOBAL_POSITION_INT, …?"

Built on proven tools

The core depends only on the standard log readers and pandas. Everything AI-related lives in extras.

LayerLibrary / Service
Log readerspymavlink ≥ 2.4.48 · pyulog ≥ 1.1
Datapandas ≥ 2.0 + pyarrow ≥ 14.0
Units & markersGenerated from MAVLink XML and PX4 msg definitions
MCP servermcp ≥ 2.3 (stdio)
Plot assistantLangChain · any provider, incl. local models
Plottingmatplotlib ≥ 3.8
PlotsJSON specs drawn by MAVPose (code mode: restricted runner)
Lint / CIruff + pytest, Python 3.10–3.13

Up and running in minutes

Python 3.10 or newer. Pick only the parts you need.

# Data layer only: 10 packages, no AI dependencies
pip install mavpose

# Add the MCP server for AI agents
pip install "mavpose[mcp]"

# Add the plain-English plot assistant
pip install "mavpose[chat]"

Need a sample log? Download here →

Connect an MCP client — most use an entry like this; file and key names vary by client

{
  "mcpServers": {
    "mavpose": {
      "command": "mavpose-mcp",
      "args": ["--root", "/home/you/flight-logs"]
    }
  }
}

Plot assistant CLI — needs [chat] and a key for your AI provider (or a local model)

mavpose log_file [options]

  log_file            MAVLink .tlog, ArduPilot .bin/.log, or PX4 .ulg
  -p, --prompt TEXT   Plot request (omit for interactive mode)
  -m, --model P:M     provider:model, e.g. anthropic:<model>, ollama:llama3.1:8b
  --spec FILE         Redraw a saved plot.json; no model needed
  -o, --out-dir DIR    Save outputs to another folder
  -f, --format FMT    png (default), svg or pdf
  --dpi N             Resolution (default 150; 400 with --code)
  --code              Have the model write a Python script instead
  -r, --retries N     Attempts per request (default: 3)
  -v, --verbose       Enable debug logging

# Writes plot.png, plot.json and telemetry.parquet next to the log (or in --out-dir)

Bring any AI provider

Set your provider's key and choose a model with --model provider:model or MAVPOSE_MODEL. Also works with OpenRouter, Groq, DeepSeek, Mistral, Together, LM Studio and any OpenAI-compatible server. Local models keep everything on your machine.

Cloud

openai

OPENAI_API_KEY
openai:<model>

Cloud

anthropic

ANTHROPIC_API_KEY
anthropic:<model>

Cloud

gemini

GEMINI_API_KEY
gemini:<model>

Local

ollama

no key needed
ollama:llama3.1:8b

Start analysing your flight data today

One line to load a log. Real units, one clock, no fake readings.