refclock_phc: add support for timestamping of external PPS

Add extpps driver option to the PHC refclock to enable external
timestamping of PPS signal and also options to configure the channel and
pin index. In this mode, the driver polling function accumulates samples
for hwclock, which is used to convert received timestamping events to
local time.
This commit is contained in:
Miroslav Lichvar 2017-05-05 16:07:34 +02:00
parent 4ba92bb6d6
commit eceb8d9937
3 changed files with 80 additions and 6 deletions

View file

@ -522,6 +522,12 @@ RCL_GetPrecision(RCL_Instance instance)
return instance->precision; return instance->precision;
} }
int
RCL_GetDriverPoll(RCL_Instance instance)
{
return instance->driver_poll;
}
static int static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time) valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
{ {

View file

@ -75,5 +75,6 @@ extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, doub
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time, extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction); double second, double dispersion, double raw_correction);
extern double RCL_GetPrecision(RCL_Instance instance); extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);
#endif #endif

View file

@ -34,22 +34,31 @@
#include "sysincl.h" #include "sysincl.h"
#include "refclock.h" #include "refclock.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "util.h" #include "util.h"
#include "sched.h"
#include "sys_linux.h" #include "sys_linux.h"
struct phc_instance { struct phc_instance {
int fd; int fd;
int mode; int mode;
int nocrossts; int nocrossts;
int extpps;
int pin;
int channel;
HCL_Instance clock;
}; };
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance) static int phc_initialise(RCL_Instance instance)
{ {
struct phc_instance *phc; struct phc_instance *phc;
int phc_fd; int phc_fd, rising_edge;
char *path; char *path, *s;
path = RCL_GetDriverParameter(instance); path = RCL_GetDriverParameter(instance);
@ -63,6 +72,25 @@ static int phc_initialise(RCL_Instance instance)
phc->fd = phc_fd; phc->fd = phc_fd;
phc->mode = 0; phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0; phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
RCL_SetDriverData(instance, phc); RCL_SetDriverData(instance, phc);
return 1; return 1;
@ -73,25 +101,64 @@ static void phc_finalise(RCL_Instance instance)
struct phc_instance *phc; struct phc_instance *phc;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
}
close(phc->fd); close(phc->fd);
Free(phc); Free(phc);
} }
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
}
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
static int phc_poll(RCL_Instance instance) static int phc_poll(RCL_Instance instance)
{ {
struct phc_instance *phc; struct phc_instance *phc;
struct timespec phc_ts, sys_ts; struct timespec phc_ts, sys_ts, local_ts;
double offset, err; double offset, phc_err, local_err;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance), if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &err)) &phc->mode, &phc_ts, &sys_ts, &phc_err))
return 0; return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0;
}
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts); offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, err); DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal); return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
} }