/* * Copyright (c) 2007 Endace Technology Ltd, Hamilton, New Zealand. * All rights reserved. * * This source code is proprietary to Endace Technology Limited and no part * of it may be redistributed, published or disclosed except as outlined in * the written contract supplied with this product. * * DESCRITION: * DAG IRIG-B test program * * $Id: dag_irigb.c 14310 2011-06-17 01:52:35Z peter.thomas $ * */ #include #include #include #include #include #if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) #include #include #include #include #include #endif /* Endace headers. */ #include "dagapi.h" #include "dag_platform.h" #include "dagutil.h" #include "dagclarg.h" #include "dag_config_api.h" /* CVS Header. */ static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dag_irigb.c 14310 2011-06-17 01:52:35Z peter.thomas $"; static const char* const kRevisionString = "$Revision: 14310 $"; /*****************************************************************************/ /* Macros and constants */ /*****************************************************************************/ #define NTPD_BASE 0x4e545030 /* "NTP0" */ #define IRIGB_R(iom, offset) (*(volatile unsigned*)((uintptr_t)iom + offset)) #define IRIGB_W(iom, offset, value) ( *(volatile unsigned*) ((uintptr_t)iom + offset) = value) const uint16_t yday_regular[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; /* Leap years. */ const uint16_t yday_leap[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; /*****************************************************************************/ /* Data structures */ /*****************************************************************************/ typedef 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; /* external clock */ int clockTimeStampUSec; /* external clock */ time_t receiveTimeStampSec; /* internal clock, when external value was received */ int receiveTimeStampUSec; /* internal clock, when external value was received */ int leap; int precision; int nsamples; int valid; int pad[10]; }ntp_shm_segment_t; enum { IRIGB_config = 0x00, IRIGB_data = 0x04 }; /* Commandline argument codes. */ enum { CLA_HELP, CLA_VERBOSE, CLA_HOST, CLA_IRIGB, CLA_SET, CLA_DELTA, CLA_VERSION, CLA_DEVICE, CLA_DAEMON, CLA_INTERVAL, CLA_DELTA_HOUR, CLA_NTP_SHM_UNIT, CLA_DUCK_REFERENCE }; typedef struct irigb_auto { int pps; int irigb; }irigb_auto_t; typedef struct irigb_status { irigb_auto_t auto_detect; int pps; int irigb; }irigb_status_t; typedef struct { uint32_t is_host_year; uint32_t is_irigb_year; uint32_t is_set_irigb; int daemon; int interval; int32_t delta; int32_t delta_hour; int32_t ntp_shm_unit; uint8_t is_duck_reader_timestamp; }global_options_t; typedef struct _dag_unit { int dagfd; uint32_t irigb_base; uint8_t *iom; dag_card_ref_t ref_card ; attr_uuid_t attr_duck_reader; } dag_unit_t; static int open_irig(char *dagname, dag_unit_t *irig_dev); static int close_irig(dag_unit_t *irig_dev); static ntp_shm_segment_t* get_shm_time(int unit); void print_info(irigb_status_t *status, struct timespec *ts, struct timespec *host, struct tm *date, time_t now, time_t t); static int update_shm(ntp_shm_segment_t *shm_seg, struct timespec *ts_local, struct timespec *ts_refclk); static int get_time_from_duck_reader(dag_unit_t *in_dag, struct timespec *in_time ); /*****************************************************************************/ /* File-scope variables */ /*****************************************************************************/ static char dagname_buf[DAGNAME_BUFSIZE] = "dag0"; static char dagname[DAGNAME_BUFSIZE]; static global_options_t global_options; static int continue_run = 1; //#define IRIG_SIM #ifdef IRIG_SIM typedef struct sim_time { int32_t nsec; int32_t sec; } sim_time_t; typedef struct tag_irig_sim_data { uint32_t irig_frame[4]; sim_time_t irq_time; sim_time_t diff_irq_time; uint64_t last_tsc; int64_t diff_tsc; } irig_sim_data_t; irig_sim_data_t irig_sim; static int read_irig_sim_data(void) { FILE *irig_data_file; irig_data_file = fopen("irig_sim.dat", "rb"); if(irig_data_file == NULL) { return -1; } fread(&irig_sim, 1, sizeof(irig_sim_data_t), irig_data_file); fclose(irig_data_file); return 0; } #endif /* IRIG_SIM */ /*****************************************************************************/ /*** Functions ***/ /*****************************************************************************/ static void anysig(int sig) { continue_run = 0; } static void irigb_read(uint8_t *iom, uint32_t irigb_base, uint32_t *irigb_frame) { #ifndef IRIG_SIM int i; for (i = 0; i < 4; i ++) { IRIGB_W(iom, irigb_base + IRIGB_config, i << 8); usleep(1); irigb_frame[i] = IRIGB_R(iom, irigb_base + IRIGB_data); } #else /* IRIG_SIM */ memcpy(irigb_frame, irig_sim.irig_frame, sizeof(irig_sim.irig_frame)); #endif /* IRIG_SIM */ } /***************************************************************************** * Given the start bit and width of bits. it will return the bits in the array ****************************************************************************/ uint32_t select_bits(uint32_t *resp, int start, int size) { int mask = (size < 32 ? 1 << size : 0) - 1; int off = ((start) / 32); int shft = (start) & 31; uint32_t res; res = resp[off] >> shft; if (size + shft > 32) res |= resp[off+1] << ((32 - shft) % 32); return res & mask; } /*****************************************************************************/ static time_t irigb_mktime(struct tm *date) { if (date->tm_year < 70) return -1; else return (date->tm_sec + date->tm_min*60 + date->tm_hour*3600 + date->tm_yday*86400 + (date->tm_year-70)*31536000 + ((date->tm_year-69)/4)*86400 -((date->tm_year-1)/100)*86400 + ((date->tm_year+299)/400)*86400); } /*****************************************************************************/ static int calc_posix_year(time_t t_curr) { int year_approx; struct tm t; if(t_curr < 0) return -1; year_approx = t_curr/(366 * 86400); year_approx += 70; /* fill t with the beggining of the year */ memset(&t, 0, sizeof(struct tm)); t.tm_year = year_approx; /* make sure that t_beginning_of_the_year <= t_curr */ if(irigb_mktime(&t) <= t_curr) { while(1) { t.tm_year = year_approx + 1; if(irigb_mktime(&t) > t_curr) return year_approx; else year_approx++; } } return -1; } /*****************************************************************************/ static int irigb_decode(uint32_t *irigb_frame, struct tm *date) { uint32_t secs, mins, hours, days; uint32_t years; years = 0; memset(date, 0, sizeof(struct tm)); /* decode the IRIG-B time */ secs = select_bits(irigb_frame, 0, 4) + select_bits(irigb_frame, 5, 3) * 10; mins = select_bits(irigb_frame, 9, 4) + select_bits(irigb_frame, 14, 3) * 10; hours = select_bits(irigb_frame, 19, 4) + select_bits(irigb_frame, 24, 2) * 10; days = select_bits(irigb_frame, 29, 4) + select_bits(irigb_frame, 34, 4) * 10 + select_bits(irigb_frame, 39, 2) * 100; /* if the IRIG-B device sends the year information then read it from the device */ if (global_options.is_irigb_year) { years = select_bits(irigb_frame, 49, 4) + select_bits(irigb_frame, 54, 4) *10; if (!years) return -1; years = 100 + years; } else if (global_options.is_host_year) { years = date->tm_year; } else { return -1; } /* fill up break-down time structure */ date->tm_sec = secs; /* seconds */ date->tm_min = mins; /* minutes */ date->tm_hour = hours; /* hours */ date->tm_yday = days - 1; /* day in the year */ date->tm_year = years; /* year */ #if 0 printf("%d %d:%d:%d %d\n", days, hours, mins, secs, years); #endif return 0; } /*****************************************************************************/ static irigb_status_t irigb_status(uint8_t *iom, uint32_t irigb_base) { irigb_status_t status; int val; /* init status */ status.auto_detect.pps = 0; status.auto_detect.irigb = 0; status.pps = 0; status.irigb = 0; #ifndef IRIG_SIM val = IRIGB_R(iom, irigb_base + IRIGB_config); switch(val&0x3) { case 0x0: if ((val&0xC) == 0x4) { status.auto_detect.pps = 1; } else if((val&0xC) == 0x8) { status.auto_detect.irigb = 1; } break; case 0x1: status.pps = 1; break; case 0x2: status.irigb = 1; break; //case 0x3: default: dagutil_warning("Unknown source\n"); } #else /* IRIG_SIM */ if(read_irig_sim_data() != -1) status.irigb = 1; #endif /* IRIG_SIM */ return status; } /*****************************************************************************/ static void print_irigb_status(const irigb_status_t* in_status) { printf("INPUT SOURCE: "); if ( (1 == in_status->auto_detect.pps) || ( 1 == in_status->auto_detect.irigb)) { printf("Auto-Detect"); if( 1 == in_status->auto_detect.irigb) printf(" (IRIG-B)"); else if (1 == in_status->auto_detect.pps) printf(" (PPS)"); else printf(" (None) "); } else if ( 1 == in_status->pps ) { printf("Forced PPS"); } else if (1 == in_status->irigb) { printf("Forced IRIG-B"); } else { printf("None"); } printf("\n"); return; } /*****************************************************************************/ static uint32_t get_cpu_freq(void) { #if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) struct timeval before; struct timeval after; struct timeval diff; uint64_t tsc1 = 0; uint64_t tsc2 = 0; uint64_t tdiff; uint64_t temp; uint32_t thiscpu; gettimeofday(&before, NULL); dagutil_tsc64_read(&tsc1); usleep(10*1000); gettimeofday(&after, NULL); dagutil_tsc64_read(&tsc2); timersub(&after, &before, &diff); temp = (uint64_t) diff.tv_sec * 1000 * 1000 + diff.tv_usec; tdiff = tsc2 - tsc1; thiscpu = tdiff / temp; return thiscpu; #elif defined(_WIN32) LARGE_INTEGER freq; uint32_t thiscpu; QueryPerformanceFrequency(&freq); thiscpu = freq.LowPart/1000000; return thiscpu; #endif /* Platform-specific code. */ } /*****************************************************************************/ static uint32_t get_nsecond(int dagfd, uint32_t freq) { duckinf_t duckinf; struct timespec tp; uint64_t tsc_now; int64_t delta_tsc, res64; uint32_t res; duck_irq_time_t irq_time; #if defined(_WIN32) DWORD BytesTransfered = 0; #elif defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) int error; #endif memset(&duckinf, 0, sizeof(duckinf_t)); memset(&irq_time, 0, sizeof(duck_irq_time_t)); #if defined CLOCK_REALTIME && HAVE_CLOCK_SETTIME #if defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) #ifndef IRIG_SIM if((error = ioctl(dagfd, DAGIOCIRQTIME, &irq_time))) dagutil_panic("DAGIOCIRQTIME failed with %d\n", error); #endif /* IRIG_SIM */ clock_gettime(CLOCK_REALTIME, &tp); #ifdef IRIG_SIM #if 0 printf("%d %d %d %d\n", irig_sim.irq_time.sec, irig_sim.irq_time.nsec, irig_sim.diff_irq_time.sec, irig_sim.diff_irq_time.nsec); #endif if(irig_sim.diff_irq_time.sec || irig_sim.diff_irq_time.nsec) { irq_time.sec = tp.tv_sec + irig_sim.diff_irq_time.sec; if(irig_sim.diff_irq_time.nsec) { int32_t nsec_res; irq_time.sec = tp.tv_sec + irig_sim.diff_irq_time.sec; nsec_res = tp.tv_nsec + irig_sim.diff_irq_time.nsec; if(nsec_res < 0) { irq_time.sec += ((nsec_res / 1000000000) - 1); nsec_res = 1000000000 - (nsec_res % 1000000000); } else { irq_time.sec += (nsec_res / 1000000000); nsec_res = nsec_res % 1000000000; } irq_time.nsec = nsec_res; } else { irq_time.nsec = tp.tv_nsec; } } else { irq_time.sec = irig_sim.irq_time.sec; irq_time.nsec = irig_sim.irq_time.nsec; } #endif /* IRIG_SIM */ #ifndef NDEBUG printf("NOW nsecond:%ld.%09ld, duckinf.Last_NS:%ld.%09d\n", tp.tv_sec, tp.tv_nsec, irq_time.sec, irq_time.nsec); #endif if (irq_time.sec && irq_time.nsec) { res64 = (((uint64_t)tp.tv_sec - (uint64_t)irq_time.sec) * 1000 * 1000 * 1000 + (uint64_t)tp.tv_nsec) - (uint64_t)irq_time.nsec; if((res64 < (uint64_t)2000000000) && (res64 > 0)) return (uint32_t)res64; } #endif /* defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) */ #endif /* CLOCK_REALTIME && HAVE_CLOCK_SETTIME */ #if defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) #ifndef IRIG_SIM if((error = ioctl(dagfd, DAGIOCDUCK, &duckinf))) dagutil_panic("DAGIOCDUCK failed with %d\n", error); #endif /* IRIG_SIM */ dagutil_tsc64_read(&tsc_now); #ifdef IRIG_SIM if(irig_sim.diff_tsc) duckinf.Last_TSC = tsc_now + irig_sim.diff_tsc; else duckinf.Last_TSC = irig_sim.last_tsc; #endif /* IRIG_SIM */ #elif defined (_WIN32) if(DeviceIoControl(dag_gethandle(dagfd), IOCTL_GET_DUCKINFO, &duckinf, sizeof(duckinf_t), &duckinf, sizeof(duckinf_t), &BytesTransfered, NULL) == FALSE) panic("DeviceIoControl IOCTL_GET_DUCKINFO: %s\n",strerror(errno)); dagutil_tsc64_read(&tsc_now); #endif /* defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) */ delta_tsc = tsc_now - duckinf.Last_TSC; res64 = (delta_tsc * 1000) / (int64_t)freq; if((res64 < (uint64_t)2000000000) && (res64 > 0)) res = (uint32_t)res64; else res = 0xFFFFFFFF; #ifndef NDEBUG printf("NOW tsc:%"PRIu64", duckinf.Last_TSC:%"PRIu64", res:%d, freq:%d\n", tsc_now, duckinf.Last_TSC, res, freq ); #endif return res; } /*****************************************************************************/ static int set_time(struct timespec *ts) { #if defined CLOCK_REALTIME && HAVE_CLOCK_SETTIME int r = clock_settime (CLOCK_REALTIME, ts); if (r == 0 || errno == EPERM) return r; #endif /* CLOCK_REALTIME && HAVE_CLOCK_SETTIME */ #if HAVE_SETTIMEOFDAY struct timeval tv; tv.tv_sec = ts->tv_sec; tv.tv_usec = ts->tv_nsec / 1000; return settimeofday(&tv, 0); #endif /* HAVE_SETTIMEOFDAY */ errno = ENOSYS; return -1; } /*****************************************************************************/ static int get_time(struct timespec *ts) { #if defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME return clock_gettime (CLOCK_REALTIME, ts); #endif /* CLOCK_REALTIME && HAVE_CLOCK_GETTIME */ #if HAVE_GETTIMEOFDAY { int ret_val; struct timeval tv; ret_val = gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; return ret_val; } #endif /* HAVE_GETTIMEOFDAY */ errno = ENOSYS; return -1; } /*****************************************************************************/ static void init_global_options(void) { global_options.is_host_year = 1; global_options.is_irigb_year = 0; global_options.is_set_irigb = 0; global_options.delta = 0; global_options.delta_hour = 0; global_options.daemon = 0; global_options.interval = 60; global_options.ntp_shm_unit = -1; global_options.is_duck_reader_timestamp = 0 ; } /*****************************************************************************/ static void print_version(void) { printf("dag_irigb (DAG %s) %s\n", kDagReleaseVersion, kRevisionString); } /*****************************************************************************/ static void print_usage(ClArgPtr clarg) { printf("%s - IRIG-B test program.\n", dagutil_get_progname()); printf("Usage: %s [options]\n", dagutil_get_progname()); dagclarg_display_usage(clarg, stdout); } /*****************************************************************************/ static int parse_commandline(int argc, char *argv[]) { ClArgPtr clarg = NULL; FILE *errorfile = NULL; int arg_index = 0; int clarg_result; int code; int dagstream = 0; int interval = 1; int ntp_shm_unit = -1; int32_t delta, delta_hour; delta = delta_hour = 0; init_global_options(); /* Set up the command line options. */ clarg = dagclarg_init(argc, (const char * const *)argv); dagclarg_add(clarg, "display help (this page)", "--help", 'h', CLA_HELP); dagclarg_add_long_option(clarg, CLA_HELP, "--usage"); dagclarg_add_short_option(clarg, CLA_HELP, '?'); dagclarg_add(clarg, "display version information", "--version", 'V', CLA_VERSION); dagclarg_add(clarg, "increase verbosity", "--verbose", 'v', CLA_VERBOSE); dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE); dagclarg_add(clarg, "year information provided by host (default)", "--host", 't', CLA_HOST); dagclarg_add(clarg, "year information provided by IRIG-B", "--irigb", 'i', CLA_IRIGB); dagclarg_add(clarg, "fork into background, run as daemon in Linux and update NTP shared memory unit", "--daemon", 'm', CLA_DAEMON); /* TODO Just commenting out the interval, as it is not used right now. For NTP daemon , 1 second interval is used*/ /* dagclarg_add_int (clarg, "interval to synchronize host system clock time in minutes (default 1 min)" , "--interval", 'l', "interval", &interval, CLA_INTERVAL); */ dagclarg_add(clarg, "set host time to IRIG-B time", "--set", 's', CLA_SET); dagclarg_add_int (clarg, "set the difference between UTC and the IRIG-B device time in second." , "--sec", 'b', "delta", &delta, CLA_DELTA); dagclarg_add_int (clarg, "set the difference between UTC and the IRIG-B device time in hours." , "--hour", 'r', "delta_hour", &delta_hour, CLA_DELTA_HOUR); dagclarg_add_int (clarg, "ntp shared memory-segment unit(0 - 3)to use" , "--ntp-shm-unit", 'u', "shm_unit", &ntp_shm_unit, CLA_NTP_SHM_UNIT); dagclarg_add(clarg, "use DUCK time as the ntp shared memory reference clock(default is IRIG-B time)", "--duck", 'D', CLA_DUCK_REFERENCE); clarg_result = dagclarg_parse(clarg, errorfile, &arg_index, &code); while (1 == clarg_result) { switch (code) { case CLA_HELP: print_usage(clarg); exit(EXIT_SUCCESS); break; case CLA_VERBOSE: dagutil_inc_verbosity(); errorfile = stderr; break; case CLA_VERSION: print_version(); exit(EXIT_SUCCESS); break; case CLA_HOST: global_options.is_host_year = 1; break; case CLA_IRIGB: global_options.is_irigb_year = 1; global_options.is_host_year = 0; break; case CLA_DAEMON: global_options.daemon = 1; break; case CLA_INTERVAL: global_options.interval *= interval; break; case CLA_SET: global_options.is_set_irigb = 1; break; case CLA_DELTA: global_options.delta = delta; break; case CLA_DELTA_HOUR: global_options.delta_hour = delta_hour; break; /* case CLA_NTP: global_options.ntp_mode = 1; break; */ case CLA_NTP_SHM_UNIT: global_options.ntp_shm_unit = ntp_shm_unit; break; case CLA_DUCK_REFERENCE: global_options.is_duck_reader_timestamp = 1; break; case CLA_DEVICE: break; default: if (argv[arg_index][0] == '-') { /* Unknown option. */ dagutil_error("unknown option %s\n", argv[arg_index]); print_usage(clarg); return EXIT_FAILURE; } break; } clarg_result = dagclarg_parse(clarg, errorfile, &arg_index, &code); } if (-1 == clarg_result) { if (arg_index < argc) { dagutil_error("while processing option %s\n", argv[arg_index]); } dagclarg_display_usage(clarg, stderr); return EXIT_FAILURE; } /* ClargPtr should no longer be necessary. */ dagclarg_dispose(clarg); if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream)) { dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno)); } return EXIT_SUCCESS; } static int validate_global_params(void) { if ( !global_options.daemon && global_options.is_duck_reader_timestamp) { dagutil_error("DUCK timestamp could be used only in NTP reference clock mode\n"); return EXIT_FAILURE; } if( (global_options.ntp_shm_unit < 0) || (global_options.ntp_shm_unit > 3) ) { if ( global_options.daemon) { dagutil_error("Invalid NTP shared memory-segment unit.\n"); return EXIT_FAILURE; } } if ( (global_options.ntp_shm_unit > 0) && !global_options.daemon) { dagutil_warning(" NTP shared memory-segment unit will be ignored\n"); } return EXIT_SUCCESS; } /*****************************************************************************/ int dagirigb_main(int argc, char *argv[]) { dag_unit_t dag; uint32_t frame[4]; irigb_status_t status; struct timespec ts, host; struct tm *date, date_stack; uint32_t freq; time_t t, now; date = &date_stack; /* initalize the dag unit*/ memset(&dag, 0, sizeof(dag_unit_t)); #ifndef IRIG_SIM if(open_irig(dagname, &dag)) return EXIT_FAILURE; status = irigb_status(dag.iom, dag.irigb_base); print_irigb_status(&status); if(!(status.auto_detect.irigb || status.irigb)) { close_irig(&dag); return EXIT_FAILURE; } #endif /* IRIG_SIM */ t = 0; freq = get_cpu_freq(); get_time(&host); /* subtract delta to approximate time for IRIG-B device */ global_options.delta += global_options.delta_hour * 3600; now = host.tv_sec - global_options.delta; irigb_read(dag.iom, dag.irigb_base, frame); if(irigb_decode(frame, date)) { dagutil_error("irigb_decode failed\n"); return EXIT_FAILURE; } if(global_options.is_host_year) { time_t t_irig, t_abs, t_abs_min; int host_year; int i_min, i; t_abs_min = (time_t)INT_MAX; i_min = INT_MAX; host_year = calc_posix_year(now); for(i = -1; i < 2; i++) { date->tm_year = host_year + i; t_irig = irigb_mktime(date); if(t_irig == -1) { return -3; } t_irig++; /* add +1 sec because the IRIG-B time stamp was received for the previous second */ t_abs = abs(now - t_irig); if(t_abs < t_abs_min) { i_min = i; t = t_irig; t_abs_min = t_abs; } } date->tm_year = host_year + i_min; } else { t = irigb_mktime(date); if(t == -1) { return -3; } t++; } if ((global_options.is_set_irigb) && (status.auto_detect.irigb || status.irigb)) { uint32_t nsec, nsec_sec; ts.tv_sec = t + global_options.delta; nsec = get_nsecond(dag.dagfd, freq); ts.tv_nsec = nsec % 1000000000; nsec_sec = nsec / 1000000000; if(nsec_sec > 1) { dagutil_error("get_nsecond returned %d\n", nsec_sec); return EXIT_FAILURE; } ts.tv_sec += nsec_sec; if (set_time (&ts) != 0) { dagutil_error("can't set time\n"); return EXIT_FAILURE; } } print_info(&status, &ts, &host, date, now, t); #ifndef IRIG_SIM /* closes the dag card */ close_irig(&dag); #endif /* IRIG_SIM */ return 0; } static int get_irigb_seconds(dag_unit_t *dag, time_t *result) { uint32_t frame[4]; struct timespec host; struct tm *date, date_stack; uint32_t freq; time_t t, now; *result = 0; date = &date_stack; t = 0; freq = get_cpu_freq(); get_time(&host); /* subtract delta to approximate time for IRIG-B device */ global_options.delta += global_options.delta_hour * 3600; now = host.tv_sec - global_options.delta; irigb_read(dag->iom, dag->irigb_base, frame); if(irigb_decode(frame, date)) { dagutil_verbose_level(2, "error : irigb_decode failed\n"); return EXIT_FAILURE; } if(global_options.is_host_year) { time_t t_irig, t_abs, t_abs_min; int host_year; int i_min, i; t_abs_min = (time_t)INT_MAX; i_min = INT_MAX; host_year = calc_posix_year(now); for(i = -1; i < 2; i++) { date->tm_year = host_year + i; t_irig = irigb_mktime(date); if(t_irig == -1) { dagutil_error("irigb_mktime failed\n"); return -3; } t_irig++; /* add +1 sec because the IRIG-B time stamp was received for the previous second */ t_abs = abs(now - t_irig); if(t_abs < t_abs_min) { i_min = i; t = t_irig; t_abs_min = t_abs; } } date->tm_year = host_year + i_min; } else { t = irigb_mktime(date); if(t == -1) { dagutil_error("irigb_mktime failed\n"); return -3; } t++; } *result = t; return 0; } void print_info(irigb_status_t *status, struct timespec *ts, struct timespec *host, struct tm *date, time_t now, time_t t) { if(!global_options.daemon) { /*TODO:added a function to display the time.*/ printf("HOST:LOCAL:\t\t%s", asctime(localtime(&(host->tv_sec)))); printf("HOST:UTC :\t\t%s", asctime(gmtime(&(host->tv_sec)))); if (status->auto_detect.irigb || status->irigb) { if (global_options.is_irigb_year) { /* tm_year = 100 + irig year = years since 1900, tm_yday = DoY - 1 */ printf("IRIG-B :\t\t %04d %03d %02d:%02d:%02d + 1 sec\n", (date->tm_year + 1900), (date->tm_yday + 1), date->tm_hour, date->tm_min, date->tm_sec); } else { printf("IRIG-B :\t\t %03d %02d:%02d:%02d + 1 sec\n", (date->tm_yday + 1), date->tm_hour, date->tm_min, date->tm_sec); } printf("HOST-IRIG :\t\t%d sec\n", (int)(now - t)); } if ((global_options.is_set_irigb) && (status->auto_detect.irigb || status->irigb)) { printf("HOST:LOCAL:\t\t%s", asctime(localtime(&(ts->tv_sec)))); printf("HOST:UTC :\t\t%s", asctime(gmtime(&(ts->tv_sec)))); } } } static int detach_ntp_shm(ntp_shm_segment_t* shared_seg) { return shmdt((void*)shared_seg); } /*****************************************************************************/ int dagirigb_daemon_main() { #if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__) int temp; ntp_shm_segment_t * ntp_shm; dag_unit_t dag; irigb_status_t status; struct timespec ts_local; struct timespec ts_refclk; duck_irq_time_t irq_time; time_t t_irig; uint8_t run_status = 1; /* 0 = error occured. used to avoid too many log messages*/ struct timeval now;/* to be used for duck reader mode to get system time*/ /* initialize dag unit*/ memset(&dag, 0, sizeof(dag_unit_t)); memset(&now, 0, sizeof(now)); /* open device */ if(open_irig(dagname, &dag)) { dagutil_error("Can not start the daemon\n"); return EXIT_FAILURE; } /* open shm unit */ ntp_shm = get_shm_time(global_options.ntp_shm_unit); if(ntp_shm == NULL) { dagutil_error("Can not start the daemon\n"); return EXIT_FAILURE; } /* inits successful . Now fork into bg */ dagutil_verbose_level(0, "Running as daemon: reading %s time from %s, writing to ntp 127.127.28.%u\n",((global_options.is_duck_reader_timestamp)?"duck":"irig-b"), dagname, global_options.ntp_shm_unit); temp = daemon(0,0); dagutil_daemon(LOG_PID | LOG_CONS, LOG_DAEMON); dagutil_verbose_level(0,"start: reading %s time from %s, writing to ntp 127.127.28.%u\n",((global_options.is_duck_reader_timestamp)?"duck":"irig-b"), dagname, global_options.ntp_shm_unit); while(continue_run) { /* sleep 1 second TODO make this time customizable */ sleep(1);//global_options.interval * 1000 ); if ( global_options.is_duck_reader_timestamp) { if ( get_time_from_duck_reader(&dag, &ts_refclk )) { if (run_status) { dagutil_error("duck_timestamp read from %s failed \n",dagname); } run_status = 0; /* indicate error */ continue; } /* get system time */ (void)gettimeofday(&now, NULL); TIMEVAL_TO_TIMESPEC(&now, &ts_local); } else { status = irigb_status(dag.iom, dag.irigb_base); if(!(status.auto_detect.irigb || status.irigb)) { if (run_status) { dagutil_error("no irig-b signal detected on %s\n",dagname); run_status = 0;/* indicate error */ } continue; } if(get_irigb_seconds(&dag, &t_irig)) { if (run_status) { dagutil_error("irig-b time decode failed on %s\n",dagname); run_status = 0;/* indicate error */ } continue; } ts_refclk.tv_sec = t_irig + global_options.delta; ts_refclk.tv_nsec = 0; /* get interrupt system time */ if(ioctl(dag.dagfd, DAGIOCIRQTIME, &irq_time)) { if (run_status) { dagutil_error("DAGIOCIRQTIME failed on %s\n",dagname); run_status = 0; /* indicate error */ } continue; } /* FIXME may be make sense to check if it same as the previous one */ ts_local.tv_sec = irq_time.sec; ts_local.tv_nsec = irq_time.nsec; } update_shm(ntp_shm, &ts_local, &ts_refclk); if(!run_status) { dagutil_verbose_level(0,"recovered previous error on %s. successfully updated 127.127.28.%u\n",dagname, global_options.ntp_shm_unit); run_status = 1; /* running successfully*/ } } /* close shm unit */ detach_ntp_shm(ntp_shm); /* closes the dag card */ close_irig(&dag); dagutil_verbose_level(0,"stop: write to ntp 127.127.28.%u from %s\n", global_options.ntp_shm_unit, dagname); return EXIT_SUCCESS; #else dagutil_error("Daemon mode is not supported\n"); return EXIT_FAILURE; #endif } int main(int argc, const char* const *argv) { int retval; dagutil_set_progname("dag_irigb"); if ( (retval = parse_commandline(argc, (char **) argv)) != EXIT_SUCCESS) { return retval; } if ( (retval = validate_global_params()) != EXIT_SUCCESS ) { return retval; } /* connect the signal handler */ dagutil_set_signal_handler(anysig); if (global_options.daemon) { return dagirigb_daemon_main(); } else { return dagirigb_main(argc, (char **) argv); } return 0; } static ntp_shm_segment_t* get_shm_time(int unit) { #if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) int shmid; unsigned int perms; /* set the SHM perms */ if (unit < 2) { /* root-only access for unit 0 and 1 */ perms = 0600; } else { /* world access for unit 2 and 3*/ perms = 0666; } shmid = shmget((key_t) (NTPD_BASE + unit), sizeof(ntp_shm_segment_t), (int)(IPC_CREAT | perms)); if (shmid == -1) { dagutil_error("NTPD shmget for unit %d (perms: %o) failed: %s\n", unit, (int)perms, strerror(errno)); return NULL; } else { ntp_shm_segment_t *p = (ntp_shm_segment_t *)shmat(shmid, 0, 0); if ((int)(long)p == -1) { dagutil_error("NTPD shmat for unit %d failed: %s\n", unit, strerror(errno)); return NULL; } dagutil_verbose_level(1, "NTPD shmat(%d,0,0) succeeded for unit %d\n", shmid, unit); return p; } #else return NULL; #endif } /* change when usec resolution will be available */ static int update_shm(ntp_shm_segment_t *shm_seg, struct timespec *ts_local, struct timespec *ts_refclk) { int rv = 0; shm_seg->valid = 0; shm_seg->count++; /* timestamp from refclock */ shm_seg->clockTimeStampSec = (time_t) ts_refclk->tv_sec; shm_seg->clockTimeStampUSec = (int)(ts_refclk->tv_nsec / 1000); /* FIXME : usec*/ /* corresponding timestamp from local system clock */ shm_seg->receiveTimeStampSec = (time_t) ts_local->tv_sec; shm_seg->receiveTimeStampUSec = (int)(ts_local->tv_nsec / 1000); /* FIXME : usec*/ shm_seg->count++; shm_seg->valid = 1; return rv; } static int open_irig(char *dagname, dag_unit_t *irig_dev) { /* If duck time to be used , open duck reader, else initialize irig-b parameters*/ if (!global_options.is_duck_reader_timestamp) { int dagfd; dag_reg_t result[DAG_REG_MAX_ENTRIES]; unsigned regn; dag_reg_t *regs; uint8_t *iom; dagfd = dag_open(dagname); if (dagfd < 0) { dagutil_error("dag_open %s: %s\n", dagname, strerror(errno)); return -1; } iom = dag_iom(dagfd); if (iom == NULL) { dagutil_error("dag_irigb main(): dag_iom() failed: %s\n", strerror(errno)); return -1; } regs = dag_regs(dagfd); regn = 0; if ((dag_reg_table_find(regs, 0, DAG_REG_IRIGB, result, ®n)) || (!regn)) { dagutil_error("Dag device does not support IRIG-B functions\n"); return -1; } irig_dev->irigb_base = DAG_REG_ADDR(*result); irig_dev->dagfd = dagfd; irig_dev->iom = iom; } else //(global_options.is_duck_reader_timestamp) { /* initialize CS API parameter if duck reader to be used */ dag_component_t root_component = NULL; dag_component_t duck_component = NULL; irig_dev->ref_card = dag_config_init(dagname); if ( NULL == irig_dev->ref_card) { dagutil_error("dag_config_init failed\n"); return -1; } root_component = dag_config_get_root_component(irig_dev->ref_card); duck_component = dag_component_get_subcomponent(root_component, kComponentDUCK, 0); if ( NULL == duck_component) { dagutil_error("No duck component\n"); return -1; } irig_dev->attr_duck_reader = dag_component_get_attribute_uuid(duck_component, kUint64AttributeDuckTimestamp); if ( kNullAttributeUuid == irig_dev->attr_duck_reader) { dagutil_error("No duck timestamp attribute\n"); return -1; } } return 0; } static int close_irig(dag_unit_t *irig_dev) { dag_close(irig_dev->dagfd); if ( irig_dev->ref_card) { irig_dev->attr_duck_reader = kNullAttributeUuid; dag_config_dispose(irig_dev->ref_card); irig_dev->ref_card = NULL; } return 0; } int get_time_from_duck_reader(dag_unit_t *irig_dev, struct timespec *in_time ) { if ( irig_dev->attr_duck_reader) { uint64_t value_64_read = 0; value_64_read = dag_config_get_uint64_attribute(irig_dev->ref_card, irig_dev->attr_duck_reader); in_time->tv_sec = value_64_read >> 32; value_64_read = ((value_64_read & 0xffffffffULL) * 1000 * 1000 * 1000); value_64_read += (value_64_read & 0x80000000ULL) << 1; /* rounding */ in_time->tv_nsec = value_64_read >> 32; if ( in_time->tv_nsec >= 1000000000) { in_time->tv_nsec -= 1000000000; in_time->tv_sec += 1; } return 0; } return -1; } .