Skip to content

Commit

Permalink
ts2phc: Add holdover support.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mlichvar authored and richardcochran committed Jul 22, 2024
1 parent 9c48f5d commit 5ce45bd
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 5 deletions.
1 change: 1 addition & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,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", ""),
Expand Down
10 changes: 10 additions & 0 deletions ts2phc.8
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ with the log level of the message as a number. The default is an empty string
(which cannot be set in the configuration file as the option requires an
argument).

.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 sa_file
Specifies the location of the file containing Security Associations used
Expand Down
42 changes: 37 additions & 5 deletions ts2phc.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,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) {
Expand All @@ -451,9 +452,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) {
Expand All @@ -470,6 +482,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) {
Expand All @@ -481,8 +503,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:
Expand Down Expand Up @@ -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 *clk;

Expand Down
2 changes: 2 additions & 0 deletions ts2phc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 5ce45bd

Please sign in to comment.