Unofficial Python library and CLI for communicating with Alti-2 skydiving altimeters
Project description
pyaltitool
Unofficial Python library and CLI for communicating with Alti-2 skydiving altimeters over USB serial.
| Device | Status |
|---|---|
| Atlas | Expected to work |
| Atlas 2 | Tested, works |
| Atlas 2 Student | Expected to work |
| Juno | Expected to work |
| MA-12 | Expected to work |
| MA-15A | Expected to work |
Features
- Read device information (serial number, firmware version, jump count, etc.)
- Read jump logbook records with full field parsing
- Export logbook to CSV
- Read device date/time
- Read custom name tables (aircraft, drop zones, alarms)
- Read/write raw FRAM memory (Warning: write operations are untested and may corrupt device data. Use at your own risk!)
- Read/write device settings (Warning: writing settings is untested. May cause configuration errors or data loss. Proceed with caution!)
- Auto port detection, keepalive, and auto-reconnect
- Pure Python — only depends on pyserial
- Automatic logbook date parsing for firmware < 1.0.10 (handles overflow bug).
Usage
Getting Started
git clone https://github.com/5BytesHook/pyaltitool.git
cd pyaltitool
pip install pyserial
Connect your Alti-2 device via USB and run:
python altitool_cli.py
The tool auto-detects the serial port. To specify a port manually:
python altitool_cli.py -p /dev/cu.usbserial-XXXX # macOS
python altitool_cli.py -p /dev/ttyUSB0 # Linux
python altitool_cli.py -p COM3 # Windows
Common Commands
Once connected, you'll enter the interactive prompt. Type any command for its usage:
altitool> logbook all # Show all jumps
altitool> logbook last 10 # Show last 10 jumps
altitool> logbook csv all # Export all jumps to CSV
altitool> logbook csv last 20 jumps.csv # Export last 20 to a file
altitool> datetime # Read device clock
altitool> names aircraft # Read aircraft names
altitool> names dz # Read drop zone names
altitool> info # Show device info
altitool> help # Full command list
Quick CSV export without entering interactive mode:
python altitool_cli.py --csv # Export all jumps then exit
python altitool_cli.py --csv 50 # Export last 50 jumps then exit
Development
Install for Development
git clone https://github.com/5BytesHook/pyaltitool.git
cd pyaltitool
pip install -e .
Using as a Library
from pyaltitool import AltitoolDevice, auto_detect_port
from pyaltitool import parse_logbook_record, LOGBOOK_RECORD_SIZE
port = auto_detect_port() # works on macOS, Linux, Windows
with AltitoolDevice(port) as dev:
info = dev.connect()
print(f"{info['product_name']} S/N {info['serial_number']}, {info['total_jumps']} jumps")
dt = dev.read_datetime()
print(f"Device time: {dt:%Y-%m-%d %H:%M:%S}")
addr = info["summary_start"] + (info["total_jumps"] - 5) * LOGBOOK_RECORD_SIZE
data = dev.read_memory(addr, 5 * LOGBOOK_RECORD_SIZE)
for i in range(5):
rec = parse_logbook_record(data[i * 22 : (i + 1) * 22])
print(f" Jump #{rec['jump_number']}: {rec['exit_alt_ft']}ft exit, {rec['freefall_time']}s freefall")
You can also set the PYALTITOOL_PORT environment variable to override auto-detection, or pass the port path directly:
# macOS: AltitoolDevice("/dev/cu.usbserial-XXXX")
# Linux: AltitoolDevice("/dev/ttyUSB0")
# Windows: AltitoolDevice("COM3")
API Reference
AltitoolDevice
The main class for communicating with an Alti-2 device.
from pyaltitool import AltitoolDevice, auto_detect_port
| Function / Method | Description |
|---|---|
auto_detect_port() -> str | None |
Detect the serial port for an Alti-2 device (macOS / Linux / Windows). |
| Method | Description |
|---|---|
connect() -> dict |
Open port, wake device, perform handshake. Returns parsed Type 0 record. |
disconnect() |
End communication and close port. |
reconnect() -> dict |
Re-establish communication with full DTR wake. |
force_reconnect() |
Thread-safe reconnection (acquires lock internally). |
read_memory(address, length) -> bytes |
Read FRAM memory. Auto-reconnects on failure. |
write_memory(address, data) |
Write to FRAM. Splits large writes automatically. Untested. |
read_datetime() -> datetime |
Read device clock (A2 command). |
read_aircraft_names() -> dict[int, str] |
Read aircraft name table from FRAM. |
read_dz_names() -> dict[int, str] |
Read drop zone name table from FRAM. |
read_alarm_names() -> dict[int, str] |
Read alarm name table from FRAM. |
write_aircraft_name(index, name) |
Write an aircraft name. Untested. |
write_dz_name(index, name) |
Write a drop zone name. Untested. |
write_alarm_name(index, name) |
Write an alarm name. Untested. |
read_settings() -> dict |
Read device settings from FRAM. Untested. |
write_settings(settings, base=None) |
Write settings (read-modify-write). Untested. |
Properties: connected, type_zero, session_key.
Protocol helpers
from pyaltitool import (
parse_logbook_record, # Parse 22-byte jump record → dict
format_logbook_record, # Format parsed record as human-readable string
logbook_record_to_csv_row,# Format parsed record as CSV row
CSV_HEADER, # CSV header string
LOGBOOK_RECORD_SIZE, # 22
parse_type_zero, # Parse 32-byte Type 0 record → dict
parse_datetime_response, # Parse A2 response → datetime
parse_settings, # Parse 13-byte settings → dict
encode_settings, # Encode settings dict → 13 bytes
parse_fram_name, # Parse 10-byte FRAM name → str
encode_fram_name, # Encode str → 10-byte FRAM name
PRODUCT_NAMES, # {product_id: name} mapping
)
Exceptions
| Exception | Description |
|---|---|
AltitoolError |
Base exception for all communication errors. |
AltitoolAckError(AltitoolError) |
Device returned an unexpected acknowledgement code. |
How It Works
The Alti-2 devices communicate over USB serial (57600 baud, 8N1, RTS/CTS hardware flow control) using a custom encrypted protocol:
- DTR wake — toggle DTR to wake the device from sleep
- ASCII handshake — send
"018080"to receive the 32-byte Type 0 identification record - Session key — derive a 16-byte XTEA key from the Type 0 record + product-specific seed
- Encrypted commands — all subsequent commands are 32-byte XTEA-encrypted packets, sent byte-by-byte with CTS flow control polling
- Exit — send
\x01EXITto end the session
The device has a ~10-15 second idle timeout. pyaltitool runs a background keepalive thread that sends periodic datetime pings to prevent disconnection.
For bulk logbook reads, the library automatically reconnects between batches of 100 records, as the device's serial state becomes unreliable during sustained transfers.
See ALTI2_ATLAS_COMMUNICATION_PROTOCOL.md for the full protocol specification.
License
Disclaimer
This is an independent open-source project. It is not affiliated with, endorsed by, or supported by Alti-2 Technologies. Use at your own risk. The authors are not responsible for any damage to your altimeter. Always verify your equipment through official channels before skydiving.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyaltitool-0.1.0.tar.gz.
File metadata
- Download URL: pyaltitool-0.1.0.tar.gz
- Upload date:
- Size: 25.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91ce686b783d4307b81928bcb56c63019410eb1a14227fa0adeec8ac87c44919
|
|
| MD5 |
73f44d41279678969636483fd1ec9647
|
|
| BLAKE2b-256 |
ebece32f77b691b3cb94d97e0f09bd36011f1d6a30523fbfd7ab937a76bbfa30
|
Provenance
The following attestation bundles were made for pyaltitool-0.1.0.tar.gz:
Publisher:
publish.yml on 5BytesHook/pyaltitool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyaltitool-0.1.0.tar.gz -
Subject digest:
91ce686b783d4307b81928bcb56c63019410eb1a14227fa0adeec8ac87c44919 - Sigstore transparency entry: 1090440811
- Sigstore integration time:
-
Permalink:
5BytesHook/pyaltitool@7779c919275c0e81867fcaa74db01adbd9af5a52 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/5BytesHook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7779c919275c0e81867fcaa74db01adbd9af5a52 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyaltitool-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyaltitool-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70e7bc15b4910b8eeb2e5ee5da3479b7e9d535e9799fd3b260df1502a645fec7
|
|
| MD5 |
67b021858bc81f41c617596074e6382b
|
|
| BLAKE2b-256 |
8f83b44c7a17560d9c39f7115d92744d7af600b38f63a4dd4953453d91f72af8
|
Provenance
The following attestation bundles were made for pyaltitool-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on 5BytesHook/pyaltitool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyaltitool-0.1.0-py3-none-any.whl -
Subject digest:
70e7bc15b4910b8eeb2e5ee5da3479b7e9d535e9799fd3b260df1502a645fec7 - Sigstore transparency entry: 1090440813
- Sigstore integration time:
-
Permalink:
5BytesHook/pyaltitool@7779c919275c0e81867fcaa74db01adbd9af5a52 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/5BytesHook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7779c919275c0e81867fcaa74db01adbd9af5a52 -
Trigger Event:
release
-
Statement type: