openrvdas.logger.transforms.timestamp_transform

No module-level documentation available.
  1#!/usr/bin/env python3
  2
  3import logging
  4
  5from logger.utils import timestamp  # noqa: E402
  6from logger.utils.nmea_timestamp import NMEATimestampExtractor  # noqa: E402
  7from logger.transforms.transform import Transform  # noqa: E402
  8
  9
 10################################################################################
 11class TimestampTransform(Transform):
 12    """Prepend a timestamp to a text record.
 13
 14    By default the system clock is used.  When ``use_nmea_timestamp`` is
 15    enabled, the transform first attempts to extract the timestamp
 16    embedded in NMEA 0183 sentences (GGA, RMC, ZDA, and many others)
 17    and only falls back to the system clock when no NMEA time is
 18    available.
 19
 20    Parameters
 21    ----------
 22    time_format : str
 23        strftime format string for the prepended timestamp.  Defaults
 24        to ``logger.utils.timestamp.TIME_FORMAT``
 25        (``'%Y-%m-%dT%H:%M:%S.%fZ'``).
 26    time_zone : datetime.timezone
 27        Timezone applied to the timestamp.  Defaults to UTC.
 28    sep : str
 29        Separator inserted between the timestamp and the record text.
 30        Defaults to a single space.
 31
 32    use_nmea_timestamp : bool
 33        When True, attempt to parse the NMEA time field from the
 34        incoming record before falling back to the system clock.
 35        Defaults to False.
 36    nmea_timestamp_timeout : float
 37        How many seconds a previously-extracted NMEA timestamp remains
 38        valid for records that do not carry their own time field
 39        (e.g. VTG, HDT).  After this period the system clock is used
 40        instead.  Defaults to 1 s.
 41    nmea_time_drift_threshold : float or None
 42        If the absolute difference between the NMEA-derived time and
 43        the system clock exceeds this value (in seconds), a warning is
 44        logged.  Set to None to disable the check.  Defaults to 0.1 s.
 45    quiet : bool
 46        Inherited from Transform (passed via ``**kwargs``).  When True,
 47        suppresses all warnings from both the transform and the
 48        underlying NMEATimestampExtractor (drift, staleness, and
 49        fallback warnings).  Defaults to False.
 50    """
 51    def __init__(self, time_format=timestamp.TIME_FORMAT,
 52                 time_zone=timestamp.timezone.utc, sep=' ',
 53                 use_nmea_timestamp=False,
 54                 nmea_timestamp_timeout=1,
 55                 nmea_time_drift_threshold=0.1, **kwargs):
 56        """Create a TimestampTransform."""
 57        super().__init__(**kwargs)  # processes 'quiet' and type hints
 58
 59        self.time_format = time_format
 60        self.time_zone = time_zone
 61        self.sep = sep
 62        self.use_nmea_timestamp = use_nmea_timestamp
 63        self.nmea_extractor = None
 64        if use_nmea_timestamp:
 65            self.nmea_extractor = NMEATimestampExtractor(
 66                timeout=nmea_timestamp_timeout,
 67                quiet=self.quiet,
 68                time_drift_threshold=nmea_time_drift_threshold)
 69
 70    ############################
 71    def transform(self, record: str, ts=None) -> str:
 72        """Prepend a timestamp"""
 73
 74        # If they've not given us a timestamp, find one.
 75        if ts is None:
 76            if self.nmea_extractor is None:
 77                # Things are simple if we're not trying to extract a timestamp from NMEA.
 78                ts = timestamp.time_str(time_format=self.time_format,
 79                                        time_zone=self.time_zone)
 80            else:
 81                # If we're going to try to extract ts from NMEA
 82                ts = self.nmea_extractor.get_timestamp(
 83                    record, self.time_format, self.time_zone)
 84                # Failed, so fall back to system time.
 85                if ts is None and not self.quiet:
 86                    logging.warning(
 87                        'TimestampTransform: no NMEA timestamp for record, '
 88                        'falling back to system time')
 89                ts = ts or timestamp.time_str(time_format=self.time_format,
 90                                              time_zone=self.time_zone)
 91
 92        # See if it's something we can process, and if not, try digesting
 93        if not self.can_process_record(record):  # inherited from BaseModule()
 94            # Special case: if we can't process it, but it's a list, pass
 95            # along the same initial timestamp so all elements in the list
 96            # share the same timestamp.
 97            if isinstance(record, list):
 98                return [self.transform(r, ts) for r in record]
 99            # If not str and not list, pass it along to digest_record()
