Rebuilding trajectories

The Rebuild class provides functionality for reconstructing aircraft trajectories from raw ADS-B and Mode S messages.

When to use the rebuild functionality

There are two main use cases for the Rebuild class:

  1. Merge different data sources to rebuild a state vector table: Combine position, velocity, identification, and rollcall data that are stored in separate database tables into a single trajectory dataframe.

  2. Redecode raw messages: Apply custom decoding logic to raw messages for more precise position calculations, BDS register extraction, or custom validation.

Rebuilding without custom decoding

If you only need to merge data from different sources and trust the database-decoded values, use the rebuild() method without specifying a decoder:

from pyopensky.trino import Trino
from pyopensky.rebuild import Rebuild

trino = Trino()
rebuild = Rebuild(trino)

# Fetch and merge position, velocity, identification data
df = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E"
)

This returns a dataframe with columns from all three sources merged on icao24 and mintime with a 5-second tolerance.

Rebuilding with custom decoding

When you need more control over position decoding or want to extract additional data from raw messages, specify a decoder:

from pyopensky.trino import Trino
from pyopensky.rebuild import Rebuild

trino = Trino()
rebuild = Rebuild(trino)

# Redecode position data with PyModesDecoder
df = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder="pymodes"  # Use string-based decoder selection
)

The decoder will process the raw messages and apply custom logic for CPR position decoding, validation, and filtering.

Available decoders

PyModes decoder

The PyModesDecoder uses the pyModeS library for decoding.

Features:

  • CPR position decoding with odd/even frame pairing

  • Reference position validation to filter outliers

  • BDS register decoding (BDS 4,0, BDS 5,0, BDS 6,0)

  • Pure Python implementation

Installation:

pip install 'pyopensky[pymodes]'

Usage:

from pyopensky.decoders import PyModesDecoder
from pyopensky.rebuild import Rebuild
from pyopensky.trino import Trino

trino = Trino()
rebuild = Rebuild(trino)

# Using string-based selection
df = rebuild.redecode_position(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder="pymodes"
)

# Or instantiate the decoder directly
decoder = PyModesDecoder()
df = rebuild.redecode_position(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder=decoder
)

Rs1090 decoder

The Rs1090Decoder uses the Rust-based rs1090 library for faster decoding.

Features:

  • High-performance Rust implementation

  • Same decoding logic as PyModesDecoder

  • Significantly faster for large datasets

Installation:

pip install 'pyopensky[rs1090]'

Usage:

from pyopensky.rebuild import Rebuild
from pyopensky.trino import Trino

trino = Trino()
rebuild = Rebuild(trino)

# Using string-based selection
df = rebuild.redecode_position(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder="rs1090"
)

Custom decoder

You can implement your own decoder by subclassing Decoder:

from pyopensky.decoders import Decoder
import pandas as pd

class MyCustomDecoder(Decoder):
    def decode_position(self, df: pd.DataFrame) -> pd.DataFrame:
        # Custom position decoding logic
        return df

    def decode_velocity(self, df: pd.DataFrame) -> pd.DataFrame:
        # Custom velocity decoding logic
        return df

    def decode_identification(self, df: pd.DataFrame) -> pd.DataFrame:
        # Custom identification decoding logic
        return df

    def decode_rollcall(self, df: pd.DataFrame) -> pd.DataFrame:
        # Custom rollcall decoding logic
        return df

# Use your custom decoder
decoder = MyCustomDecoder()
df = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder=decoder
)

Performance considerations

When to use PyModesDecoder vs Rs1090Decoder:

  • Use PyModesDecoder for small to medium datasets (< 1M messages) or when you need to inspect the decoding logic

  • Use Rs1090Decoder for large datasets (> 1M messages) where performance is critical

  • Both decoders produce equivalent results, Rs1090Decoder is simply faster

Database vs custom decoding:

  • Database-decoded positions are already validated and filtered by OpenSky

  • Custom decoding gives you full control over CPR frame pairing and outlier filtering

  • Use custom decoding when you need BDS register data or have specific validation requirements

Merge tolerance:

