Implement auto mode for frequency setting. In this mode the tool
will measure the frequency difference between PHC and CLOCK_REALTIME
and apply the correction to the PHC to match the system clock.

Signed-off-by: Maciek Machnikowski <mac...@machnikowski.net>
---
 phc_ctl.8 | 13 +++++++-
 phc_ctl.c | 92 +++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 84 insertions(+), 21 deletions(-)

diff --git a/phc_ctl.8 b/phc_ctl.8
index 650e661..c9e37e2 100644
--- a/phc_ctl.8
+++ b/phc_ctl.8
@@ -60,7 +60,8 @@ Adjust the PHC clock by an amount of seconds provided. This 
argument is required
 .BI freq " ppb"
 Adjust the frequency of the PHC clock by the specified parts per billion. If no
 argument is provided, it will attempt to read the current frequency and report
-it.
+it. If keyword "auto" is passed, the frequency of the PHC clock will be 
adjusted
+to match the frequency of the CLOCK_REALTIME.
 .TP
 .BI cmp
 Compare the PHC clock device to CLOCK_REALTIME, using the best method 
available.
@@ -95,6 +96,16 @@ Set PHC clock time to 0 (seconds since Epoch)
 \f(CWphc_ctl /dev/ptp0 set 0.0\fP
 .RE
 
+Set PHC frequency to a negative value
+.RS
+\f(CWphc_ctl /dev/ptp0 -- freq -10\fP
+.RE
+
+Set PHC frequency and time from the system clock
+.RS
+\f(CWphc_ctl /dev/ptp0 -- freq auto set\fP
+.RE
+
 Quickly sanity check frequency slewing by setting slewing frequency by positive
 10%, resetting clock to 0.0 time, waiting for 10 seconds, and then reading
 time. The time read back should be (roughly) 11 seconds, since the clock was
diff --git a/phc_ctl.c b/phc_ctl.c
index 6a5c2f4..6a66b1a 100644
--- a/phc_ctl.c
+++ b/phc_ctl.c
@@ -49,6 +49,7 @@
 #include "version.h"
 
 #define NSEC2SEC 1000000000.0
+#define N_SAMPLES 9
 
 /* trap the alarm signal so that pause() will wake up on receipt */
 static void handle_alarm(int s)
@@ -243,35 +244,88 @@ static int do_adj(clockid_t clkid, int cmdc, char *cmdv[])
 
 static int do_freq(clockid_t clkid, int cmdc, char *cmdv[])
 {
-       double ppb;
+       int64_t t1_sys_offset, t1_delay, t2_sys_offset, t2_delay;
+       struct timespec t1_clk, t1_sys, t2_clk, t2_sys;
+       uint64_t t1_sys_ts, t2_sys_ts;
+       int64_t clk_diff, sys_diff;
        enum parser_result r;
+       int method, fd;
+       double ppb;
 
        clockadj_init(clkid);
-
        if (cmdc < 1 || name_is_a_command(cmdv[0])) {
                ppb = clockadj_get_freq(clkid);
                pr_err("clock frequency offset is %lfppb", ppb);
 
                /* no argument was used */
                return 0;
-       }
+       } else if (strcmp(cmdv[0], "auto") == 0) {
+               /* set the PHC freq to match the system time */
+               fd = CLOCKID_TO_FD(clkid);
 
-       /* parse the double ppb argument */
-       r = get_ranged_double(cmdv[0], &ppb, -NSEC2SEC, NSEC2SEC);
-       switch (r) {
-       case PARSED_OK:
-               break;
-       case MALFORMED:
-               pr_err("freq: '%s' is not a valid double", cmdv[0]);
-               return -2;
-       case OUT_OF_RANGE:
-               pr_err("freq: '%s' is out of range.", cmdv[0]);
-               return -2;
-       default:
-               pr_err("freq: couldn't process '%s'", cmdv[0]);
-               return -2;
-       }
+               ppb = clockadj_get_freq(clkid);
+
+               /* Try sysoff first */
+               method = sysoff_probe(fd, N_SAMPLES);
+               if (method >= 0) {
+                       if (sysoff_measure(fd, method, N_SAMPLES,
+                                          &t1_sys_offset,
+                                          &t1_sys_ts, &t1_delay)) {
+                               return -1;
+                       }
+
+                       sleep(1);
 
+                       if (sysoff_measure(fd, method, N_SAMPLES,
+                                          &t2_sys_offset,
+                                          &t2_sys_ts, &t2_delay)) {
+                               return -1;
+                      }
+
+                       sys_diff = t2_sys_ts - t1_sys_ts;
+                       clk_diff = t2_sys_offset - t1_sys_offset;
+                       ppb += (1e9 - ppb) * ((double)clk_diff / 
(double)sys_diff);
+               } else {
+                       /* if sysoff is not available - try gettime */
+                       if (clock_gettime(clkid, &t1_clk)) {
+                               pr_err("get: failed to get clock time: %s",
+                                       strerror(errno));
+                               return -1;
+                       }
+                       clock_gettime(CLOCK_REALTIME, &t1_sys);
+
+                       sleep(1);
+
+                       if (clock_gettime(clkid, &t2_clk)) {
+                               pr_err("get: failed to get clock time: %s",
+                                      strerror(errno));
+                               return -1;
+                       }
+                       clock_gettime(CLOCK_REALTIME, &t2_sys);
+
+                       clk_diff = NSEC2SEC * (t2_clk.tv_sec - t1_clk.tv_sec) +
+                                  (t2_clk.tv_nsec - t1_clk.tv_nsec);
+                       sys_diff = NSEC2SEC * (t2_sys.tv_sec - t1_sys.tv_sec) +
+                                  (t2_sys.tv_nsec - t1_sys.tv_nsec);
+                       ppb += (1e9 - ppb) * (1 - (double)clk_diff / 
(double)sys_diff);
+               }
+       } else {
+               /* parse the double ppb argument */
+               r = get_ranged_double(cmdv[0], &ppb, -NSEC2SEC, NSEC2SEC);
+               switch (r) {
+               case PARSED_OK:
+                       break;
+               case MALFORMED:
+                       pr_err("freq: '%s' is not a valid double", cmdv[0]);
+                       return -2;
+               case OUT_OF_RANGE:
+                       pr_err("freq: '%s' is out of range.", cmdv[0]);
+                       return -2;
+               default:
+                       pr_err("freq: couldn't process '%s'", cmdv[0]);
+                       return -2;
+               }
+       }
        clockadj_set_freq(clkid, ppb);
        pr_err("adjusted clock frequency offset to %lfppb", ppb);
 
@@ -326,8 +380,6 @@ static int do_cmp(clockid_t clkid, int cmdc, char *cmdv[])
        uint64_t sys_ts;
        int method, fd;
 
-#define N_SAMPLES 9
-
        fd = CLOCKID_TO_FD(clkid);
 
        method = sysoff_probe(fd, N_SAMPLES);
-- 
2.34.1



_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to