diff --git a/src/drv/CMakeLists.txt b/src/drv/CMakeLists.txt index 254d285c56ebc9506390f99c75034b1380a067b2..a7bac44ce97c2c4b241b03fecd818627efc5f2e4 100644 --- a/src/drv/CMakeLists.txt +++ b/src/drv/CMakeLists.txt @@ -9,4 +9,8 @@ add_library(driver::shmem ALIAS shmem) add_library(ini_parsing STATIC param.c crc.c) target_include_directories(ini_parsing PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) -add_library(driver::ini_parsing ALIAS ini_parsing) \ No newline at end of file +add_library(driver::ini_parsing ALIAS ini_parsing) + +add_library(gpsclock STATIC gpsclock.c) +target_include_directories(gpsclock PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) +add_library(driver::gpsclock ALIAS gpsclock) diff --git a/src/drv/gpsclock.c b/src/drv/gpsclock.c new file mode 100644 index 0000000000000000000000000000000000000000..7fcf928e465ab22aeff6297e7316cdd0028cc8a1 --- /dev/null +++ b/src/drv/gpsclock.c @@ -0,0 +1,84 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include "drv/gpsclock.h" + +// GPS offset from system time (depends on number of elapsed leap seconds) +static long gpsoffset; + +static int initdone = 0; + +int gpsclock_init(void) { + int status; + status = gpsclock_offset(&gpsoffset); + initdone = 1; + return status; +} + +int gpsclock_offset(long *offset) { + // TAI-UTC offset in seconds as of 1972 + const long INITIAL_TAI_OFFSET = 10; + // GPS-TAI offset in seconds + const long GPS_TAI_OFFSET = -19; + + char *savetz; + time_t now, gpszero, posixutc, rightutc; + struct tm now_tm, gpszero_tm = {.tm_year=80, .tm_mon=0, .tm_mday=6, .tm_hour=0, .tm_min=0, .tm_sec=0, .tm_wday=-1, .tm_yday=-1, .tm_isdst=-1}; + + // save TZ + savetz = getenv("TZ"); + if (savetz) { + setenv("GPSCLOCK_TZ_ORIG", savetz, 1); + } + // get current time in posix/UTC (no leap seconds) + setenv("TZ", "posix/UTC", 1); + tzset(); + now = time(0); + localtime_r(&now, &now_tm); + posixutc = mktime(&now_tm); + // get UTC time at GPS zero + gpszero = mktime(&gpszero_tm); + // get current time in right/UTC (includes leap seconds) + setenv("TZ", "right/UTC", 1); + tzset(); + rightutc = mktime(&now_tm); + // restore TZ + savetz = getenv("GPSCLOCK_TZ_ORIG"); + if (savetz) { + setenv("TZ", savetz, 1); + unsetenv("GPSCLOCK_TZ_ORIG"); + } else { + unsetenv("TZ"); + } + tzset(); + *offset = rightutc - posixutc; + if (*offset == 0) { + fprintf(stderr, "gpsclock: failed to set correct GPS time offset\n"); + return -1; + } + // add fixed offsets (TAI-UTC as of 1972 and GPS-TAI) + *offset += INITIAL_TAI_OFFSET + GPS_TAI_OFFSET; + // subtract gps zero + *offset -= gpszero; + + return 0; +} + +char *gpsclock_timestring(char *s, int size) { + struct timespec clk; + if (!initdone) { gpsclock_init(); } + clock_gettime(CLOCK_REALTIME, &clk); + snprintf(s, size, "%ld.%02ld\n", clk.tv_sec + gpsoffset, clk.tv_nsec/10000000L); + return s; +} + +void gpsclock_time(unsigned long *req) { + struct timespec clk; + if (!initdone) { gpsclock_init(); } + clock_gettime(CLOCK_REALTIME, &clk); + req[0] = clk.tv_sec + gpsoffset; + req[1] = clk.tv_nsec / 1000; + req[2] = clk.tv_nsec % 1000; +} diff --git a/src/epics/seq/CMakeLists.txt b/src/epics/seq/CMakeLists.txt index 67356bb0f9252196cf152fad1719c49ce9de6389..1b75be152417cbcd099cb5e8d38e6ba894596144 100644 --- a/src/epics/seq/CMakeLists.txt +++ b/src/epics/seq/CMakeLists.txt @@ -17,7 +17,8 @@ target_link_libraries(standalone_edc PUBLIC driver::ini_parsing pv::simple_pv ${Boost_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT}) + ${CMAKE_THREAD_LIBS_INIT} + driver::gpsclock) target_requires_cpp11(standalone_edc PUBLIC) configure_file(test/epics_test.py ${CMAKE_CURRENT_BINARY_DIR}/epics_test.py COPYONLY) diff --git a/src/epics/seq/gps.hh b/src/epics/seq/gps.hh index 9f99ab9a28f1383bf31778e4bbfa3b2b8038d0bd..29bc6f4eaf07d02ad9fcb2f300c06a5126c4365c 100644 --- a/src/epics/seq/gps.hh +++ b/src/epics/seq/gps.hh @@ -15,6 +15,7 @@ #include <unistd.h> +#include <drv/gpsclock.h> /** * Operations on GPS time. @@ -107,7 +108,13 @@ namespace GPS { public: explicit gps_clock(int offset):fd_(open ("/dev/gpstime", O_RDWR | O_SYNC)), offset_(offset), - ok_(gps_clock::symm_ok(fd_)) {} + ok_(gps_clock::symm_ok(fd_)) + { + if (!ok_) + { + gpsclock_init(); + } + } ~gps_clock() { if (fd_ >= 0) close(fd_); @@ -167,10 +174,10 @@ namespace GPS { } else { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - result.sec = ts.tv_sec; - result.nanosec = ts.tv_nsec; + unsigned long req[3]; + gpsclock_time(req); + result.sec = req[0] + offset_; + result.nanosec = req[1]*1000 + req[2]; } return result; } diff --git a/src/epics/seq/standalone_edcu.cc b/src/epics/seq/standalone_edcu.cc index b5230d741cc5847d7758819a5319c82b1d79f015..b5cb0ce529050efea8556e67e9fb30084ed8aac0 100644 --- a/src/epics/seq/standalone_edcu.cc +++ b/src/epics/seq/standalone_edcu.cc @@ -341,7 +341,7 @@ int nextTrig = 0; * @brief EdcuClock the clock for the standalone edc * @details This class abstracts the clock allowing multiple sources to be * selected from. Currently this will read form a mbuf (ie the time output of a - * LIGO model) or from the gpstime device. + * LIGO model) or from the gpstime device or from the system clock via gpsclock. */ class EdcuClock { @@ -676,59 +676,6 @@ void update_diag_info( diag_thread_queues& queues ); // End Header ************************************************************ // -// ************************************************************************** -/// Get current GPS time from the symmetricom IRIG-B card -unsigned long -symm_gps_time( unsigned long* frac, int* stt ) -{ - // ************************************************************************** - unsigned long t[ 3 ]; - ioctl( symmetricom_fd, IOCTL_SYMMETRICOM_TIME, &t ); - t[ 1 ] *= 1000; - t[ 1 ] += t[ 2 ]; - if ( frac ) - *frac = t[ 1 ]; - if ( stt ) - *stt = 0; - // return t[0] + daqd.symm_gps_offset; - return t[ 0 ]; -} - -// ************************************************************************** -/// See if the GPS card is locked. -int -symm_gps_ok( ) -{ - // ************************************************************************** - unsigned long req = 0; - ioctl( symmetricom_fd, IOCTL_SYMMETRICOM_STATUS, &req ); - printf( "Symmetricom status: %s\n", req ? "LOCKED" : "UNCLOCKED" ); - return req; -} - -// ************************************************************************** -unsigned long -symm_initialize( ) -// ************************************************************************** -{ - symmetricom_fd = open( "/dev/gpstime", O_RDWR | O_SYNC ); - if ( symmetricom_fd < 0 ) - { - perror( "/dev/gpstime" ); - exit( 1 ); - } - unsigned long gpsSec, gpsuSec; - int gpsx; - int gpssync; - gpssync = symm_gps_ok( ); - gpsSec = symm_gps_time( &gpsuSec, &gpsx ); - printf( "GPS SYNC = %d %d\n", gpssync, gpsx ); - printf( "GPS SEC = %ld USEC = %ld OTHER = %d\n", gpsSec, gpsuSec, gpsx ); - // Set system to start 2 sec from now. - gpsSec += 2; - return ( gpsSec ); -} - // ************************************************************************** void connectCallback( struct connection_handler_args args ) diff --git a/src/epics/util/feCodeGen.pl b/src/epics/util/feCodeGen.pl index 024ac4f9086d6f1b79963ceca8a5d23c309030ef..c9efd27d9c9bbb5e5376ab1188ec90b62df75c31 100755 --- a/src/epics/util/feCodeGen.pl +++ b/src/epics/util/feCodeGen.pl @@ -161,6 +161,7 @@ $daq_prefix="DC0"; $allBiquad = 1; $dac_dt_enable = 0; $internalclk = 0; +$userspacegps = 0; # Load model name without .mdl extension. $skeleton = $ARGV[1]; @@ -2395,6 +2396,10 @@ sub createEpicsMakefile { print OUTME "SRC += $rcg_src_dir/src/drv/crc.c\n"; print OUTME "SRC += $rcg_src_dir/src/drv/fmReadCoeff.c\n"; #print OUTME "SRC += src/epics/seq/get_local_time.st\n"; + if ($userspacegps) + { + print OUTME "SRC += $rcg_src_dir/src/drv/gpsclock.c\n"; + } for($ii=0;$ii<$useWd;$ii++) { print OUTME "SRC += src/epics/seq/hepiWatchdog"; @@ -2447,6 +2452,10 @@ sub createEpicsMakefile { { print OUTME "EXTRA_CFLAGS += -DFIR_FILTERS\n"; } + if ($userspacegps) + { + print OUTME "EXTRA_CFLAGS += -DUSE_GPSCLOCK\n"; + } print OUTME "include $rcg_src_dir/config/Makefile.linux\n"; print OUTME "\n"; print OUTME "build/\$(TARGET)/"; diff --git a/src/epics/util/lib/Parameters.pm b/src/epics/util/lib/Parameters.pm index c4475300eb66fcdad12c7fc3fe73fa02126de054..4c657246a3596dd3e3fc28d3ad193d5bc0e5cb52 100644 --- a/src/epics/util/lib/Parameters.pm +++ b/src/epics/util/lib/Parameters.pm @@ -307,6 +307,10 @@ sub parseParams { # Specify IPC rate if lower than model rate $::ipcrate = $spp[1]; } + case "userspacegps" + { + $::userspacegps = 1; + } # Following are old options that are no longer required case "biquad" { diff --git a/src/epics/util/lib/createUserMakefile.pm b/src/epics/util/lib/createUserMakefile.pm index 9cbe08e1b71f38a9180023a5c5624853a6c5ccb2..14da30f7dd2aa29ec063ff7d4e4bec734b82387d 100644 --- a/src/epics/util/lib/createUserMakefile.pm +++ b/src/epics/util/lib/createUserMakefile.pm @@ -169,6 +169,10 @@ if ($::::rfmDelay) { print OUTM "CFLAGS += -DUSER_SPACE=1\n"; print OUTM "CFLAGS += -fno-builtin-sincos\n"; +if ($::userspacegps) { + print OUTM "CFLAGS += -DUSE_GPSCLOCK\n"; +} + print OUTM "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/opt/DIS/lib64\n"; print OUTM "API_LIB_PATH=/opt/DIS/lib64\n"; print OUTM "\n\n"; @@ -188,10 +192,18 @@ print OUTM "LDFLAGS = -L \$(API_LIB_PATH) \n"; } print OUTM "TARGET=$::skeleton\n\n\n"; +if ($::userspacegps) { +print OUTM "$::skeleton: $::skeleton.o gpsclock.o rfm.o \n\n"; +} else { print OUTM "$::skeleton: $::skeleton.o rfm.o \n\n"; +} print OUTM "rfm.o: $::rcg_src_dir\/src/drv/rfm.c \n"; my $ccf = "\$\(CC\) \$\(CFLAGS\) \$\(CPPFLAGS\) \-c \$\< \-o \$\@"; print OUTM "\t$ccf \n"; +if ($::userspacegps) { +print OUTM "gpsclock.o: $::rcg_src_dir\/src/drv/gpsclock.c \n"; +print OUTM "\t$ccf \n"; +} print OUTM ".c.o: \n"; print OUTM "\t$ccf \n"; diff --git a/src/fe/controllerAppUser.c b/src/fe/controllerAppUser.c index 9d372b3044850578b455ce9231975f28fbf1f79c..9b073658c7d557119b410aee680dc773b4ce17c4 100644 --- a/src/fe/controllerAppUser.c +++ b/src/fe/controllerAppUser.c @@ -61,6 +61,24 @@ // #include "dolphin_usp.c" +#ifdef USE_GPSCLOCK +#include "drv/gpsclock.h" +#else +static void +gpsclock_timestring(char* line, int size) +{ + FILE* timef; + timef = fopen( "/sys/kernel/gpstime/time", "r" ); + if ( !timef ) + { + printf( "Cannot find GPS time \n" ); + return ( 0 ); + } + fgets( line, 100, timef ); + fclose(timef); +} +#endif + #define BILLION 1000000000L // Contec 64 input bits plus 64 output bits (Standard for aLIGO) @@ -93,21 +111,12 @@ struct timespec myTimer[ 2 ]; ///< Used in code cycle timing unsigned int getGpsTimeProc( ) { - FILE* timef; char line[ 100 ]; - int status; unsigned int mytime; - timef = fopen( "/sys/kernel/gpstime/time", "r" ); - if ( !timef ) - { - printf( "Cannot find GPS time \n" ); - return ( 0 ); - } - fgets( line, 100, timef ); + gpsclock_timestring(line, 100); mytime = atoi( line ); printf( "GPS TIME is %d\n", mytime ); - fclose( timef ); return ( mytime ); } @@ -410,10 +419,6 @@ fe_start_app_user( ) timeSec--; onePpsTime = cycleNum; - // timeSec = current_time() -1; - timeSec = ioMemData->gpsSecond; - timeSec--; - // timeSec = ioMemData->iodata[0][0].timeSec; printf( "Using local GPS time %d \n", timeSec ); pLocalEpics->epicsOutput.fe_status = NORMAL_RUN; diff --git a/src/fe/controllerIopUser.c b/src/fe/controllerIopUser.c index 55c80b1e504f46a9cbabca4299711f2f90adac19..0fd131a3e6190a9cfe1bc3d43e67ff53dc4f011b 100644 --- a/src/fe/controllerIopUser.c +++ b/src/fe/controllerIopUser.c @@ -61,6 +61,24 @@ #include "dolphin.c" #endif +#ifdef USE_GPSCLOCK +#include "drv/gpsclock.h" +#else +static void +gpsclock_timestring(char* line, int size) +{ + FILE* timef; + timef = fopen( "/sys/kernel/gpstime/time", "r" ); + if ( !timef ) + { + printf( "Cannot find GPS time \n" ); + return ( 0 ); + } + fgets( line, 100, timef ); + fclose(timef); +} +#endif + #define BILLION 1000000000L // Contec 64 input bits plus 64 output bits (Standard for aLIGO) @@ -98,22 +116,12 @@ int getGpsTime( unsigned int* tsyncSec, unsigned int* tsyncUsec ); unsigned int getGpsTimeProc( ) { - FILE* timef; char line[ 100 ]; - int status; unsigned int mytime; - timef = fopen( "/sys/kernel/gpstime/time", "r" ); - if ( !timef ) - { - printf( "Cannot find GPS time \n" ); - return ( 0 ); - } - fgets( line, 100, timef ); + gpsclock_timestring(line, 100); mytime = atoi( line ); printf( "GPS TIME is %d\n", mytime ); - fclose( timef ); - mytime -= 1; return ( mytime ); } void @@ -493,10 +501,8 @@ fe_start_iop_user( ) } while (status != 0 || myTimer[0].tv_nsec > 13000); /// Begin the first cycle at the start of the next second + 13 us clock_gettime(CLOCK_MONOTONIC, &myTimer[1]); - timeSec = getGpsTimeProc() + 2; - timeoff = myTimer[1].tv_nsec + 13000 - myTimer[0].tv_nsec; - if (timeoff >= BILLION) { timeoff -= BILLION; myTimer[1].tv_sec++; } - nextstep = myTimer[1].tv_sec*BILLION + BILLION + timeoff; + timeSec = getGpsTimeProc(); // increments at the start of the first cycle + nextstep = myTimer[1].tv_sec*BILLION + myTimer[1].tv_nsec - myTimer[0].tv_nsec + BILLION; clk = nextstep - cyclensec; startGpsTime = timeSec; pLocalEpics->epicsOutput.startgpstime = startGpsTime; diff --git a/src/include/drv/gpsclock.h b/src/include/drv/gpsclock.h new file mode 100644 index 0000000000000000000000000000000000000000..609d6c72d76f320a15ad0155030873f974f4e97e --- /dev/null +++ b/src/include/drv/gpsclock.h @@ -0,0 +1,30 @@ +#ifndef GPSCLOCK_INCLUDED +#define GPSCLOCK_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +// gpsclock_init: initialize library (return 0 if successful) +// currently this just calls gpsclock_offset +int gpsclock_init(void); + +// gpsclock_offset: store difference between GPS and system time in offset (return 0 if successful) +int gpsclock_offset(long *offset); + +// gpsclock_timestring: print gps time into string s with length size (return s) +// calls gpsclock_init if it has not already been called +char *gpsclock_timestring(char *s, int size); + +// gpsclock_time: store gps time in req +// req[0] : seconds +// req[1] : microseconds +// req[2] : nanoseconds +// calls gpsclock_init if it has not already been called +void gpsclock_time(unsigned long *req); + +#ifdef __cplusplus +} +#endif + +#endif // GPSCLOCK_INCLUDED diff --git a/src/local_dc/CMakeLists.txt b/src/local_dc/CMakeLists.txt index 0204b2bf653f3c99240092ffb94cad346a20c87b..020ce2666b7dfcfc69bc00a7c0b58335489e22cf 100644 --- a/src/local_dc/CMakeLists.txt +++ b/src/local_dc/CMakeLists.txt @@ -2,7 +2,8 @@ add_executable(local_dc local_dc.c ${CMAKE_CURRENT_SOURCE_DIR}/../drv/rfm.c) target_link_libraries(local_dc PUBLIC args - util) + util + driver::gpsclock) configure_file(test_local_dc.sh.in test_local_dc.sh @ONLY) configure_file(test_local_dc_stopped_model.sh.in test_local_dc_stopped_model.sh @ONLY) diff --git a/src/local_dc/local_dc.c b/src/local_dc/local_dc.c index dd5687abc6870198f0ca4bb784d5b2e45df06940..6a4ed11934ebfd4356bc25ff67e9fe886ae029c7 100644 --- a/src/local_dc/local_dc.c +++ b/src/local_dc/local_dc.c @@ -26,6 +26,7 @@ #include <pthread.h> #include "modelrate.h" #include "local_dc_utils.h" +#include "drv/gpsclock.h" #define MSG_BUF_SIZE sizeof( daq_dc_data_t ) @@ -60,11 +61,17 @@ int daqStatBit[ 2 ]; // ********************************************************************************************** /// Get current GPS time from the symmetricom IRIG-B card unsigned long -symm_gps_time( unsigned long* frac, int* stt ) -{ - unsigned long t[ 3 ]; +symm_gps_time( unsigned long* frac, int* stt ) { + unsigned long t[3]; - ioctl( symmetricom_fd, IOCTL_SYMMETRICOM_TIME, &t ); + if (symmetricom_fd >= 0) + { + ioctl(symmetricom_fd, IOCTL_SYMMETRICOM_TIME, &t); + } + else + { + gpsclock_time(t); + } t[ 1 ] *= 1000; t[ 1 ] += t[ 2 ]; if ( frac ) @@ -79,9 +86,13 @@ symm_gps_time( unsigned long* frac, int* stt ) int symm_ok( ) { - unsigned long req = 0; - ioctl( symmetricom_fd, IOCTL_SYMMETRICOM_STATUS, &req ); - fprintf( stderr, "Symmetricom status: %s\n", req ? "LOCKED" : "UNCLOCKED" ); + unsigned long req = 1; + if (symmetricom_fd >= 0) + { + req = 0; + ioctl(symmetricom_fd, IOCTL_SYMMETRICOM_STATUS, &req); + fprintf(stderr, "Symmetricom status: %s\n", req ? "LOCKED" : "UNCLOCKED"); + } return req; } @@ -598,8 +609,10 @@ int __CDECL symmetricom_fd = open( "/dev/gpstime", O_RDWR | O_SYNC ); if ( symmetricom_fd < 0 ) { - perror( "/dev/gpstime" ); - exit( 1 ); + if (gpsclock_init() < 0) { + perror("Unable to open /dev/gpstime or initialize the system based gpsclock"); + exit(1); + } } gps_ok = symm_ok( ); gps_time = symm_gps_time( &gps_frac, &gps_stt );