100            # to let it try and/or complain.
101            else:
102                return self.digest_record(record)  # inherited from BaseModule()
103
104        # If it is something we can process, put a timestamp on it.
105        return ts + self.sep + record
class TimestampTransform(logger.transforms.transform.Transform):
 12class TimestampTransform(Transform):
 13    """Prepend a timestamp to a text record.
 14
 15    By default the system clock is used.  When ``use_nmea_timestamp`` is
 16    enabled, the transform first attempts to extract the timestamp
 17    embedded in NMEA 0183 sentences (GGA, RMC, ZDA, and many others)
 18    and only falls back to the system clock when no NMEA time is
 19    available.
 20
 21    Parameters
 22    ----------
 23    time_format : str
 24        strftime format string for the prepended timestamp.  Defaults
 25        to ``logger.utils.timestamp.TIME_FORMAT``
 26        (``'%Y-%m-%dT%H:%M:%S.%fZ'``).
 27    time_zone : datetime.timezone
 28        Timezone applied to the timestamp.  Defaults to UTC.
 29    sep : str
 30        Separator inserted between the timestamp and the record text.
 31        Defaults to a single space.
 32
 33    use_nmea_timestamp : bool
 34        When True, attempt to parse the NMEA time field from the
 35        incoming record before falling back to the system clock.
 36        Defaults to False.
 37    nmea_timestamp_timeout : float
 38        How many seconds a previously-extracted NMEA timestamp remains
 39        valid for records that do not carry their own time field
 40        (e.g. VTG, HDT).  After this period the system clock is used
 41        instead.  Defaults to 1 s.
 42    nmea_time_drift_threshold : float or None
 43        If the absolute difference between the NMEA-derived time and
 44        the system clock exceeds this value (in seconds), a warning is
 45        logged.  Set to None to disable the check.  Defaults to 0.1 s.
 46    quiet : bool
 47        Inherited from Transform (passed via ``**kwargs``).  When True,
 48        suppresses all warnings from both the transform and the
 49        underlying NMEATimestampExtractor (drift, staleness, and
 50        fallback warnings).  Defaults to False.
 51    """
 52    def __init__(self, time_format=timestamp.TIME_FORMAT,
 53                 time_zone=timestamp.timezone.utc, sep=' ',
 54                 use_nmea_timestamp=False,
 55                 nmea_timestamp_timeout=1,
 56                 nmea_time_drift_threshold=0.1, **kwargs):
 57        """Create a TimestampTransform."""
 58        super().__init__(**kwargs)  # processes 'quiet' and type hints
 59
 60        self.time_format = time_format
 61        self.time_zone = time_zone
 62        self.sep = sep
 63        self.use_nmea_timestamp = use_nmea_timestamp
 64        self.nmea_extractor = None
 65        if use_nmea_timestamp:
 66            self.nmea_extractor = NMEATimestampExtractor(
 67                timeout=nmea_timestamp_timeout,
 68                quiet=self.quiet,
 69                time_drift_threshold=nmea_time_drift_threshold)
 70
 71    ############################
 72    def transform(self, record: str, ts=None) -> str:
 73        """Prepend a timestamp"""
 74
 75        # If they've not given us a timestamp, find one.
 76        if ts is None:
 77            if self.nmea_extractor is None:
 78                # Things are simple if we're not trying to extract a timestamp from NMEA.
 79                ts = timestamp.time_str(time_format=self.time_format,
 80                                        time_zone=self.time_zone)
 81            else:
 82                # If we're going to try to extract ts from NMEA
 83                ts = self.nmea_extractor.get_timestamp(
 84                    record, self.time_format, self.time_zone)
 85                # Failed, so fall back to system time.
 86                if ts is None and not self.quiet:
 87                    logging.warning(
 88                        'TimestampTransform: no NMEA timestamp for record, '
 89                        'falling back to system time')
 90                ts = ts or timestamp.time_str(time_format=self.time_format,
 91                                              time_zone=self.time_zone)
 92
 93        # See if it's something we can process, and if not, try digesting
 94        if not self.can_process_record(record):  # inherited from BaseModule()
 95            # Special case: if we can't process it, but it's a list, pass
 96            # along the same initial timestamp so all elements in the list
 97            # share the same timestamp.
 98            if isinstance(record, list):
 99                return [self.transform(r, ts) for r in record]
