Skip to content

Reinstate support for last_letter simulator - #33945

Merged
tridge merged 3 commits into
ArduPilot:masterfrom
Georacer:pr/last_letter
Aug 8, 2026
Merged

Reinstate support for last_letter simulator#33945
tridge merged 3 commits into
ArduPilot:masterfrom
Georacer:pr/last_letter

Conversation

@Georacer

@Georacer Georacer commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Reinstate support for last_letter simulator. It had bitrotten since 2013.

Classification & Testing (check all that apply and add your own)

  • Checked by a human programmer
  • Non-functional change
  • No-binary change
  • Infrastructure change (e.g. unit tests, helper scripts)
  • Automated test(s) verify changes (e.g. unit test, autotest)
  • Tested manually, description below (e.g. SITL)
  • Tested on hardware
  • Logs attached
  • Logs available on request

Description

last_letter is alive again! It is still a C++ simulator over the JSON bridge.
The project is completely revamped, so the SITL backend needed upgrading as well.

Features

  • Flies plane! Multicopters and VTOLs soon.
  • Runs in lock-step with SITL.
  • Has configurable aircraft definitions.
  • Optionally stores simulation logs.

Testing instructions

  1. Clone https://github.com/Georacer/last_letter
  2. Install the dependencies outlied in README.md:
sudo apt install build-essential git libeigen3-dev libboost-dev liblz4-dev libzstd-dev
  1. Build and install the project and the ArduPilot simulator binary:
cmake -S . -B build -DLAST_LETTER_BUILD_ARDUPILOT=ON && cmake --build build && cmake --install build

The binary will be installed at ~/.local/bin/last_letter_ardupilot. Ensure it's in your PATH.

  1. Your aircraft models are installed in ~/last_letter_models/. You will be running the ardupilot_plane model.

  2. Run AP SITL with:

sim_vehicle.py -v ArduPlane --model last_letter --console --map

Fly around as usual.

  1. (Optional) Run the simulation with logging enabled:
sim_vehicle.py -v ArduPlane --model last_letter --console --map -A "--config log"

Upon exiting the simulation you will have a .mcap log file in your working directory, containing timeseries from the simulator.
You can open it with PlotJuggler.
Screenshot from 2026-08-05 01-26-01

Warning: Project documentation under /docs is still stale, don't refer to it. But the above should be enough to get you running with ArduPilot.


Not relevant to ArduPilot:

  • last_letter provides a C++ library with all the included physics models
  • It also has Python bindings for the C++ libraries, which expose the same functionality over to Python. You can get the Python package directly from pypi with:
pip install last_letter

Important: The installation process involves building the C++ codebase, so you will need to have the project dependencies installed.

uv is the recommended Python management tool to use:

uv pip install last_letter

@tridge

tridge commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting.

Reviewed at head bb883994f4.

Verdict: REQUEST CHANGES

  • BUGSIM_last_letter.cpp:43,161 — the child is tracked only in a file-static pid with an atexit() reaper. That does not survive a normal SITL reboot, and stop_last_letter() sends SIGTERM without a waitpid(), so the child is left as a zombie.

  • ISSUESIM_last_letter.cpp:136 — the parent returns immediately after fork() without checking that execvp(), model loading or the UDP bind succeeded. A missing binary, bad model name or busy port leaves the child dead while ArduPilot waits indefinitely in the JSON receive path with no diagnostic.

  • NOTE — Typo in the constructor comment: “We devidate from the JSON model convention” should be “deviate”.

Also checked, no action needed
  • Checked, no action neededWithdrawn: the cold review flagged the members left unset by inheriting JSON as indeterminate. ArduPilot's global zeroing operator new covers them — the model is created through NEW_NOTHROW in create().

Full report: https://uav.tridgell.net/DevCallReviews/2026_08_05/devcall_pr_reviews.html

@tridge

tridge commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@Georacer I'm happy once reboot works

Also introduced optional sim launch step in SITL
By default this is a noop.
last_letter is the first non-stock SITL backend which manages the
simulator launch, so it implements the method.
By default this is a noop.
last_letter is the first non-stock SITL backend which manages the
simulator launch, so it implements the method.
@Georacer

Georacer commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@tridge I have just tested a reboot via MAVProxy (after landing someplace other than HOME).

What happens is that SITL reboots, a new instance of last_letter_ardupilot is forked, but because the UDP port is occupied by the previous one, it will fail and close itself. Meanwhile the previous one will detect the time reset and will re-initialize the simulation.

This makes it so that the simulated world (last_letter) will keep a continuous history and so will the .mcap log (albeit with a state discontinuity).

But to be honest I'd prefer having a one-to-one correspondence between AP boots and .mcap logs, so I think I'll have the first sim killed and the second take over.

Tested and a reboot kills the old instance and creates a new one, with a new .mcap log with it.
Mind you my knowledge on OS internals is quite limited, so Claude did most of the lifting here.

@tridge
tridge merged commit 3d7e592 into ArduPilot:master Aug 8, 2026
100 checks passed
@Georacer
Georacer deleted the pr/last_letter branch August 10, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants