Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

stanag4609

An MIT-licensed, pure-Python toolkit for live and recorded STANAG 4609 motion imagery and MISB KLV metadata.

The project is building the missing open foundation for applications that need to inspect FMV, visualize live geospatial metadata, preserve audio and unknown streams, add or modify KLV in transit, encode AI detections as ST 0903 VMTI, and send the same timed metadata to independent GIS or analytics consumers.

Status: pre-alpha. The implemented protocol slices are strict, typed, lossless, and tested, but this release does not yet claim complete STANAG 4609, ST 0601.19, ST 0902.8, ST 0903.6, or ST 1001.1 conformance. See conformance and known limitations.

Why this library

  • Pure Python and zero dependencies in the core package.
  • Incremental APIs for files, sockets, pipes, and arbitrarily chunked live data.
  • Standards-strict by default, with explicit lossless diagnostics for imperfect deployed streams.
  • Immutable raw wire data remains available; unknown fields are not discarded.
  • Video and every audio PES pass through metadata transforms without decoding or transcoding.
  • Bounded resource limits and explicit processor decisions are part of the API.
  • AI inference is a first-party integration boundary, not an application-only afterthought.

Implemented today

  • BER length and BER-OID codecs, streaming Universal KLV framing, lossless Local Sets, and ST 0601 running-sum checksums.
  • Exact ST 1201 IMAPA/IMAPB mapping, including special and reserved code words.
  • A growing audited ST 0601 codec with typed common fields, mandatory-structure validation, canonical encoding, and lossless field updates.
  • ST 1010.3 uncertainty matrices and ST 0601 Item 102, including Mode 2 IEEE/IMAP values, sparse correlations, and ordered Refined Source Lists.
  • ST 0806.4 independent and embedded RVT Local Sets, including CRC-32, POI, AOI, typed user data, and the ST 0601 Item 73 bridge.
  • ST 1202.3 generalized projective transformations with IEEE binary16/32/64 coefficients, forward/inverse coordinate mapping, and ST 1010 uncertainty.
  • ST 1206.1 SAR motion imagery metadata with typed collection/image geometry, radar parameters, radiometric polynomial evaluation, and the ST 0601 Item 95 bridge.
  • ST 1303.2 Multi-Dimensional Array Packs with Natural, IMAP, Boolean, BER-OID unsigned, and ordered run-length representations.
  • ST 1002.3 range imagery with standalone/embedded CRC handling, SPRMs, sectioned ST 1303 arrays, planar-fit parameters, and the ST 0601 Item 97 bridge.
  • ST 1601.2 geo-registration with typed tie-point arrays, UUID configuration identity, heterogeneous uncertainty mappings, and the ST 0601 Item 98 bridge.
  • ST 1602.2 composite imaging with source/AOI/sub-image geometry, transparency, Z-order, and the ST 0601 Item 99 bridge.
  • ST 0903.6 standalone/embedded VMTI, VTarget packs, frame-bounded pixel centroids and bounding boxes, lifecycle status, and packet-scoped Algorithm/Ontology/VObject/VFeature metadata for standards-native AI labels and confidence, typed ST 1204 MIIS identity, plus typed VChip image references/embeds, VMask polygon/run-length segmentations, and absolute Location/Boundary Series and embedded-parent offset geospatial geometry with VTracker UUIDs, timelines, track history, velocity, acceleration, and algorithm attribution, plus bounded cross-frame lifecycle/ID-reuse checks.
  • MPEG-2 TS framing; CRC-checked PAT/PMT; KLVA descriptor discovery; PES and PTS/DTS reconstruction; video, audio, metadata, and other stream events.
  • Strict ST 1402 PAT/PMT recurrence monitoring on exact PCR-derived, recorded, or live monotonic timelines, including multi-section table cycles.
  • ST 1402 UDP packet grouping and validation with the recommended seven-packet Ethernet-MTU default, strict integer packet boundaries, and bounded payloads.
  • ST 1001.1 audio profile validation for MPEG-1 Layer II, MPEG-2 Layer II, and MPEG-2 AAC-LC, with Layer II/ADTS header inspection and channel configuration.
  • Exact 27 MHz PCR/OPCR decoding and program-aware clock events. Unchanged media PES retain their source adaptation-field and PCR layout during remux; a per-program validator audits the ST 1402 100 ms PCR interval and rollover.
  • Synchronous and asynchronous KLVA mux/demux, including Metadata AU fragmentation and boundary-correct asynchronous KLV spanning across PES.
  • Bounded pass/drop/replace/inject KLV processors and a single-program live demux-process-remux transformer.
  • Dependency-free AI frame/detection contracts, nested sequential and bounded-parallel inference graphs, optional Ultralytics YOLO, ONNX Runtime, and NVIDIA Triton AsyncIO adapters, and an ontology-aware AI-box-to-VMTI bridge.
  • Bounded, per-program frame/KLV correlation with 33-bit PTS rollover, exact/latest/nearest policies, and Precision-Time-Stamp-to-frame UTC mapping.
  • A graph-result-to-VMTI emitter that preserves correlated ST 0601 wire fields or creates a minimal timed parent for media-only sources.
  • A synchronized reference overlay for VMTI boxes, centroids, polygon contours, compact bit masks, labels, confidence, lifecycle status, and geospatial state.
  • Bounded async decoded-frame queues with explicit backpressure, drop-oldest, drop-newest, and fail-fast overload behavior plus observable loss counters.
  • Per-PID streaming Layer II/AAC-LC compressed-frame reconstruction and an optional direct PyAV/FFmpeg decoder covering all three ST 1001 codecs.
  • Bounded ST 0601 Report-on-Change receiver state with 30-second expiry, immediate zero-length clearing, malformed-update isolation, and sparse-stream reconstruction in the reference player.
  • Atomic MPEG-TS creation from ordinary video plus ArcGIS FMV Multiplexer CSV, with lossless FFmpeg media remux, repeated KLVA signalling, and rollover-safe conversion from microsecond timestamps to synchronous 90 kHz PTS.
  • Streaming line-delimited GeoJSON fan-out with Report-on-Change reconstruction, sensor/frame-center/target points, full or offset-derived image footprints, and antimeridian-safe longitude handling.
  • An incremental FMV verifier and CLI with human/JSON reports covering transport structure and continuity, stream inventory, KLV carriage, ST 1402 declarations and PCR cadence, ST 0601 diagnostics, ST 0902 missing requirements, ST 0903 lifecycle inventory, ST 1001 compressed audio, and bounded per-service ST 0601 tag coverage.

Install

The package is not published to PyPI yet. The intended release command is python -m pip install stanag4609; until the first signed alpha release, use a local development install:

git clone git@github.com:trane293/stanag4609.git
cd stanag4609
pyenv install 3.10.13  # omit when already installed
pyenv virtualenv 3.10.13 stanag4609-dev
pyenv local stanag4609-dev
python -m pip install -e '.[dev]'

The committed .python-version selects the local environment. Packaging uses PEP 517/518 and PEP 621 metadata with Hatchling and produces a universal wheel.

Documentation

The documentation is a searchable, light/dark static site with task-oriented guides for KLV, live transforms, BYOAI/VMTI, web clients, the CLI, FFmpeg and GStreamer boundaries, audio, GIS/OSINT interoperability, and conformance.

python -m pip install -e '.[docs]'
mkdocs serve

Start at docs/index.md. The documentation roadmap records the tutorial and quality gates that must be met before the first stable release. The site is configured for versioned hosting on Read the Docs; PyPI will show this README and link to the full documentation rather than hosting the complete site itself. Maintainer setup and the release gates are in the release process.

Verify and debug an FMV file

stanag4609-verify mission.ts
stanag4609-verify mission.ts --format json > verification.json
stanag4609-verify legacy-mission.ts --profile structural

The report identifies what passed, what is missing, malformed fields, transport offsets, affected programs/PIDs, repeated issue counts, and VMTI target/lifecycle statistics. It diagnoses impossible ST 0903 state transitions and reuse of an identifier after Dropped, while treating an omitted optional detection status as a warning. Declared MP2/AAC audio is parsed through complete compressed frames so the report includes sample rate, channel count, samples, duration, PTS coverage, malformed headers, and trailing truncation. Per-service ST 0601 inventories show every observed known or extension tag, how often it appears, ZLIs, malformed values, versions, and timestamp span. An ST 0902 checklist marks every selected minimum-item group current, missing, or overdue. Reports can be emitted as terminal text, stable JSON, or a self-contained printable HTML file. Use the incremental FMVVerifier API for upload services and live capture pipelines. The verifier guide defines current coverage, policies, exit statuses, and known non-checked areas.

