A Python library for reading and manipulating Universal Tracking Data Format (UTDF) satellite tracking data.
A direct conversion of https://github.com/trwyant/perl-Astro-UTDF. Thanks go to https://github.com/trwyant for creating the Perl module.
UTDF records are 75-byte big-endian binary structures. The library uses explicit big-endian unpacking (struct format >), so it produces identical results on any architecture (arm64, amd64, etc.).
- Task (task runner)
- Docker (or Podman)
All development tasks run inside a Docker container via Taskfile.yaml. To use Podman instead of Docker:
task tests CONTAINER_BIN=podmantask tests # Run lint, format check, typecheck, unit tests, then example
task unit_tests # Run unit tests (pytest inside Docker)
task tests:cov # Run tests with coverage report
task lint # Run ruff linter
task lint:fix # Run ruff linter with auto-fix (modifies host files)
task format # Run ruff formatter (modifies host files)
task format:check # Check formatting without changes
task typecheck # Run mypy type checking
task check # Run all checks (lint, format, typecheck, tests)
task build # Build distribution packages
task publish # Build and publish package to PyPI
task utdfpy:install # Install utdfpy from PyPI into local venv
task utdfpy:install:from:local # Install utdfpy from local source (editable)
task example_utdf # Run read_utdf.py example against test data
task utdf-cli # Run interactive UTDF query tool
task ci:test:locally # Run both GitHub Actions and GitLab CI locally
task github:ci:test:locally # Run GitHub Actions locally with act
task gitlab:ci:test:locally # Run GitLab CI locally with gitlab-ci-local
task rebuild # Clean everything and run full test suite from scratch
task clean # Remove build artifacts and caches
task clean:all # Remove everything including Docker image
tests depends on lint, format:check, typecheck, unit_tests, and example_utdf, each of which depends on docker:build. Running task tests will automatically build the Docker image, run the linter, check formatting, run mypy, execute the unit test suite, and run the example script. publish depends on tests, so it runs the full check suite before uploading to PyPI.
pip install utdfpyOr for development:
pip install -e '.[dev]'from utdfpy import UTDFRecord
# Read all records from a UTDF file
records = UTDFRecord.slurp("tracking_data.utd")
for record in records:
print(record.sic, record.vid, record.decode("measurement_time"))
print(f" azimuth: {record.azimuth}")
print(f" elevation: {record.elevation}")
print(f" range: {record.range}")$ .venv/bin/python examples/read_utdf.py tests/data/data.utd
Read 2 records from tests/data/data.utd
Record 0:
SIC: 86
VID: 99
Time: 2010-03-19T01:01:30+00:00
Azimuth: 5.839702317578685
Azimuth (deg): 334.5397498011498
Elevation: 0.9403839735099665
Elevation (deg): 53.877694653498
Range (km): 425.12365727401914
Record 1:
SIC: 86
VID: 99
Time: 2010-03-19T01:01:31+00:00
Azimuth: 5.852690026751761
Azimuth (deg): 335.28364953218365
Elevation: 0.9534989112054779
Elevation (deg): 54.62862629867479
Range (km): 421.4236702842119Use the utdf-cli task to read any UTDF file by passing the path via CLI_ARGS:
task utdf-cli -- /path/to/your/file.utdOr run the example script directly:
.venv/bin/python examples/read_utdf.py /path/to/your/file.utdBoth require a local venv. Run task utdfpy:install:from:local first if you haven't already.
An interactive query tool is included:
utdf-cli tracking_data.utdCommands: load, count, select, next, list, or type any field name to inspect it.
GitHub Actions via act. On macOS, Docker Desktop fails due to its containerd image store, so use Podman instead:
task github:ci:test:locally CONTAINER_BIN=podmanGitLab CI via gitlab-ci-local:
task gitlab:ci:test:locally- GitHub Actions (.github/workflows/ci.yaml): runs lint and typecheck in parallel, then unit tests on push/PR; auto-creates a release and publishes to PyPI when the version in
pyproject.tomlchanges. - GitLab CI (.gitlab-ci.yaml): runs lint and typecheck in parallel, then unit tests; auto-creates a release and publishes to PyPI when the version in
pyproject.tomlchanges.
Artistic-2.0 OR GPL-1.0-or-later (same as the original Perl module).
classDiagram
class UTDFRecord {
-bytes _front
-str _router
-int _year
-int _sic
-int _vid
-int _seconds_of_year
-int _microseconds_of_year
-float _transponder_latency
-bool _enforce_validity
-UTDFRecord _prior_record
-float _factor_K
-float _factor_M
+azimuth() float
+elevation() float
+range_delay() float
+doppler_count() float
+doppler_shift() float
+range() float
+range_rate() float
+measurement_time() float
+transmit_frequency() int
+raw_record() bytes
+hex_record() str
+clone() UTDFRecord
+slurp(file) list~UTDFRecord~
+iter_file(file) Iterator~UTDFRecord~
+decode(field_name) str
}
class UTDFError {
<<exception>>
}
class InvalidRecordError {
<<exception>>
}
class FrequencyBand {
<<IntEnum>>
UNSPECIFIED
VHF
UHF
S_BAND
C_BAND
X_BAND
KU_BAND
VISIBLE
}
class TransmissionType {
<<IntEnum>>
TEST
SIMULATED
RESUBMIT
REAL_TIME
PLAYBACK
}
class TrackingMode {
<<IntEnum>>
AUTOTRACK
PROGRAM_TRACK
MANUAL
SLAVED
}
class TrackerType {
<<IntEnum>>
C_BAND_PULSE
SRE
SGLS
TDRSS
}
class AntennaGeometry {
<<IntEnum>>
AZ_EL
XY_SOUTH
XY_EAST
RA_DEC
HR_DEC
}
Exception <|-- UTDFError
UTDFError <|-- InvalidRecordError
UTDFRecord --> UTDFRecord : prior_record
UTDFRecord ..> InvalidRecordError : raises
UTDFRecord ..> parsing : uses
UTDFRecord ..> decode : uses