diff --git a/linuxptp-nmeadelay.patch b/linuxptp-nmeadelay.patch index b06c7c1..25114c8 100644 --- a/linuxptp-nmeadelay.patch +++ b/linuxptp-nmeadelay.patch @@ -1,29 +1,342 @@ -commit 0404c1924254c9162222dd5000a140165d21250c +commit c4d00cfd9a611c9157f57a97babde11255f4b4e4 Author: Miroslav Lichvar -Date: Thu Jan 25 15:23:44 2024 +0100 +Date: Mon Jun 3 16:43:50 2024 +0200 - ts2phc: Fix offset for NMEA delays over 0.5 seconds. + ts2phc: Provide source type. - The current code of ts2phc assumes that the NMEA RMC message is received - within 0.5 seconds of the pulse the timestamp in the message is refering - to. However, with most receivers NMEA messages are referenced to the - previous pulse. This causes a 1-second error in the measured offset for - receivers with delays over 0.5 seconds. - - Add a 0.5 second correction to map the whole interval between pulses to - the preceding pulse. + 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 + Reviewed-by: Jacob Keller +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 e9f8d8fa119109f15e2377aef0ce4c62e261cba4 +Author: Miroslav Lichvar +Date: Mon Jun 3 16:43:51 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 + Reviewed-by: Jacob Keller + +diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c +index e3e9caf..33df968 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 81c447a24c75d56d414bf318ea5b5a6fc35d5129 +Author: Miroslav Lichvar +Date: Mon Jun 3 16:43:52 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 + Reviewed-by: Jacob Keller + +diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c +index 33df968..be3bd9a 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) { +@@ -248,12 +244,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 683f66aa46b7f9a5bd434e65a2d5703145c43b09 +Author: Miroslav Lichvar +Date: Mon Jun 3 16:43:53 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 + Reviewed-by: Jacob Keller + +diff --git a/ts2phc.c b/ts2phc.c +index 9fca34d..39d31b6 100644 +--- a/ts2phc.c ++++ b/ts2phc.c +@@ -420,9 +420,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 be3bd9a..f285c2b 100644 +--- a/ts2phc_pps_sink.c ++++ b/ts2phc_pps_sink.c +@@ -249,8 +249,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; + +commit 50f5a22df7a9a3f361e42bd29f84e26af04ac78b +Author: Miroslav Lichvar +Date: Mon Jun 3 16:43:54 2024 +0200 + + ts2phc: Add option to correct for NMEA delay. + + Add an option to specify the minimum expected delay of NMEA RMC messages + to correct timestamps returned by the NMEA PPS time source. + + This enables operation with receivers that have delays outside of the + expected 0-1.0s interval, or 0-pulsewidth if the PPS edge rejection is + enabled. + + [ RPC: Preserve alphabetical ordering in the ts2phc man page. ] + + Signed-off-by: Miroslav Lichvar + Signed-off-by: Richard Cochran + Reviewed-by: Jacob Keller + +diff --git a/config.c b/config.c +index 5dcf7ca..d0bc32c 100644 +--- a/config.c ++++ b/config.c +@@ -373,6 +373,7 @@ struct config_item config_tab[] = { + PORT_ITEM_INT("ts2phc.holdover", 0, 0, INT_MAX), + PORT_ITEM_INT("ts2phc.master", 0, 0, 1), + PORT_ITEM_INT("ts2phc.nmea_baudrate", 9600, 300, INT_MAX), ++ PORT_ITEM_INT("ts2phc.nmea_delay", 0, INT_MIN, INT_MAX), + GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""), + GLOB_ITEM_STR("ts2phc.nmea_remote_port", ""), + GLOB_ITEM_STR("ts2phc.nmea_serialport", "/dev/ttyS0"), +diff --git a/ts2phc.8 b/ts2phc.8 +index 41f91d9..177586d 100644 +--- a/ts2phc.8 ++++ b/ts2phc.8 +@@ -239,16 +239,6 @@ The kp_scale constant in the formula used to set the proportional constant of + the PI controller from the sync interval. + The default is 0.7. + +-.TP +-.B ts2phc.holdover +-The holdover interval, specified in seconds. When the ToD information stops +-working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified +-interval to continue synchronizing the target clock as long as the servo is in +-the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the +-\fBservo_offset_threshold\fP option. The holdover is not supported with the +-\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP. +-The default is 0 (disabled). +- + .TP + .B sa_file + Specifies the location of the file containing Security Associations used +@@ -288,6 +278,26 @@ for all messages in accordance to the corresponding security association + sourced via the \fBsa_file\fR directive. Not compatible with one step ports. + Must be in the range of -1 to 255, inclusive. The default is -1 (disabled). + ++.TP ++.B ts2phc.holdover ++The holdover interval, specified in seconds. When the ToD information stops ++working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified ++interval to continue synchronizing the target clock as long as the servo is in ++the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the ++\fBservo_num_offset_values\fP option. The holdover is not supported with the ++\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP. ++The default is 0 (disabled). ++ ++.TP ++.B ts2phc.nmea_delay ++Specifies the minimum expected delay of NMEA RMC messages in nanoseconds. ++If the maximum delay is longer than 1 second, or 'ts2phc.pulsewidth' ++if 'ts2phc.extts_polarity' is set to "both", this option needs to be set ++accordingly to allow the timestamps from NMEA messages to be correctly ++assigned to pulses from the PPS signal and wrong PPS edges to be rejected if ++the edge rejection is enabled. ++The default is 0 nanoseconds. ++ + .TP + .B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port + Specifies the remote host providing ToD information when using the diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c -index 3a4267d..274b70a 100644 +index 7a28433..3385e39 100644 --- a/ts2phc_nmea_pps_source.c +++ b/ts2phc_nmea_pps_source.c -@@ -191,6 +191,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src, +@@ -33,6 +33,7 @@ struct ts2phc_nmea_pps_source { + pthread_t worker; + /* Protects anonymous struct fields, below, from concurrent access. */ + pthread_mutex_t mutex; ++ tmv_t delay_correction; + struct { + struct timespec local_monotime; + struct timespec local_utctime; +@@ -187,6 +188,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src, return -1; } rmc = tmv_add(rmc, duration_since_rmc); -+ rmc = tmv_add(rmc, nanoseconds_to_tmv(500000000)); ++ rmc = tmv_add(rmc, m->delay_correction); utc_time = tmv_to_nanoseconds(rmc); utc_time /= (int64_t) 1000000000; *ts = tmv_to_timespec(rmc); +@@ -232,6 +234,8 @@ struct ts2phc_pps_source *ts2phc_nmea_pps_source_create(struct ts2phc_private *p + s->pps_source.destroy = ts2phc_nmea_pps_source_destroy; + s->pps_source.getppstime = ts2phc_nmea_pps_source_getppstime; + s->config = priv->cfg; ++ s->delay_correction = nanoseconds_to_tmv( ++ config_get_int(priv->cfg, NULL, "ts2phc.nmea_delay")); + pthread_mutex_init(&s->mutex, NULL); + err = pthread_create(&s->worker, NULL, monitor_nmea_status, s); + if (err) {