The verifier also keeps bounded state per program, metadata PID, and metadata service for Control Commands, Wavelengths, Payloads, Weapons Stores, and Waypoints. Cross-packet identifier/reference/order violations therefore appear in the same report as wire-format and transport failures.

Decode KLV incrementally

from stanag4609 import KLVStreamParser, ST0601_KEY, decode_uas_local_set

parser = KLVStreamParser(key_prefix=ST0601_KEY)
for network_chunk in source:
    for packet in parser.feed(network_chunk):
        uas = decode_uas_local_set(packet)
        print(uas.value(2), uas.value(13), uas.value(14))
parser.finish()

For sparse live metadata, reconstruct the receiver-visible state prescribed by ST 0601/ST 0107 Report-on-Change:

from stanag4609 import ReportOnChangeState

state = ReportOnChangeState()
for packet in klv_packets:
    snapshot = state.observe(packet)
    print(snapshot.value(13), snapshot.value(14), snapshot.expired_tags)

The default 30-second window is inclusive. A ZLI clears a single-use value immediately, invalid field updates leave the last valid value untouched, and the state is bounded by the finite ST 0601 item registry.

ST 0601 permits legacy and newer representations of the same logical value in one packet. Resolve the value applications should actually consume without discarding the original wire fields:

from stanag4609 import ST0601Semantic

height = snapshot.preferred_field(ST0601Semantic.SENSOR_HEIGHT)
if height is not None:
    print(height.tag, height.value, [field.definition.tag for field in height.ignored])

for field in snapshot.effective_fields:
    publish(field.definition.name, field.value)

The resolver implements the normative full-range, HAE-over-MSL, and extended-over-restricted priority chains. snapshot.fields and uas.fields remain lossless; effective_fields is the presentation/analytics view.

WavelengthTableState adds identity-aware merging for distributed custom Wavelengths List records and validates each Active Wavelength ID against the predefined table or a current custom definition. Similar standard-specific evaluators are used where treating a whole list as one scalar would be wrong. PayloadTableState does the same for distributed Payload List records and Active Payload bit references, while exposing completeness and missing IDs. WeaponsStoresState reconstructs Item 140 fragments by their four-part physical addresses, so status changes replace the right store while unrelated stores retain their own refresh lifetimes:

from stanag4609 import WeaponsStoresState

weapons = WeaponsStoresState()
for packet in klv_packets:
    snapshot = weapons.observe(packet)
    for address, store in snapshot.records.items():
        print(address, store.weapon_type, store.status.general_status)

WaypointListState merges Item 141 records by Waypoint ID and provides ordered current, planned, historical, and cancelled views. Its order_conflicts view intentionally exposes the temporary duplicate Prosecution Orders that the standard permits receivers to observe while a distributed reorder is underway.

ControlCommandState validates the Item 115/116 lifecycle without retaining an unbounded stream: new Command IDs increase and remain unique, repeats preserve their original text and effective issue time, acknowledgements reference known commands, and acknowledged commands cannot reappear.

from stanag4609 import ControlCommandState

commands = ControlCommandState()
for packet in klv_packets:
    snapshot = commands.observe(packet)
    for issue in snapshot.issues:
        alert(issue.code, issue.command_ids, issue.message)

For ST 1607 metadata substreams, MetadataTreeState reconstructs every Segment or Amend branch by its full MSID lineage and evaluates an effective metadata view without changing the first-generation root values:

from stanag4609 import MetadataTreeState

tree = MetadataTreeState()
for packet in klv_packets:
    snapshot = tree.observe(packet)
    for path in snapshot.branches:
        latitude = snapshot.effective_value(path, 13)
        longitude = snapshot.effective_value(path, 14)
        print(path, latitude, longitude)