100            # If not str and not list, pass it along to digest_record()
101            # to let it try and/or complain.
102            else:
103                return self.digest_record(record)  # inherited from BaseModule()
104
105        # If it is something we can process, put a timestamp on it.
106        return ts + self.sep + record

Prepend a timestamp to a text record.

By default the system clock is used. When use_nmea_timestamp is enabled, the transform first attempts to extract the timestamp embedded in NMEA 0183 sentences (GGA, RMC, ZDA, and many others) and only falls back to the system clock when no NMEA time is available.

Parameters

time_format : str strftime format string for the prepended timestamp. Defaults to logger.utils.timestamp.TIME_FORMAT ('%Y-%m-%dT%H:%M:%S.%fZ'). time_zone : datetime.timezone Timezone applied to the timestamp. Defaults to UTC. sep : str Separator inserted between the timestamp and the record text. Defaults to a single space.

use_nmea_timestamp : bool When True, attempt to parse the NMEA time field from the incoming record before falling back to the system clock. Defaults to False. nmea_timestamp_timeout : float How many seconds a previously-extracted NMEA timestamp remains valid for records that do not carry their own time field (e.g. VTG, HDT). After this period the system clock is used instead. Defaults to 1 s. nmea_time_drift_threshold : float or None If the absolute difference between the NMEA-derived time and the system clock exceeds this value (in seconds), a warning is logged. Set to None to disable the check. Defaults to 0.1 s. quiet : bool Inherited from Transform (passed via **kwargs). When True, suppresses all warnings from both the transform and the underlying NMEATimestampExtractor (drift, staleness, and fallback warnings). Defaults to False.

TimestampTransform( time_format='%Y-%m-%dT%H:%M:%S.%fZ', time_zone=datetime.timezone.utc, sep=' ', use_nmea_timestamp=False, nmea_timestamp_timeout=1, nmea_time_drift_threshold=0.1, **kwargs)
52    def __init__(self, time_format=timestamp.TIME_FORMAT,
53                 time_zone=timestamp.timezone.utc, sep=' ',
54                 use_nmea_timestamp=False,
55                 nmea_timestamp_timeout=1,
56                 nmea_time_drift_threshold=0.1, **kwargs):
57        """Create a TimestampTransform."""
58        super().__init__(**kwargs)  # processes 'quiet' and type hints
59
60        self.time_format = time_format
61        self.time_zone = time_zone
62        self.sep = sep
63        self.use_nmea_timestamp = use_nmea_timestamp
64        self.nmea_extractor = None
65        if use_nmea_timestamp:
66            self.nmea_extractor = NMEATimestampExtractor(
67                timeout=nmea_timestamp_timeout,
68                quiet=self.quiet,
69                time_drift_threshold=nmea_time_drift_threshold)

Create a TimestampTransform.

time_format
time_zone
sep
use_nmea_timestamp
nmea_extractor
def transform(self, record: str, ts=None) -> str:
 72    def transform(self, record: str, ts=None) -> str:
 73        """Prepend a timestamp"""
 74
 75        # If they've not given us a timestamp, find one.
 76        if ts is None:
 77            if self.nmea_extractor is None:
 78                # Things are simple if we're not trying to extract a timestamp from NMEA.
 79                ts = timestamp.time_str(time_format=self.time_format,
 80                                        time_zone=self.time_zone)
 81            else:
 82                # If we're going to try to extract ts from NMEA
 83                ts = self.nmea_extractor.get_timestamp(
 84                    record, self.time_format, self.time_zone)
 85                # Failed, so fall back to system time.
 86                if ts is None and not self.quiet:
 87                    logging.warning(
 88                        'TimestampTransform: no NMEA timestamp for record, '
 89                        'falling back to system time')
 90                ts = ts or timestamp.time_str(time_format=self.time_format,
 91                                              time_zone=self.time_zone)
 92
 93        # See if it's something we can process, and if not, try digesting
 94        if not self.can_process_record(record):  # inherited from BaseModule()
 95            # Special case: if we can't process it, but it's a list, pass
 96            # along the same initial timestamp so all elements in the list
 97            # share the same timestamp.
 98            if isinstance(record, list):
 99                return [self.transform(r, ts) for r in record]
100            # If not str and not list, pass it along to digest_record()
101            # to let it try and/or complain.
102            else:
103                return self.digest_record(record)  # inherited from BaseModule()
104
105        # If it is something we can process, put a timestamp on it.
106        return ts + self.sep + record

Prepend a timestamp