mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-10-03 12:18:10 +00:00
The MAC latches a hardware receive timestamp later than the frame reaches the wire, because of the PHY and the clock domain crossing. This fixed delay is the ingressLatency port parameter of IEEE 1588 and shows up as a constant phase error between the local and the master clock. Subtract the configured latency from every hardware receive timestamp in ptp_getrxtime(), the single place where they enter the daemon, so Sync, Delay_Resp and the peer delay messages are all corrected. - Add CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS (default 0, which applies no compensation). - Add the -I option to override it at run time. - Add ingress_latency_ns to struct ptpd_config_s. Software timestamps are not affected. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
290 lines
9.8 KiB
C
290 lines
9.8 KiB
C
/****************************************************************************
|
|
* apps/system/ptpd/ptpd_main.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
#include "netutils/ptpd.h"
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static int do_ptpd_start(FAR const struct ptpd_config_s *config)
|
|
{
|
|
int ret;
|
|
|
|
ret = ptpd_start(config);
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: ptpd_start() failed: %d\n", ret);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int do_ptpd_status(int pid)
|
|
{
|
|
struct ptpd_status_s status;
|
|
char buf[64];
|
|
struct tm time_tm;
|
|
struct timespec time_now;
|
|
int ret;
|
|
|
|
ret = ptpd_status(pid, &status);
|
|
if (ret != OK)
|
|
{
|
|
fprintf(stderr, "Failed to query PTPD status: %s\n", strerror(-ret));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("PTPD (PID %d) status:\n", pid);
|
|
printf("- clock_source_valid: %d\n", (int)status.clock_source_valid);
|
|
|
|
if (status.clock_source_valid)
|
|
{
|
|
printf("|- id: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
status.clock_source_info.id[0], status.clock_source_info.id[1],
|
|
status.clock_source_info.id[2], status.clock_source_info.id[3],
|
|
status.clock_source_info.id[4], status.clock_source_info.id[5],
|
|
status.clock_source_info.id[6], status.clock_source_info.id[7]
|
|
);
|
|
|
|
printf("|- utcoffset: %d\n", status.clock_source_info.utcoffset);
|
|
printf("|- priority1: %d\n", status.clock_source_info.priority1);
|
|
printf("|- class: %d\n", status.clock_source_info.clockclass);
|
|
printf("|- accuracy: %d\n", status.clock_source_info.accuracy);
|
|
printf("|- variance: %d\n", status.clock_source_info.variance);
|
|
printf("|- priority2: %d\n", status.clock_source_info.priority2);
|
|
|
|
printf("|- gm_id: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
status.clock_source_info.gm_id[0], status.clock_source_info.gm_id[1],
|
|
status.clock_source_info.gm_id[2], status.clock_source_info.gm_id[3],
|
|
status.clock_source_info.gm_id[4], status.clock_source_info.gm_id[5],
|
|
status.clock_source_info.gm_id[6], status.clock_source_info.gm_id[7]
|
|
);
|
|
|
|
printf("|- stepsremoved: %d\n", status.clock_source_info.stepsremoved);
|
|
printf("'- timesource: %d\n", status.clock_source_info.timesource);
|
|
}
|
|
|
|
gmtime_r(&status.last_clock_update.tv_sec, &time_tm);
|
|
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time_tm);
|
|
printf("- last_clock_update: %s.%09ld\n",
|
|
buf, status.last_clock_update.tv_nsec);
|
|
|
|
printf("- last_delta_ns: %lld\n", (long long)status.last_delta_ns);
|
|
printf("- last_adjtime_ns: %lld\n", (long long)status.last_adjtime_ns);
|
|
printf("- drift_ppb: %ld\n", status.drift_ppb);
|
|
printf("- path_delay_ns: %ld\n", status.path_delay_ns);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &time_now);
|
|
|
|
printf("- last_received_multicast: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_received_multicast.tv_sec));
|
|
printf("- last_received_announce: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_received_announce.tv_sec));
|
|
printf("- last_received_sync: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_received_sync.tv_sec));
|
|
printf("- last_transmitted_sync: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_transmitted_sync.tv_sec));
|
|
printf("- last_transmitted_announce: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_transmitted_announce.tv_sec));
|
|
printf("- last_transmitted_delayresp: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_transmitted_delayresp.tv_sec));
|
|
printf("- last_transmitted_delayreq: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_transmitted_delayreq.tv_sec));
|
|
printf("- last_transmitted_pdelayreq: %jd s ago\n",
|
|
(intmax_t)(time_now.tv_sec - status.last_transmitted_pdelayreq.tv_sec));
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int do_ptpd_stop(int pid)
|
|
{
|
|
int ret;
|
|
|
|
ret = ptpd_stop(pid);
|
|
|
|
if (ret == OK)
|
|
{
|
|
printf("Stopped ptpd\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to stop ptpd: %s\n", strerror(-ret));
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
static void usage(FAR const char *progname)
|
|
{
|
|
fprintf(stderr, "Usage: %s [options]: run this daemon in background.\n"
|
|
" -s client only synchronization mode\n"
|
|
" Network Transport:\n"
|
|
" -2 IEEE 802.3\n"
|
|
" -4 UDP IPV4 (default)\n"
|
|
" -6 UDP IPV6\n"
|
|
" Time Stamping:\n"
|
|
" -H HARDWARE (default) depends on NET_TIMESTAMP\n"
|
|
" -S SOFTWARE\n"
|
|
" -B The best master clock algorithm is used\n"
|
|
" -r synchronize system (realtime) clock\n"
|
|
" -E E2E, support client delay request-response\n"
|
|
" -P P2P, support peer delay request-response\n"
|
|
" -i [dev] interface device to use, for example 'eth0'\n"
|
|
" -p [dev] clock device to use\n"
|
|
" -I [ns] hardware RX timestamp latency to compensate\n"
|
|
" -t [pid] look the status of ptp daemon\n"
|
|
" -d [pid] stop ptp daemon\n",
|
|
progname);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* ptpd_main
|
|
****************************************************************************/
|
|
|
|
int main(int argc, FAR char *argv[])
|
|
{
|
|
struct ptpd_config_s config;
|
|
int option;
|
|
|
|
/* Default config for ptp daemon */
|
|
|
|
config.interface = "eth0";
|
|
config.clock = "realtime";
|
|
config.client_only = false;
|
|
config.delay_mechanism = PTP_DELAY_NONE;
|
|
#ifdef CONFIG_NET_TIMESTAMP
|
|
config.hardware_ts = true;
|
|
#else
|
|
config.hardware_ts = false;
|
|
#endif
|
|
config.bmca = false;
|
|
config.af = AF_INET;
|
|
config.ingress_latency_ns = CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS;
|
|
|
|
while ((option = getopt(argc, argv, "p:i:t:d:I:rs246BEHSP")) != ERROR)
|
|
{
|
|
switch (option)
|
|
{
|
|
case 's':
|
|
config.client_only = true;
|
|
break;
|
|
case 't':
|
|
return do_ptpd_status(atoi(optarg));
|
|
case 'd':
|
|
return do_ptpd_stop(atoi(optarg));
|
|
case '2':
|
|
config.af = AF_PACKET;
|
|
break;
|
|
case '4':
|
|
config.af = AF_INET;
|
|
break;
|
|
case '6':
|
|
config.af = AF_INET6;
|
|
break;
|
|
case 'B':
|
|
config.bmca = true;
|
|
break;
|
|
case 'E':
|
|
if (config.delay_mechanism != PTP_DELAY_NONE)
|
|
{
|
|
usage(argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
config.delay_mechanism = PTP_DELAY_E2E;
|
|
break;
|
|
case 'P':
|
|
if (config.delay_mechanism != PTP_DELAY_NONE)
|
|
{
|
|
usage(argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
config.delay_mechanism = PTP_DELAY_P2P;
|
|
break;
|
|
#ifdef CONFIG_NET_TIMESTAMP
|
|
case 'H':
|
|
config.hardware_ts = true;
|
|
break;
|
|
#endif
|
|
case 'S':
|
|
config.hardware_ts = false;
|
|
break;
|
|
case 'i':
|
|
config.interface = optarg;
|
|
break;
|
|
case 'p':
|
|
config.clock = optarg;
|
|
break;
|
|
case 'I':
|
|
config.ingress_latency_ns = atoi(optarg);
|
|
break;
|
|
case 'r':
|
|
config.clock = "realtime";
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_SCHED_TICKLESS
|
|
if (config.delay_mechanism == PTP_DELAY_P2P)
|
|
{
|
|
/* Without a tickless (hardware timer-backed) clock, clock_gettime()
|
|
* only advances once per CONFIG_USEC_PER_TICK scheduler tick, with
|
|
* no interpolation. The P2P peer delay formula subtracts two local
|
|
* timestamps (t1, t4) captured microseconds apart on a link this
|
|
* fast, which almost always fall inside the same tick: (t4 - t1)
|
|
* comes out exactly 0, or a full tick jump on the rare occasions a
|
|
* tick boundary falls in between. Either way path_delay_ns will be
|
|
* rejected as out of range and never converge.
|
|
*/
|
|
|
|
fprintf(stderr,
|
|
"WARNING: P2P (-P) selected without CONFIG_SCHED_TICKLESS. "
|
|
"path_delay_ns measurements require a tickless "
|
|
"(hardware timer-backed) clock and will likely never "
|
|
"converge on this build.\n");
|
|
}
|
|
#endif
|
|
|
|
return do_ptpd_start(&config);
|
|
}
|