Sparse values age independently at every level. Segment ZLIs reveal the parent value, Amend deletions remain effective for their refresh lifetime, and a child report refreshes only when its complete parent MSID lineage is present. snapshot.effective_security(path) overlays a branch's permitted ST 0102 object-country Items 12 and 13 onto inherited root markings. Use validate_st1607_security(snapshot) to report incomplete child country sets, unexpected child security items, or an attempted deletion of root security. validate_st1607_mismms(snapshot) applies the airborne MISP/ST 0902 minimum profile at the root for Amend trees and to every terminal effective union for Segment trees. For a non-hierarchical reconstructed view, use validate_mismms_current_state(snapshot.fields) directly. These static checks are intended after the receiver has had a reporting interval to warm up, or when finalizing a finite recording; MISMMSValidator remains the packet-level cadence validator.

Validate the ST 0902 minimum profile

from stanag4609 import MISMMSecurityContext, MISMMSValidator

validator = MISMMSValidator(
    security_context=MISMMSecurityContext(releasing_instructions=True),
)

for klv_packet in packets:
    for issue in validator.observe(klv_packet):
        print(issue.code, issue.requirement, issue.tags)

# Required for finite recordings: reports Table 1 items that never appeared.
for issue in validator.finish():
    print(issue.code, issue.requirement, issue.tags)

The validator tracks alternative field groups and the inclusive 30-second reporting window across packets. It emits structured diagnostics for malformed or zero-length values and models ST 0102 SCI/SHI, Caveats, and Releasing Instructions as explicit mission-context policy. It does not guess security markings.

Process an FMV transport stream

from time import monotonic

from stanag4609 import FieldDecodingMode, LiveTransportTransformer

transformer = LiveTransportTransformer(
    metadata_processors=(redact_sensitive_fields, add_vmti_detections),
    field_decoding=FieldDecodingMode.PRESERVE,
)

for chunk in live_transport_source:
    batch = transformer.feed(chunk, at=monotonic())
    output_transport.write(batch.transport)
    metadata_sidecar.write(batch.metadata)

# Keep PAT/PMT cadence alive even while the source is idle.
output_transport.write(transformer.poll_program_tables(at=monotonic()).transport)

final = transformer.finish()
output_transport.write(final.transport)

STRICT is the default and rejects malformed known fields. PRESERVE still enforces KLV structure, checksums, required tags, and singleton rules, but keeps an undecodable field's exact wire bytes and reports a FieldDecodingIssue so a player can remain useful without pretending the stream is conformant.

See the live pipeline guide for an in-transit VMTI example, scheduled program-table output, and exact remux constraints. Omitting at preserves the input stream's PAT/PMT repetition behavior; supplying one switches to the drift-free eight-Hz output scheduler.

When transformation changes packet positions, wrap output in TransportRateShaper. It assigns exact constant-rate 188-byte slots, inserts bounded PID 0x1FFF padding for idle slots, and optionally rewrites every retained PCR from a caller-anchored 27 MHz output clock at the H.222.0-defined PCR-base byte position. See transport-rate shaping.

Properly versioned live PMTs may add or remove streams and change a KLVA PID's synchronous/asynchronous carriage. Retained PID continuity is preserved; an affected KLVA change is rejected if it would discard a partial item or access unit.

For UDP delivery, group output without changing its TS bytes:

from stanag4609 import iter_udp_datagrams

for datagram in iter_udp_datagrams(remuxed_chunks):
    udp_socket.sendto(datagram, destination)

The default is seven 188-byte packets per payload, with a smaller integral final datagram. See UDP transport datagrams.

Validate live PAT/PMT acquisition cadence against ST 1402-02 independently of the clock source used by the host application:

from time import monotonic

from stanag4609 import PSICadenceValidator
from stanag4609.transport import PATEvent, PMTEvent

cadence = PSICadenceValidator()
cadence.start(at=monotonic())

for event in demuxer.feed(transport_chunk):
    now = monotonic()
    if isinstance(event, PATEvent):
        issues = cadence.observe_pat(event.table, at=now)
    elif isinstance(event, PMTEvent):
        issues = cadence.observe_pmt(event.table, at=now)
    else:
        issues = ()
    for issue in issues:
        print(issue.table, issue.program_number, issue.elapsed)

# Also call from an idle-loop timer to detect a table that never arrives.
for issue in cadence.check(at=monotonic()):
    print(issue.message)

