commit 0c406008b530140ed6d992915a6c8a1e5abf15d5 Author: Miroslav Lichvar Date: Thu Jan 25 11:26:15 2024 +0100 ts2phc: Don't switch system clock to nanosecond mode. ts2phc is not synchronizing the system clock and should not switch the clock to the nanosecond mode with adjtimex(modes=ADJ_NANO) or make any other modifications to it. The process that is controlling the clock (e.g. an NTP client) might not be using the nanosecond mode. There are two instances of the adjtimex() call in the code. One is used only to read the clock and can be replaced with faster clock_gettime(). The other instance is also reading the TAI offset. Instead of switching to the nanosecond mode, change the timestamp conversion to handle both microsecond and nanosecond modes according to the current clock status. Reviewed-by: Jacob Keller Signed-off-by: Miroslav Lichvar diff --git a/ts2phc_generic_pps_source.c b/ts2phc_generic_pps_source.c index d503aac..e6b8145 100644 --- a/ts2phc_generic_pps_source.c +++ b/ts2phc_generic_pps_source.c @@ -38,7 +38,6 @@ static int get_ntx(struct timex *ntx) return -1; memset(ntx, 0, sizeof(*ntx)); - ntx->modes = ADJ_NANO; code = adjtimex(ntx); if (code == -1) { pr_err("adjtimex failed: %m"); @@ -93,7 +92,10 @@ static int ts2phc_generic_pps_source_getppstime(struct ts2phc_pps_source *src, } ts->tv_sec = ntx.time.tv_sec + tai_offset; - ts->tv_nsec = ntx.time.tv_usec; + if (ntx.status & STA_NANO) + ts->tv_nsec = ntx.time.tv_usec; + else + ts->tv_nsec = ntx.time.tv_usec * 1000; return 0; } diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c index 3a4267d..5b2e06b 100644 --- a/ts2phc_nmea_pps_source.c +++ b/ts2phc_nmea_pps_source.c @@ -62,14 +62,13 @@ static int open_nmea_connection(const char *host, const char *port, static void *monitor_nmea_status(void *arg) { + struct timespec rxtime, rxtime_rt, tmo = { 2, 0 }; struct nmea_parser *np = nmea_parser_create(); struct pollfd pfd = { -1, POLLIN | POLLPRI }; char *host, input[256], *port, *ptr, *uart; struct ts2phc_nmea_pps_source *s = arg; - struct timespec rxtime, tmo = { 2, 0 }; int cnt, num, parsed, baud; struct nmea_rmc rmc; - struct timex ntx; if (!np) { pr_err("failed to create NMEA parser"); @@ -79,8 +78,6 @@ static void *monitor_nmea_status(void *arg) port = config_get_string(s->config, NULL, "ts2phc.nmea_remote_port"); uart = config_get_string(s->config, NULL, "ts2phc.nmea_serialport"); baud = config_get_int(s->config, NULL, "ts2phc.nmea_baudrate"); - memset(&ntx, 0, sizeof(ntx)); - ntx.modes = ADJ_NANO; while (is_running()) { if (pfd.fd == -1) { @@ -92,7 +89,7 @@ static void *monitor_nmea_status(void *arg) } num = poll(&pfd, 1, NMEA_TMO); clock_gettime(CLOCK_MONOTONIC, &rxtime); - adjtimex(&ntx); + clock_gettime(CLOCK_REALTIME, &rxtime_rt); if (num < 0) { pr_err("poll failed"); break; @@ -124,8 +121,7 @@ static void *monitor_nmea_status(void *arg) if (!nmea_parse(np, ptr, cnt, &rmc, &parsed)) { pthread_mutex_lock(&s->mutex); s->local_monotime = rxtime; - s->local_utctime.tv_sec = ntx.time.tv_sec; - s->local_utctime.tv_nsec = ntx.time.tv_usec; + s->local_utctime = rxtime_rt; s->rmc_utctime = rmc.ts; s->rmc_fix_valid = rmc.fix_valid; pthread_mutex_unlock(&s->mutex); commit 72b44bc885e519667a12c89d5b640484807e4946 Author: Miroslav Lichvar Date: Tue Jun 4 08:57:15 2024 +0200 ts2phc: Use CLOCK_MONOTONIC_RAW for NMEA PPS timestamp. In the calculation of the NMEA PPS timestamp is used an interval measured by the CLOCK_MONOTONIC system clock. This clock may have a large frequency error when another process (e.g. phc2sys or an NTP client) is correcting a large time error by slewing. This frequency error may cause the timestamp to overflow into the next second and cause a one-second error in the measured offset, or the wrong edge of the pulse to be rejected. Switch from CLOCK_MONOTONIC to CLOCK_MONOTONIC_RAW to avoid the impact of the system clock adjustments. Signed-off-by: Miroslav Lichvar diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c index 5b2e06b..7a28433 100644 --- a/ts2phc_nmea_pps_source.c +++ b/ts2phc_nmea_pps_source.c @@ -88,7 +88,7 @@ static void *monitor_nmea_status(void *arg) } } num = poll(&pfd, 1, NMEA_TMO); - clock_gettime(CLOCK_MONOTONIC, &rxtime); + clock_gettime(CLOCK_MONOTONIC_RAW, &rxtime); clock_gettime(CLOCK_REALTIME, &rxtime_rt); if (num < 0) { pr_err("poll failed"); @@ -160,7 +160,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src, int64_t utc_time; bool fix_valid; - clock_gettime(CLOCK_MONOTONIC, &now); + clock_gettime(CLOCK_MONOTONIC_RAW, &now); local_t2 = timespec_to_tmv(now); pthread_mutex_lock(&m->mutex); commit 30e6c4dba892236d8cfe08dc6c55238e11504c71 Author: Miroslav Lichvar Date: Mon Jun 3 10:32:05 2024 +0200 ts2phc: Provide source type. Save the PPS source type in the instance and add a function to retrieve it. This will be needed for NMEA-specific rounding of timestamps. Signed-off-by: Miroslav Lichvar diff --git a/ts2phc_pps_source.c b/ts2phc_pps_source.c index c333f65..ae2ad46 100644 --- a/ts2phc_pps_source.c +++ b/ts2phc_pps_source.c @@ -26,6 +26,8 @@ struct ts2phc_pps_source *ts2phc_pps_source_create(struct ts2phc_private *priv, src = ts2phc_phc_pps_source_create(priv, dev); break; } + if (src) + src->type = type; return src; } @@ -46,3 +48,8 @@ struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src) return NULL; } + +enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src) +{ + return src->type; +} diff --git a/ts2phc_pps_source.h b/ts2phc_pps_source.h index 293c693..c87e3af 100644 --- a/ts2phc_pps_source.h +++ b/ts2phc_pps_source.h @@ -53,4 +53,11 @@ int ts2phc_pps_source_getppstime(struct ts2phc_pps_source *src, struct timespec struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src); +/** + * Returns the type of the PPS source + * @param src Pointer to a source obtained via @ref ts2phc_pps_source_create(). + * @return The type of the clock. + */ +enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src); + #endif diff --git a/ts2phc_pps_source_private.h b/ts2phc_pps_source_private.h index 99e6a78..ea6a8ad 100644 --- a/ts2phc_pps_source_private.h +++ b/ts2phc_pps_source_private.h @@ -13,6 +13,7 @@ #include "ts2phc_pps_source.h" struct ts2phc_pps_source { + enum ts2phc_pps_source_type type; void (*destroy)(struct ts2phc_pps_source *src); int (*getppstime)(struct ts2phc_pps_source *src, struct timespec *ts); struct ts2phc_clock *(*get_clock)(struct ts2phc_pps_source *src); commit bebd15ae90dbfcb74a33e5b428f24c733abf1134 Author: Miroslav Lichvar Date: Mon Jun 3 11:06:16 2024 +0200 ts2phc: Fix edge rejection for pulse widths over 0.5s. If the configured pulse width is longer than 0.5 seconds, the calculated range of ignored offsets is too small to cover the wrong edge. Fix the calculation of the limits to use the minimum of pulsewidth and (1.0s - pulsewidth). A pulsewidth of 0.5s should give the shortest interval. Signed-off-by: Miroslav Lichvar diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c index 05ac225..ca9f721 100644 --- a/ts2phc_pps_sink.c +++ b/ts2phc_pps_sink.c @@ -175,6 +175,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri sink->correction = nanoseconds_to_tmv(correction); pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth"); + if (pulsewidth > 500000000) + pulsewidth = 1000000000 - pulsewidth; pulsewidth /= 2; sink->ignore_upper = 1000000000 - pulsewidth; sink->ignore_lower = pulsewidth; commit 435e9fc6ebec8daa8ab9f88c2d590e35ace9b2f6 Author: Miroslav Lichvar Date: Mon Jun 3 13:57:23 2024 +0200 ts2phc: Move upper/lower rejection limit calculation. The ignore_upper and ignore_lower fields of the ts2phc_pps_sink struct are calculated when the PPS source is not known yet. Replace the fields with the configured pulsewidth and calculate the limits locally later when needed in ts2phc_pps_sink_ignore(). This will allow an NMEA-specific calculation of the limits. Signed-off-by: Miroslav Lichvar diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c index ca9f721..8121afb 100644 --- a/ts2phc_pps_sink.c +++ b/ts2phc_pps_sink.c @@ -30,8 +30,7 @@ struct ts2phc_pps_sink { struct ptp_pin_desc pin_desc; unsigned int polarity; tmv_t correction; - uint32_t ignore_lower; - uint32_t ignore_upper; + uint32_t pulsewidth; struct ts2phc_clock *clock; }; @@ -153,8 +152,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri struct config *cfg = priv->cfg; struct ptp_extts_request extts; struct ts2phc_pps_sink *sink; - int err, pulsewidth; int32_t correction; + int err; sink = calloc(1, sizeof(*sink)); if (!sink) { @@ -174,12 +173,9 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri correction = config_get_int(cfg, device, "ts2phc.extts_correction"); sink->correction = nanoseconds_to_tmv(correction); - pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth"); - if (pulsewidth > 500000000) - pulsewidth = 1000000000 - pulsewidth; - pulsewidth /= 2; - sink->ignore_upper = 1000000000 - pulsewidth; - sink->ignore_lower = pulsewidth; + sink->pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth"); + if (sink->pulsewidth > 500000000) + sink->pulsewidth = 1000000000 - sink->pulsewidth; sink->clock = ts2phc_clock_add(priv, device); if (!sink->clock) { @@ -243,12 +239,16 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv, struct timespec source_ts) { tmv_t source_tmv = timespec_to_tmv(source_ts); + uint32_t ignore_lower, ignore_upper; source_tmv = tmv_sub(source_tmv, priv->perout_phase); source_ts = tmv_to_timespec(source_tmv); - return source_ts.tv_nsec > sink->ignore_lower && - source_ts.tv_nsec < sink->ignore_upper; + ignore_upper = 1000000000 - sink->pulsewidth / 2; + ignore_lower = sink->pulsewidth / 2; + + return source_ts.tv_nsec > ignore_lower && + source_ts.tv_nsec < ignore_upper; } static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv, commit 0257b245df1a32869f356c0cfbeacfe5f0a522f5 Author: Miroslav Lichvar Date: Mon Jun 3 11:31:45 2024 +0200 ts2phc: Allow longer NMEA delays. Timestamps from PPS sources are rounded to the nearest pulse, i.e. for correct synchronization the offset between the source timestamp and external PPS event timestamp needs to be between -0.5 and +0.5 seconds. For the pulse edge rejection to work correctly it needs to be even smaller, between -pulsewidth/2 and +pulsewidth/2. With the NMEA PPS source the offset is the delay in the reception of the RMC message. This message is not expected to come exactly on time with the corresponding pulse. A positive delay is expected, typically at least few tens of milliseconds, but some receivers have delays longer than 0.5 seconds. Add NMEA-specific rounding of the offset to not expect negative delays and allow positive delays of up to 1 second, or whole pulsewidth if the edge rejection is enabled. Signed-off-by: Miroslav Lichvar diff --git a/ts2phc.c b/ts2phc.c index 03c88b1..4817c85 100644 --- a/ts2phc.c +++ b/ts2phc.c @@ -418,9 +418,16 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv, * deduce the timestamp (actually only seconds part, nanoseconds are by * construction zero) of this edge at the emitter based on the * emitter's current time. + * + * With an NMEA source assume its messages always follow the pulse, i.e. + * assign the timestamp to the previous pulse instead of nearest pulse. */ - if (source_ts.tv_nsec > NS_PER_SEC / 2) + if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) { source_ts.tv_sec++; + } else { + if (source_ts.tv_nsec > NS_PER_SEC / 2) + source_ts.tv_sec++; + } source_ts.tv_nsec = 0; tmv = timespec_to_tmv(source_ts); diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c index 8121afb..0d399b8 100644 --- a/ts2phc_pps_sink.c +++ b/ts2phc_pps_sink.c @@ -244,8 +244,13 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv, source_tmv = tmv_sub(source_tmv, priv->perout_phase); source_ts = tmv_to_timespec(source_tmv); - ignore_upper = 1000000000 - sink->pulsewidth / 2; - ignore_lower = sink->pulsewidth / 2; + if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) { + ignore_upper = sink->pulsewidth; + ignore_lower = 0; + } else { + ignore_upper = 1000000000 - sink->pulsewidth / 2; + ignore_lower = sink->pulsewidth / 2; + } return source_ts.tv_nsec > ignore_lower && source_ts.tv_nsec < ignore_upper;