linuxptp/SOURCES/linuxptp-holdover.patch

361 lines
12 KiB
Diff

commit c7acd4b5bdb95e45d93833ffaac9cac51dfe934b
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu May 16 15:52:48 2024 +0200
ts2phc: Avoid unnecessary call of getppstime().
Don't get the ToD timestamp for the pulse polarity detection if it won't
be needed (i.e. extts_polarity is not "both"). This allows PPS
timestamps to be saved even when the ToD source fails.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 0d399b8..76912a2 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -277,21 +277,22 @@ static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv,
goto out;
}
- err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
- if (err < 0) {
- pr_debug("source ts not valid");
- return 0;
- }
-
- if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE) &&
- ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
+ if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE)) {
+ err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
+ if (err < 0) {
+ pr_debug("source ts not valid");
+ return 0;
+ }
- pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
- sink->name, event.index, event.t.sec, event.t.nsec,
- (int64_t) source_ts.tv_sec, source_ts.tv_nsec);
+ if (ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
+ pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
+ sink->name, event.index, event.t.sec,
+ event.t.nsec, (int64_t)source_ts.tv_sec,
+ source_ts.tv_nsec);
- result = EXTTS_IGNORE;
- goto out;
+ result = EXTTS_IGNORE;
+ goto out;
+ }
}
out:
commit 9880cccc928351a42a3fa9e018949442aca7ddae
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu May 16 15:52:49 2024 +0200
ts2phc: Add holdover support.
If the external PPS signal is generated by a clock with better long-term
stability than the clock synchronizing to it, it can work as a good time
source for some period of time after losing its own time source (e.g.
GPS receiver losing its signal).
Add an option to specify a holdover interval where ts2phc can continue
synchronizing the clock without any ToD information. Allow that only in
the LOCKED_STABLE servo state, which needs to be enabled by the
servo_num_offset_values option.
This is supported only in the non-automatic mode and when the pulse
polarity detection is disabled.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/config.c b/config.c
index c220a3e..58481db 100644
--- a/config.c
+++ b/config.c
@@ -344,6 +344,7 @@ struct config_item config_tab[] = {
PORT_ITEM_INT("ts2phc.channel", 0, 0, INT_MAX),
PORT_ITEM_INT("ts2phc.extts_correction", 0, INT_MIN, INT_MAX),
PORT_ITEM_ENU("ts2phc.extts_polarity", PTP_RISING_EDGE, extts_polarity_enu),
+ 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),
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
diff --git a/ts2phc.8 b/ts2phc.8
index 852a527..bcc6f61 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -168,6 +168,16 @@ by changing the clock frequency instead of stepping the clock. When
set to 0.0, the servo will never step the clock except on start.
The default is 0.0.
+.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_remote_host, ts2phc.nmea_remote_port
Specifies the remote host providing ToD information when using the
diff --git a/ts2phc.c b/ts2phc.c
index 4817c85..d552e0f 100644
--- a/ts2phc.c
+++ b/ts2phc.c
@@ -440,9 +440,10 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv,
static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
{
+ struct timespec source_ts, now;
tmv_t source_tmv;
struct ts2phc_clock *c;
- int valid, err;
+ int holdover, valid;
if (autocfg) {
if (!priv->ref_clock) {
@@ -456,9 +457,20 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
return;
}
} else {
- err = ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
- if (err < 0)
+ valid = !ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
+ }
+
+ if (valid) {
+ priv->holdover_start = 0;
+ holdover = 0;
+ } else {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if (!priv->holdover_start)
+ priv->holdover_start = now.tv_sec;
+ if (now.tv_sec >= priv->holdover_start + priv->holdover_length)
return;
+ holdover = 1;
}
LIST_FOREACH(c, &priv->clocks, list) {
@@ -475,6 +487,16 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
continue;
}
+ if (holdover) {
+ if (c->servo_state != SERVO_LOCKED_STABLE)
+ continue;
+ source_ts = tmv_to_timespec(ts);
+ if (source_ts.tv_nsec > NS_PER_SEC / 2)
+ source_ts.tv_sec++;
+ source_ts.tv_nsec = 0;
+ source_tmv = timespec_to_tmv(source_ts);
+ }
+
offset = tmv_to_nanoseconds(tmv_sub(ts, source_tmv));
if (c->no_adj) {
@@ -486,8 +508,15 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
adj = servo_sample(c->servo, offset, tmv_to_nanoseconds(ts),
SAMPLE_WEIGHT, &c->servo_state);
- pr_info("%s offset %10" PRId64 " s%d freq %+7.0f",
- c->name, offset, c->servo_state, adj);
+ if (holdover && c->servo_state != SERVO_LOCKED_STABLE) {
+ pr_info("%s lost holdover lock (offset %10" PRId64 ")",
+ c->name, offset);
+ continue;
+ }
+
+ pr_info("%s offset %10" PRId64 " s%d freq %+7.0f%s",
+ c->name, offset, c->servo_state, adj,
+ holdover ? " holdover" : "");
switch (c->servo_state) {
case SERVO_UNLOCKED:
@@ -751,6 +780,9 @@ int main(int argc, char *argv[])
return -1;
}
+ priv.holdover_length = config_get_int(cfg, NULL, "ts2phc.holdover");
+ priv.holdover_start = 0;
+
while (is_running()) {
struct ts2phc_clock *c;
diff --git a/ts2phc.h b/ts2phc.h
index 4833ded..5dbde9b 100644
--- a/ts2phc.h
+++ b/ts2phc.h
@@ -55,6 +55,8 @@ struct ts2phc_private {
bool state_changed;
LIST_HEAD(port_head, ts2phc_port) ports;
LIST_HEAD(clock_head, ts2phc_clock) clocks;
+ int holdover_length;
+ time_t holdover_start;
};
struct ts2phc_clock *ts2phc_clock_add(struct ts2phc_private *priv,
commit bf237fd55d4983a42d9344890dd861f18bea70ca
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Jul 25 12:43:59 2024 +0200
ts2phc: Describe servo options in man page.
Copy and adapt missing servo options used by ts2phc from the ptp4l man
page.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc.8 b/ts2phc.8
index bcc6f61..4c54576 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -117,6 +117,16 @@ command line option.
.SH GLOBAL OPTIONS
+.TP
+.B clock_servo
+The servo which is used to synchronize the local clock. Valid values
+are "pi" for a PI controller, "linreg" for an adaptive controller
+using linear regression, "ntpshm" and "refclock_sock" for the NTP SHM and
+chrony SOCK reference clocks respectively to allow another process to
+synchronize the local clock, and "nullf" for a servo that always dials
+frequency offset zero (for use in SyncE nodes).
+The default is "pi".
+
.TP
.B first_step_threshold
The maximum offset, specified in seconds, that the servo will correct by
@@ -162,12 +172,64 @@ with the log level of the message as a number. The default is an empty string
argument).
.TP
-.B step_threshold
-The maximum offset, specified in seconds, that the servo will correct
-by changing the clock frequency instead of stepping the clock. When
-set to 0.0, the servo will never step the clock except on start.
+.B ntpshm_segment
+The number of the SHM segment used by ntpshm servo.
+The default is 0.
+
+.TP
+.B pi_integral_const
+The integral constant of the PI controller. When set to 0.0, the
+integral constant will be set by the following formula from the current
+sync interval.
+The default is 0.0.
+
+ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync)
+
+.TP
+.B pi_integral_exponent
+The ki_exponent constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.4.
+
+.TP
+.B pi_integral_norm_max
+The ki_norm_max constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.3.
+
+.TP
+.B pi_integral_scale
+The ki_scale constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.3.
+
+.TP
+.B pi_proportional_const
+The proportional constant of the PI controller. When set to 0.0, the
+proportional constant will be set by the following formula from the current
+sync interval.
The default is 0.0.
+kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync)
+
+.TP
+.B pi_proportional_exponent
+The kp_exponent constant in the formula used to set the proportional constant of
+the PI controller from the sync interval.
+The default is \-0.3.
+
+.TP
+.B pi_proportional_norm_max
+The kp_norm_max 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 pi_proportional_scale
+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
@@ -178,6 +240,28 @@ the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
The default is 0 (disabled).
+.TP
+.B servo_num_offset_values
+The number of offset values considered in order to transition from the
+SERVO_LOCKED to the SERVO_LOCKED_STABLE state.
+The transition occurs once the last 'servo_num_offset_values' offsets
+are all below the 'servo_offset_threshold' value.
+The default value is 10.
+
+.TP
+.B servo_offset_threshold
+The offset threshold used in order to transition from the SERVO_LOCKED
+to the SERVO_LOCKED_STABLE state. The transition occurs once the
+last 'servo_num_offset_values' offsets are all below the threshold value.
+The default value of offset_threshold is 0 (disabled).
+
+.TP
+.B step_threshold
+The maximum offset, specified in seconds, that the servo will correct
+by changing the clock frequency instead of stepping the clock. When
+set to 0.0, the servo will never step the clock except on start.
+The default is 0.0.
+
.TP
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
Specifies the remote host providing ToD information when using the
commit 7734d2fe8ac52afaa233262548615f79021ae6ee
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Jul 25 12:46:43 2024 +0200
ts2phc: Fix description of holdover option in man page.
Fix the man page to explain that the LOCKED_STABLE servo state is
enabled by setting the servo_offset_threshold, not
servo_num_offset_values, which is already enabled by default.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc.8 b/ts2phc.8
index 4c54576..c0b718b 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -236,7 +236,7 @@ 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
+\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).