Use exact ProgramClockReference.seconds fractions for deterministic recording audits. The 250 ms boundary is a failure because the standard requires more than four insertions per second; the exposed 125 ms interval is the standard's recommendation. Current multi-section tables count only after a complete cycle.

For generated streams, ProgramTableScheduler(muxer).poll(at=monotonic()) emits an immediate PAT/PMT pair and then maintains the recommended exact 125 ms schedule. Late polls expose skipped repetitions and mandatory-interval failure without creating a misleading burst of stale tables. See PAT/PMT cadence.

Audit the encoded PCR time base independently for every program:

from stanag4609 import PCRCadenceValidator, ProgramClockEvent

pcr_cadence = PCRCadenceValidator()
for event in demuxer.feed(transport_chunk):
    if isinstance(event, ProgramClockEvent):
        for issue in pcr_cadence.observe(event):
            print(issue.program_number, issue.elapsed, issue.message)

The exact 100 ms boundary passes; larger gaps and unannounced clock regressions are diagnostics. Rollover, declared discontinuities, shared clock PIDs, and PMT-driven PCR PID changes are handled explicitly. See PCR cadence.

For newly constructed streams, muxer.mux_pcr(ProgramClockReference(...)) emits an adaptation-only clock packet on the PMT-declared PCR PID without advancing payload continuity. The application supplies PCR values from its actual output schedule; the library does not pretend callback wall time is the transport packet's decoder-arrival time.

Live writers with an authoritative output clock can use ProgramClockScheduler(muxer): start() anchors an encoder PCR and poll() emits drift-free current clock packets at a 50 ms operational cadence. Late polls expose skipped slots and whether the actual gap exceeded ST 1402's inclusive 100 ms limit.

Audit each elementary stream's successive presentation timestamps as well:

from stanag4609 import PESStreamEvent, PTSCadenceValidator

pts_cadence = PTSCadenceValidator()
for event in demuxer.feed(transport_chunk):
    if isinstance(event, PESStreamEvent):
        for issue in pts_cadence.observe(event):
            print(issue.pid, issue.difference, issue.message)

The ST 1402 §7.3 limit is 0.7 seconds. The validator handles 33-bit rollover, presentation-order regressions, per-stream state, and declared discontinuities. See PTS cadence.

Validate the metadata declaration itself before accepting or publishing a program map:

from stanag4609 import KLVCarriage, validate_st1402_metadata_program

issues = validate_st1402_metadata_program(
    pmt,
    expected_carriage={0x120: KLVCarriage.SYNCHRONOUS},
)
for issue in issues:
    print(issue.requirement, issue.elementary_pid, issue.message)

Supplying the expected PID lets the report diagnose a missing or misplaced identifier that automatic discovery could not safely infer. The report checks same-program motion imagery, carriage stream type, KLVA identification, and the synchronous metadata/STD descriptor rules.

Create FMV from video and metadata CSV

The first-party CSV multiplexer turns ordinary video, including MPEG Program Streams, into an MPEG-2 Transport Stream with the original video/audio and a properly signalled synchronous KLVA PID:

stanag4609-mux-esri \
  "/path/to/Raw_Video.mpeg" \
  "/path/to/Raw_Metadata.csv" \
  "/path/to/output.ts"

FFmpeg remuxes the media without transcoding; the Python library then aligns the first CSV timestamp to the first video PTS, encodes every row as ST 0601, and injects it as ST 1402 synchronous metadata. Existing output files are protected unless --force is given. The same workflow is available through multiplex_esri_fmv() and the lower-level inject_esri_csv_metadata() API for an input that is already MPEG-TS.

The supplied 149-second MPEG-2/MP2 sample has been exercised end to end: all 866 CSV records decode back from the generated KLVA stream and feed the player timeline from 0.000 to 144.961 seconds while both media streams remain present.

The reverse path writes the same metadata as a separate ArcGIS/Esri-compatible sidecar. It reconstructs sparse Report-on-Change values independently for each program/PID and streams the transport through bounded buffers:

stanag4609-export-esri "/path/to/input.ts" "/path/to/metadata.csv"

Applications can use iter_esri_metadata_rows() for live fan-out or export_esri_metadata_csv() for an atomic file export. Special/unknown numeric sentinels become empty cells rather than fabricated coordinates.

