rework NMEA delay patch to fix PPS edge rejection (RHEL-54719)
Resolves: RHEL-54719
This commit is contained in:
parent
14b00cf4eb
commit
8a2fbb8daf
@ -1,29 +1,342 @@
|
||||
commit 0404c1924254c9162222dd5000a140165d21250c
|
||||
commit c4d00cfd9a611c9157f57a97babde11255f4b4e4
|
||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||
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 <mlichvar@redhat.com>
|
||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||
|
||||
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 <mlichvar@redhat.com>
|
||||
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 <mlichvar@redhat.com>
|
||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||
|
||||
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 <mlichvar@redhat.com>
|
||||
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 <mlichvar@redhat.com>
|
||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||
|
||||
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 <mlichvar@redhat.com>
|
||||
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 <mlichvar@redhat.com>
|
||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||
|
||||
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 <mlichvar@redhat.com>
|
||||
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 <mlichvar@redhat.com>
|
||||
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
|
||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user