Navigator / src / navigator / utils / matcher / sp3_matcher.py
sp3_matcher.py
Raw
"""Matcher for IGS SP3 files."""

from .matcher import Matcher

__all__ = ["SP3Matcher", "LegacySP3Matcher"]


class SP3Matcher(Matcher):
    """Matcher for SP3 files.

    This class is designed to match the new name format for SP3 files since GPS Week 2238.

    The SP3 file format is described at:
    - https://cddis.nasa.gov/Data_and_Derived_Products/GNSS/orbit_products.html

    Attributes:
        sp3_pattern (str): Regular expression pattern for matching SP3 file names.
    """

    sp3_pattern = r"^([A-Z]{3})0([A-Z]{3})([A-Z]{3})_(\d{4})(\d{3})(\d{2})(\d{2})_([A-Z0-9]{3})_([A-Z0-9]{3})_([A-Z]{3})\.([A-Z0-9]{3})\.gz"

    def __init__(
        self,
    ) -> None:
        """Initialize the Matcher for SP3 files."""
        super().__init__(pattern=self.sp3_pattern)

    def extract_metadata(self, filename: str) -> dict:
        """Extract metadata from the filename.

        Args:
            filename (str): Filename to extract metadata from.

        Returns:
            dict: Extracted metadata from the filename.
        """
        match = self.pattern.match(filename)

        if match:
            return {
                "analysis_center": match.group(1),
                "campaign": match.group(2),
                "solution_type": match.group(3),
                "year": match.group(4),
                "day": match.group(5),
                "hour": match.group(6),
                "minute": match.group(7),
                "length": match.group(8),
                "sampling": match.group(9),
                "content": match.group(10),
                "format": match.group(11),
            }
        return {}

    def invert(
        self,
        year: int,
        day: int,
        hour: int = 00,
        minute: int = 00,
        analysis_center: str = "IGS",
        campaign: str = "OPS",
        solution_type: str = "FIN",
        length: str = "01D",
        sampling: str = "15M",
        content: str = "ORB",
        format: str = "SP3",
    ) -> str:
        """Invert the metadata back to a filename.

        Args:
            year (int): The year of the metadata.
            day (int): The day of the metadata.
            hour (int): The hour of the metadata.
            minute (int): The minute of the metadata.
            analysis_center (str, optional): The analysis center. Defaults to "IGS".
            campaign (str, optional): The campaign. Defaults to "OPS".
            solution_type (str, optional): The solution type. Defaults to "FIN".
            length (str, optional): The length of the metadata. Defaults to "01D".
            sampling (str, optional): The sampling rate of the metadata. Defaults to "15M".
            content (str, optional): The content of the metadata. Defaults to "ORB".
            format (str, optional): The format of the metadata. Defaults to "SP3".

        Returns:
            str: The inverted filename.
        """
        # Assert that the metadata is valid
        assert len(str(year)) == 4, "Year must be 4 digits long"
        assert day <= 366, "Day must be between 0 and 365"
        assert hour <= 24, "Hour must be between 0 and 23"
        assert minute <= 60, "Minute must be between 0 and 59"
        assert len(analysis_center) == 3, "Analysis center must be 3 characters long"
        assert len(campaign) == 3, "Campaign must be 3 characters long"
        assert len(solution_type) == 3, "Solution type must be 3 characters long"
        assert len(length) == 3, "Length must be 3 characters long"
        assert len(sampling) == 3, "Sampling must be 3 characters long"
        assert len(content) == 3, "Content must be 3 characters long"
        assert len(format) == 3, "Format must be 3 characters long"

        return f"{analysis_center}0{campaign}{solution_type}_{str(int(year)).zfill(4)}{str(int(day)).zfill(3)}{str(int(hour)).zfill(2)}{str(int(minute)).zfill(2)}_{length}_{sampling}_{content}.{format}.gz"


class LegacySP3Matcher(Matcher):
    """Matcher for legacy SP3 files.

    This class is designed to match the legacy name format for SP3 files before GPS Week 2237.

    The SP3 file format is described at:
    - https://cddis.nasa.gov/Data_and_Derived_Products/GNSS/orbit_products.html

    Attributes:
        sp3_pattern (str): Regular expression pattern for matching SP3 file names.
    """

    sp3_pattern = r"^([a-z]{3})(\d{4})([0-7]{1})\.([a-z0-9]{3})\.Z"

    def __init__(
        self,
    ) -> None:
        """Initialize the Matcher for SP3 files."""
        super().__init__(pattern=self.sp3_pattern)

    def extract_metadata(self, filename: str) -> dict | None:
        """Extract metadata from the filename.

        Args:
            filename (str): Filename to extract metadata from.

        Returns:
            dict: Extracted metadata from the filename.
        """
        match = self.pattern.match(filename)

        if match:
            return {
                "analysis_center": match.group(1),
                "gps_week": match.group(2),
                "day": match.group(3),
                "format": match.group(4),
            }
        return None

    def invert(
        self,
        gps_week: int,
        day: int,
        analysis_center: str = "igs",
        format: str = "sp3",
    ) -> str:
        """Invert the metadata back to a filename.

        Args:
            gps_week (int): _description_
            day (int): _description_
            analysis_center (str, optional): _description_. Defaults to "igs".
            format (str, optional): _description_. Defaults to "sp3".

        Returns:
            str: _description_
        """
        # Assert that the metadata is valid
        assert day in range(8), "Day must be between 0 and 7"
        assert len(analysis_center) == 3, "Analysis center must be 3 characters long"
        assert len(format) == 3, "Format must be 3 characters long"

        return f"{analysis_center}{str(gps_week).zfill(4)}{str(int(day)).zfill(1)}.{format}.Z"