For map services, spatial databases, and independent analytics consumers, the same timed stream can be exported as line-delimited GeoJSON:

stanag4609-export-geojson "/path/to/input.ts" "/path/to/metadata.geojsonl"

Each metadata packet becomes one streaming FeatureCollection containing the available sensor, frame-center, target, and image-footprint geometry. Use iter_geojson_feature_collections() to publish these records directly to a message bus or web backend while the FMV transport continues independently. See GeoJSON metadata streams.

Audio stays independent of video and metadata throughout the live pipeline:

from stanag4609 import StreamKind, TransportDemuxer, parse_aac_adts_header

demuxer = TransportDemuxer()
for event in demuxer.feed(transport_chunk):
    if getattr(event, "kind", None) is StreamKind.AUDIO:
        print(event.pid, event.audio_codec, event.pes.pts_seconds)
        if event.audio_codec and event.audio_codec.value == "mpeg-2-aac-lc":
            adts = parse_aac_adts_header(event.pes.payload)
            print(adts.sample_rate, adts.channel_count, adts.has_crc)

For PES payloads that split or combine compressed audio frames, keep one bounded, timestamp-aware parser per audio PID:

from stanag4609 import AudioPESFrameParser

parsers = {}
for event in audio_events:
    if event.audio_codec is None:
        continue
    parser = parsers.setdefault(event.pid, AudioPESFrameParser())
    for timed in parser.feed(event):
        frame = timed.frame
        print(frame.offset, timed.presentation_seconds, frame.channel_count)

This reconstructs MPEG-1/2 Layer II and MPEG-2 AAC-LC ADTS frames across arbitrary chunks, honors the H.222.0 first-access-unit PTS rule at split PES boundaries, unwraps 33-bit timestamps, and advances time with exact rational sample durations. See examples/audio_frames.py for a complete TS walk.

Decode completed frames to native FFmpeg-backed PyAV audio frames without a subprocess or probe delay:

from stanag4609 import PyAVAudioDecoder

decoder = PyAVAudioDecoder(codec)
for timed in frame_parser.feed(event):
    for audio_frame in decoder.decode(timed.frame):
        audio_sink.consume(audio_frame)

Install with pip install 'stanag4609[audio-pyav]'. Keep one codec context per audio PID and flush it at end of stream. See audio decoding.

Bring your own AI

from stanag4609.sidecar import (
    InferenceContext,
    InferenceStage,
    Parallel,
    Sequential,
)

graph = Sequential(
    Parallel(
        InferenceStage("local-yolo", local_detector, threaded=True),
        InferenceStage("remote-triton", triton_detector, timeout_seconds=0.150),
        max_concurrency=2,
    ),
    InferenceStage("fusion", fuse_detections),
    InferenceStage("tracker", track_objects),
)

result = await graph.run(InferenceContext(frame))

Turn a named detector or tracker result back into synchronized ST 0601 Item 74 without hand-assembling transport context:

from stanag4609.sidecar import VMTIMetadataEmitter

packet = VMTIMetadataEmitter("tracker", metadata_pid=0x120)(result)
transport_sink.write(transformer.emit_metadata(packet).transport)

If the frame carries correlated ST 0601, the emitter preserves its unrelated and unknown fields while refreshing the timestamp and VMTI. For a media-only input it creates a minimal parent on the explicitly declared KLVA PID.

Local synchronous models can run off the event loop; remote clients can be native async callables. Parallel outputs are deterministic, later stages can read earlier named results, and common AI bounding boxes convert directly to ST 0903 VMTI. Ultralytics YOLO, ONNX Runtime, NVIDIA Triton, VMTI injection, and nested graph examples are in AI sidecars.

The first packaged runtime adapter is optional, so the core stays dependency free:

pip install 'stanag4609[ai-ultralytics]'
pip install 'stanag4609[ai-onnx]'
pip install 'stanag4609[ai-triton-grpc]'
pip install 'stanag4609[audio-pyav]'

Public FMV test data

The project uses FFmpeg's public Day Flight.mpg and Night Flight IR.mpg real-UAS streams as opt-in full-file integration fixtures. The fetcher verifies the exact byte size and SHA-256 identity before the library demuxes every transport packet and diagnoses the embedded ST 0601 metadata.

