/*
* ntpshm.c - put time information in SHM segment for xntpd
* struct shmTime and getShmTime from file in the xntp distribution:
* sht.c - Testprogram for shared memory refclock
*/
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "gpsd.h"
#ifdef NTPSHM_ENABLE
#if defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#endif
#include <sys/ipc.h>
#include <sys/shm.h>
#define PPS_MAX_OFFSET 100000 /* microseconds the PPS can 'pull' */
#define PUT_MAX_OFFSET 500000 /* microseconds for lost lock */
#define NTPD_BASE 0x4e545030 /* "NTP0" */
#define SHM_UNIT 0 /* SHM driver unit number (0..3) */
struct shmTime {
int mode; /* 0 - if valid set
* use values,
* clear valid
* 1 - if valid set
* if count before and after read of values is equal,
* use values
* clear valid
*/
int count;
time_t clockTimeStampSec;
int clockTimeStampUSec;
time_t receiveTimeStampSec;
long receiveTimeStampUSec;
int leap;
int precision;
int nsamples;
int valid;
int pad[10];
};
static /*@null@*/ struct shmTime *getShmTime(int unit)
{
int shmid=shmget ((key_t)(NTPD_BASE+unit),
sizeof (struct shmTime), IPC_CREAT|0644);
if (shmid == -1) {
gpsd_report(1, "shmget failed\n");
return NULL;
} else {
struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
/*@ -mustfreefresh */
if ((int)(long)p == -1) {
gpsd_report(1, "shmat failed\n");
return NULL;
}
return p;
/*@ +mustfreefresh */
}
}
int ntpshm_init(struct gps_context_t *context, bool enablepps)
/* attach all NTP SHM segments. called once at startup, while still root */
{
int i;
for (i = 0; i < NTPSHMSEGS; i++)
context->shmTime[i] = getShmTime(i);
memset(context->shmTimeInuse,0,sizeof(context->shmTimeInuse));
# ifdef PPS_ENABLE
context->shmTimePPS = enablepps;
# endif /* PPS_ENABLE */
return (int)enablepps;
}
int ntpshm_alloc(struct gps_context_t *context)
/* allocate NTP SHM segment. return its segment number, or -1 */
{
int i;
for (i = 0; i < NTPSHMSEGS; i++)
if (context->shmTime[i] != NULL && !context->shmTimeInuse[i]) {
context->shmTimeInuse[i] = true;
memset((void *)context->shmTime[i],0,sizeof(struct shmTime));
context->shmTime[i]->mode = 1;
context->shmTime[i]->precision = -1; /* initially 0.5 sec */
context->shmTime[i]->nsamples = 3; /* stages of median filter */
return i;
}
return -1;
}
bool ntpshm_free(struct gps_context_t *context, int segment)
/* free NTP SHM segment */
{
if (segment < 0 || segment >= NTPSHMSEGS)
return false;
context->shmTimeInuse[segment] = false;
return true;
}
int ntpshm_put(struct gps_device_t *session, double fixtime)
/* put a received fix time into shared memory for NTP */
{
struct shmTime *shmTime = NULL;
struct timeval tv;
double seconds,microseconds;
if (session->shmTime < 0 ||
(shmTime = session->context->shmTime[session->shmTime]) == NULL)
return 0;
(void)gettimeofday(&tv,NULL);
microseconds = 1000000.0 * modf(fixtime,&seconds);
shmTime->count++;
shmTime->clockTimeStampSec = (time_t)seconds;
shmTime->clockTimeStampUSec = (int)microseconds;
shmTime->receiveTimeStampSec = (time_t)tv.tv_sec;
shmTime->receiveTimeStampUSec = tv.tv_usec;
/* setting the precision here does not seem to help anything, too
hard to calculate properly anyway. Let ntpd figure it out.
Any NMEA will be about -1 or -2.
Garmin GPS-18/USB is around -6 or -7.
*/
shmTime->count++;
shmTime->valid = 1;
return 1;
}
#ifdef PPS_ENABLE
/* put NTP shared memory info based on received PPS pulse */
int ntpshm_pps(struct gps_device_t *session, struct timeval *tv)
{
struct shmTime *shmTime = NULL, *shmTimeP = NULL;
time_t seconds;
double offset;
long l_offset;
if (session->shmTime < 0 || session->shmTimeP < 0 ||
(shmTime = session->context->shmTime[session->shmTime]) == NULL ||
(shmTimeP = session->context->shmTime[session->shmTimeP]) == NULL)
return 0;
/* check if received time messages are within locking range */
l_offset = shmTime->receiveTimeStampSec - shmTime->clockTimeStampSec;
l_offset *= 1000000;
l_offset += shmTime->receiveTimeStampUSec - shmTime->clockTimeStampUSec;
/*@ +ignorequals */
if (labs( l_offset ) > PUT_MAX_OFFSET) {
gpsd_report(5, "ntpshm_pps: not in locking range: %ld\n"
, (long)l_offset);
return -1;
}
/*@ -ignorequals */
if (tv->tv_usec < PPS_MAX_OFFSET) {
seconds = (time_t)tv->tv_sec;
offset = (double)tv->tv_usec / 1000000.0;
} else {
if (tv->tv_usec > (1000000 - PPS_MAX_OFFSET)) {
seconds = (time_t)(tv->tv_sec + 1);
offset = 1 - ((double)tv->tv_usec / 1000000.0);
} else {
shmTimeP->precision = -1; /* lost lock */
gpsd_report(2, "ntpshm_pps: lost PPS lock\n");
return -1;
}
}
shmTimeP->count++;
shmTimeP->clockTimeStampSec = seconds;
shmTimeP->clockTimeStampUSec = 0;
shmTimeP->receiveTimeStampSec = (time_t)tv->tv_sec;
shmTimeP->receiveTimeStampUSec = tv->tv_usec;
shmTimeP->precision = offset != 0 ? (int)(ceil(log(offset) / M_LN2)) : -20;
shmTimeP->count++;
shmTimeP->valid = 1;
gpsd_report(5, "ntpshm_pps: precision %d\n",shmTimeP->precision);
return 1;
}
#endif /* PPS_ENABLE */
#endif /* NTPSHM_ENABLE */
syntax highlighted by Code2HTML, v. 0.9.1