All merges use a 5-second tolerance by default. This means:

  • Position and velocity data must be within 5 seconds to be merged

  • Identification data must be within 5 seconds of position data

  • Rollcall data must be within 5 seconds of position data

This tolerance balances data completeness with temporal accuracy.

class pyopensky.rebuild.Rebuild(trino)

Bases: object

Class for rebuilding state vectors from raw ADS-B messages.

This class provides methods to download and decode raw ADS-B messages from different data tables and merge them into complete state vectors.

Example usage:

from pyopensky.trino import Trino
from pyopensky.rebuild import Rebuild

trino = Trino()
rebuild = Rebuild(trino)

# Without decoding
data = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E"
)

# With pymodes or rs1090 decoding
data = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder="pymodes"  # or "rs1090"
)
rebuild(start, stop, *, icao24=None, bounds=None, cached=True, compress=False, decoder=None, include_rollcall=False, **kwargs)

Rebuild state vectors by downloading and merging all data tables.

This method downloads data from position, velocity, identification, and optionally rollcall tables, then merges them using time-based joins to reconstruct complete state vectors.

Parameters:
  • start (Union[str, Real, datetime, Timestamp]) – start of time range

  • stop (Union[str, Real, datetime, Timestamp]) – end of time range

  • icao24 (None | str | list[str]) – aircraft transponder code(s)

  • bounds (None | HasBounds | tuple[float, float, float, float]) – geographical footprint

  • cached (bool) – use cached data

  • compress (bool) – compress cache files

  • decoder (Union[None, Decoder, Literal['pymodes', 'rs1090']]) – decoder for decoding raw messages (e.g. PyModesDecoder)

  • include_rollcall (bool) – include rollcall (squawk) data in merge

Return type:

None | DataFrame

Returns:

DataFrame with merged state vectors

Example usage:

# Without decoding (use database decoded values)
data = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E"
)

# With pymodes or rs1090 decoding
data = rebuild.rebuild(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder="pymodes"  # or "rs1090"
)
redecode_identification(start, stop, *, icao24=None, cached=True, compress=False, decoder=None, **kwargs)

Download and optionally redecode identification (callsign) data.

Parameters:

decoder (None | Decoder) – decoder implementing decode_identification(df) method

Return type:

None | DataFrame

redecode_position(start, stop, *, icao24=None, bounds=None, cached=True, compress=False, decoder=None, **kwargs)

Download and optionally redecode position data from raw messages.

Downloads position data with raw ADS-B messages, pairs odd/even CPR frames, and optionally decodes them using a visitor pattern.

Parameters:
  • start (Union[str, Real, datetime, Timestamp]) – start of time range

  • stop (Union[str, Real, datetime, Timestamp]) – end of time range

  • icao24 (None | str | list[str]) – aircraft transponder code(s)

  • bounds (None | HasBounds | tuple[float, float, float, float]) – geographical footprint

  • cached (bool) – use cached data

  • compress (bool) – compress cache files

  • decoder (None | Decoder) – decoder object implementing decode_position(df) method that decodes raw messages. If None, returns raw data.

Return type:

None | DataFrame

Returns:

DataFrame with position data (decoded if visitor provided)

Example usage:

# Create a pymodes decoder
from pyopensky.decoders import PyModesDecoder
decoder = PyModesDecoder()

# Redecode positions
pos = rebuild.redecode_position(
    start="2023-01-03 16:00",
    stop="2023-01-03 20:00",
    icao24="400A0E",
    decoder=decoder
)
redecode_rollcall(start, stop, *, icao24=None, cached=True, compress=False, decoder=None, **kwargs)

Download and optionally redecode rollcall (squawk) data.

Parameters:

decoder (None | Decoder) – decoder implementing decode_rollcall(df) method

Return type:

None | DataFrame

redecode_velocity(start, stop, *, icao24=None, cached=True, compress=False, decoder=None, **kwargs)

Download and optionally redecode velocity data from raw messages.

Parameters:

decoder (None | Decoder) – decoder implementing decode_velocity(df) method

Return type:

None | DataFrame