python scripts/fetch_public_fixtures.py
pytest -m integration tests/integration/test_public_fmv.py

Large media is not committed. Fixture provenance, hashes, and expected results are recorded in references/fixtures.json; see public fixture details.

Reference player

With FFmpeg installed, launch the bundled local player against any MPEG-2 TS FMV file:

stanag4609-player "/path/to/Truck.ts"

For separate video and CSV inputs, run stanag4609-mux-esri first and pass its output to the player.

It serves a browser-compatible video and synchronizes the live side panel to the source KLV PTS. The panel exposes timestamps, platform/sensor state, sensor and frame-center coordinates, altitude, target coordinates, VMTI, and field diagnostics. A synchronized local map plots the sensor, frame center, target, and full or offset-derived image footprint without requiring an external tile service. Typed VMTI targets with pixel geometry are drawn as synchronized bounding boxes and centroid markers, including labels and confidence when the packet carries Algorithm/Ontology metadata. Sparse Report-on-Change packets inherit still-current values, so the display does not lose coordinates merely because a packet omits an unchanged item. See reference player details.

Architecture

TS bytes -> framing -> PAT/PMT -> PES demux -> timed video/audio/KLV events
                                                |             |
                                                v             v
                                        AI sidecar graph   KLV processors
                                                |             |
                                                +---- VMTI ---+
                                                              |
                                  +---------------------------+----------+
                                  v                           v          v
                              TS remux                   KLV/NDJSON    GIS/UI

The dependency-free core owns transport and metadata truth. Optional adapters will own compressed video/audio decoding, model runtimes, browser delivery, and third-party GIS formats. See the accepted live architecture decision.

Development and verification

Protocol work follows specification-led TDD: cite the edition and requirement, add a failing normative or adversarial test, implement the smallest coherent slice, run every quality gate, review the diff, and make a focused Conventional Commit.

ruff check .
mypy src
pytest --cov=stanag4609 --cov-branch --cov-report=term-missing
python -m build

See CONTRIBUTING.md for branch/PR conventions, required PR evidence, compatibility review, and rollback expectations. The standards manifest records the exact MISB and ITU editions used; normative documents are not redistributed in Git.

Roadmap

  • Full typed ST 0601.19 and ST 0903.6 coverage, deeper ST 0902.8 conformance, ST 1001.1 decoding adapters, security metadata, and edition-aware diagnostics.
  • Automatic bitrate/clock-policy selection inside the transformer, PAT program switching, multiple-program remuxing, and full System Target Decoder buffer modeling. Constant-rate packet scheduling, null filling, and retained-PCR restamping are available through the explicit TransportRateShaper output boundary.
  • Low-latency FFmpeg/PyAV frame decoding and live browser delivery.
  • Parallel KLV/NDJSON sinks, additional ArcGIS products, and configurable metadata/property projections.
  • VMTI mask/polygon overlays and optional tiled basemaps for the synchronized reference player.
  • Property, fuzz, corpus, interoperability, performance, and conformance tests.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stanag4609-0.1.0a1.tar.gz (431.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stanag4609-0.1.0a1-py3-none-any.whl (261.9 kB view details)

Uploaded Python 3

File details

Details for the file stanag4609-0.1.0a1.tar.gz.

File metadata

  • Download URL: stanag4609-0.1.0a1.tar.gz
  • Upload date:
  • Size: 431.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stanag4609-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 b2a4be00831c08aa6abc483ee0f07ee63244d38f7bc448a9e2d5bed6fc423f21
MD5 4d8eacd6ac6068ec8f57274ba6fecb68
BLAKE2b-256 6d9b698f1d6630452a1ca52eb8abb1aa5318e0c737bd5a428068bdd3c98a32ca

See more details on using hashes here.

File details

Details for the file stanag4609-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: stanag4609-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 261.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stanag4609-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 33d1b971d7df2679c28c3497f9a17565e25604a1dc903b27af89d59431cba858
MD5 a43fc745a1f65f24586525592770df99
BLAKE2b-256 08873e6a913ec32c706f5773253acc540715be50f5fbd2d6a51c9d26b0cf6979

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

This release

0.1.0a1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page