Source code for pride_ppp.specifications.output

"""
PRIDE-PPP kinematic position model.

Provides the ``PridePPP`` Pydantic model for individual kinematic records
parsed from pdp3 ``.kin`` output files.
"""

import logging
from datetime import datetime
from typing import Union

import julian
from pydantic import BaseModel, Field, ValidationError, model_validator

logger = logging.getLogger(__name__)

#: Column-index → field-name mapping for PRIDE-PPPAR ``.kin`` output lines.
#: The pdp3 binary writes fixed-width records; this dict maps positional
#: token indices (after whitespace-split) to the corresponding field names
#: used by ``PridePPP``.
PRIDE_PPP_LOG_INDEX = {
    0: "modified_julian_date",
    1: "second_of_day",
    2: "east",
    3: "north",
    4: "up",
    5: "latitude",
    6: "longitude",
    7: "height",
    8: "number_of_satellites",
    9: "pdop",
}


[docs] class PridePPP(BaseModel): """Single-epoch kinematic position record from a pdp3 ``.kin`` file. Each instance represents one line of output. The ``east``, ``north``, and ``up`` fields store ECEF X/Y/Z coordinates in metres (despite the field names, which mirror the column order in the pdp3 output file). Geodetic lat/lon/height are also provided. Attributes ---------- modified_julian_date : float Modified Julian Date of the epoch (≥ 0). second_of_day : float Seconds elapsed since midnight UTC (0–86 400). east : float ECEF X-coordinate in metres (field name mirrors pdp3 column order). north : float ECEF Y-coordinate in metres (field name mirrors pdp3 column order). up : float ECEF Z-coordinate in metres (field name mirrors pdp3 column order). latitude : float Geodetic latitude in decimal degrees (−90–90). longitude : float Geodetic longitude in decimal degrees (0–360, east-positive). height : float WGS-84 ellipsoidal height in metres. number_of_satellites : int Number of satellites used in the solution. pdop : float Position Dilution of Precision. time : datetime, optional UTC timestamp derived from ``modified_julian_date`` and ``second_of_day`` (populated automatically by a validator). Docs: https://github.com/PrideLab/PRIDE-PPPAR """ modified_julian_date: float = Field(ge=0) second_of_day: float = Field(ge=0, le=86400) east: float = Field(ge=-6378100, le=6378100) north: float = Field(ge=-6378100, le=6378100) up: float = Field(ge=-6378100, le=6378100) latitude: float = Field(ge=-90, le=90) longitude: float = Field(ge=0, le=360) height: float = Field(ge=-1000, le=10000) number_of_satellites: int = Field(default=1, ge=0, le=125) pdop: float = Field(default=0, ge=0, le=1000) time: datetime | None = None
[docs] class Config: coerce = True
[docs] @model_validator(mode="before") def validate_time(cls, values): """Coerce ``pdop`` to float before field-level validation. Some ``.kin`` files encode PDOP as an integer string rather than a float literal. This pre-validator normalises the value so the ``ge``/``le`` constraints on the ``pdop`` field always pass. """ values["pdop"] = float(values.get("pdop", 0.0)) return values
[docs] @model_validator(mode="after") def populate_time(cls, values): """Derive UTC ``time`` from MJD and second-of-day. Converts ``modified_julian_date`` + ``second_of_day`` to a full Julian Date, then to a Python ``datetime`` via the ``julian`` library. """ julian_date = values.modified_julian_date + (values.second_of_day / 86400) + 2400000.5 t = julian.from_jd(julian_date, fmt="jd") values.time = t return values
[docs] @classmethod def from_kin_file(cls, data: list[str]) -> Union["PridePPP", ValidationError]: """Parse a single line (as split tokens) from a ``.kin`` file. Parameters ---------- data : List[str] A list of strings representing a line from the kin file. Returns ------- Union["PridePPP", ValidationError] A PridePPP object or a validation error. """ try: data_dict = {} if "*" in data: data.remove("*") if len(data) < 10: data.insert(-1, 1) # account for missing number of satellites for i, item in enumerate(data): field = PRIDE_PPP_LOG_INDEX[i] data_dict[field] = item return cls(**data_dict) except ValidationError as e: raise Exception(f"Error parsing PridePPP kin file {e}")