361 lines
12 KiB
Diff
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).
|
|
|