// // msf.c // // (C) Peter Talbot 2021 all rights reserved - www.pearbright.com // // A program to interpret the MSF time signal transmitted by the Anthorn VLF radio station // operated by the UK's National Physical Laboratory. This station transmits the time as // an amplitude-modulated 60KHz signal carrying data at a rate of two bits per second within // a series of 'words' each delineated by start and stop sequences and each lasting one // second, with a minute marker identifying the start of a 120-bit* data sequence. The // software assumes that a radio receiver presents the amplitude of the carrier signal as // the modem control signal Data Carrier Detect on an interface broadly conforming to ITU // V.24 and EIA RS-232 and connected to the computer as a USB Serial Port. In addition to // the DCD signal, the connected receiving equipment is assumed to present a signal on the // Data Set Ready circuit, indicating that the receiver is operational. // *excluding leaps - see https://www.npl.co.uk/msf-signal for full specification // // amend the following if your computer uses a different serial port // #define USB_SERIAL_PORT "/dev/ttyUSB0" // // if your clock inverts the signal // #define INVERTING 1 // // change the following to control debugging behaviours, set it to 0 for normal operation, // 1 for less verbose debugging and 2 for more verbose debugging // #define MYDEBUG 1 // // this is added to the two-digit year from MSF in order to make it equivalent to the // origin of 1900 used by C. So may need to be changed after the next millenium. // #define MYCENTURY 100 // // the number of seconds difference tolerated between local system time and MSF time // #define CLOCKCHANGE 10 // // assuming a second consists of up to four different signal levels, this is // what we need to hold a minute's worth of data // #define FOURBYONEMIN 264 // // all the following #defines are mathematically inter-related depending // on how often we sample the signal // // buffer sizes #define OVER2MINS 14400 #define MINUTELOW 5900 #define MINUTEHIGH 6100 // number of nanoseconds for each sample - 10000000 means 100 samples per second #define FREQNANOSEC 10000000 // // data is parsed assuming the buffer represents a minute of data which should be held in buf4 // as the run lengths of consecutive high or low values. // The following expected ideal values assume a sample rate of 100 samples per second: // +50 minute marker second half then sequences each second depending on the data content: // 11 -30, +70 // 10 -20, +80 // 01 -10, +10, -10, +70 // 00 -10, +90 // in practice the width of pulses varies due to signal distortion // // limits for detecting minute marker #define MINUTEMARKERHIGH 55 #define MINUTEMARKERLOW 45 #define MINUTEMARKERTRAILER 8 // limits for detecting second marker (possibly including data bits) #define SECONDMARKERHIGH 98 #define SECONDMARKERLOW 60 // if start and data bits more than the following width, assume bit A is 1 #define TWOBITS 15 // if start and data bits more than the following width, assume bit A and B both 1 #define THREEBITS 25 // for adjusting the window if synchronisation fails #define SKIPABIT 200 // // for logging - when the log file reaches a certain size it is renamed and // a new log file is created // #define LOGFILENAME "msf.log" #define SECONDLOGFILENAME "msf2.log" #define COMMAND1 "rm msf2.log" #define COMMAND2 "mv msf.log msf2.log" #define LOGFILELIMIT 1000000 #define LOGCOUNTLIMIT 1000 #define LOGBUFSIZE 200 // #include #include #include #include #include #include #include #include #include #include #include // // global variables // pthread_mutex_t lock; /* a semaphore used to lock access to the buffers */ int shutdown_flag; /* non-zero if ctrl-C is pressed or the kill signal is received */ int serial_port; /* the file handle for the serial port */ int serial; /* the returned status from ioctl() */ struct sigaction _sigact; /* signal structure for kill or break */ char msf; /* last level on DCD */ int internal_error; /* flag for errors other than from operating system */ int debug_counter; /* counter for debug function */ // // FREQNANOSEC/1000000 millisecond timer for sampling input // struct sigevent sev; struct itimerspec its; long freq_nanosecs; sigset_t mask; struct sigaction sa; timer_t timerid; // // buffers // char buf1[OVER2MINS]; /* Buffer size allows for over two minutes worth of MSF signal */ char buf2[OVER2MINS]; /* to be collected when looking for the minute marker. Data is */ int ptr1, ptr2, whichptr; /* alternately collected in these two buffers. Each second */ int currentlimit, skipcount; /* (except minute marker) has a maximum of four levels of */ int buf4[FOURBYONEMIN]; /* varying duration, a 100msec "start bit", two "data bits" and */ int ptr4; /* a 700msec "stop bit", so 264 elements is enough to store */ int databita[61]; /* 66 seconds. If both bit A and bit B are one then the start */ int databitb[61]; /* bit will appear to be 300msec in duration, and so on. */ // // for logging // int logcount; FILE *logfilehandle; char logbuffer[LOGBUFSIZE]; char timebuffer[LOGBUFSIZE]; int logmessage(char *message2log) { struct stat st; off_t logfilesize; long logfsize; int r; time_t t; struct tm *tm; if (logcount == 0) { logfilehandle = fopen(LOGFILENAME, "a"); } if (logcount == -1) { fclose(logfilehandle); } else { logcount++; t = time(NULL); tm = localtime(&t); r = sprintf(timebuffer, "%s", asctime(tm)); *strstr(timebuffer,"\n") = '\0'; fprintf(logfilehandle, "[%s] %s", timebuffer, message2log); if (logcount > LOGCOUNTLIMIT) { logcount = 1; // // check size of log file and create a new one if too large // r = stat(LOGFILENAME , &st); if (r == 0) { logfilesize = st.st_size; logfsize = logfilesize; if (logfsize > LOGFILELIMIT) { fclose(logfilehandle); logfilehandle = fopen(SECONDLOGFILENAME, "r"); if (NULL != logfilehandle) { fclose(logfilehandle); r = system(COMMAND1); } r = system(COMMAND2); logfilehandle = fopen(LOGFILENAME, "w"); } } } } } // // initialise the buffers // int bufferinit() { ptr1 = 0; ptr2 = 0; whichptr = 0; currentlimit = OVER2MINS; /* start with a sample in excess of two minutes, to find the minute marker */ skipcount = 0; return 0; } // // for parity checks // int oddoreven(int x) { int i, j, r; r = x % 2; return r; } // // check parity // int paritycheck(int afrom, int ato, int bparity) { int b, i, j, r; r = 0; j = 0; for (i = (afrom - 1); i < ato; i++) j = j + databita[i]; j = j + databitb[bparity - 1]; if (oddoreven(j) == 0) { if (MYDEBUG != 0) { sprintf(logbuffer, "[paritycheck] bits %iA through %iA failed parity check\n", afrom, ato); b = logmessage(logbuffer); printf(logbuffer); } r = -1; } return r; } // // parsetimesignal is called when the data bits have been assembled // int parsetimesignal() { int r, b, ibst, x; int iyear, imonth, iday, idow, ihour, iminute; struct tm mytime, mysystm; char timebuffer[80]; time_t mysystime, msftime; static struct timespec mytspec; double mydiff; r = 0; // // 52A and 59A must be zero // if (databita[51] == 0 && databita[58] == 0) { // // 53A through 58A must be 1 // if(databita[52] == 1 && databita[53] == 1 && databita[54] == 1 && databita[55] == 1 && databita[56] == 1 && databita[57] == 1) { r = paritycheck(17, 24, 54); if (r == 0) r = paritycheck(25, 35, 55); if (r == 0) r = paritycheck(36, 38, 56); if (r == 0) r = paritycheck(39, 51, 57); if (r == 0) { // // all parity checks passed // iyear = 80 * databita[16] + 40 * databita[17] + 20 * databita[18] + 10 * databita[19] + 8 * databita[20]; iyear = iyear + 4 * databita[21] + 2 * databita[22] + databita[23]; imonth = 10 * databita[24] + 8 * databita[25] + 4 * databita[26] + 2 * databita[27] + databita[28]; iday = 20 * databita[29] + 10 * databita[30] + 8 * databita[31] + 4 * databita[32] + 2 * databita[33] + databita[34]; idow = 4 * databita[35] + 2 * databita[36] + databita[37]; ihour = 20 * databita[38] + 10 * databita[39] + 8 * databita[40] + 4 * databita[41] + 2 * databita[42] + databita[43]; iminute = 40 * databita[44] + 20 * databita[45]; iminute = iminute + 10 * databita[46] + 8 * databita[47] + 4 * databita[48] + 2 * databita[49] + databita[50]; // // daylight saving time // ibst = databitb[57]; if (MYDEBUG != 0) { sprintf(logbuffer, "[parsetimesignal] time from MSF is %.2d/%.2d/%.2d %.2d:%.2d BST=%i\n", iday, imonth, iyear, ihour, iminute, ibst); b = logmessage(logbuffer); printf(logbuffer); } // // convert to C time formats // mytime.tm_year = MYCENTURY + iyear; mytime.tm_mon = imonth - 1; mytime.tm_mday = iday; mytime.tm_hour = ihour; mytime.tm_min = iminute; mytime.tm_sec = 0; mytime.tm_isdst = ibst; msftime = mktime(&mytime); if (msftime != -1) { mysystime = time(NULL); mysystm = *localtime(&mysystime); x = 0; if (MYDEBUG != 0) x = 1; mydiff = difftime(msftime, mysystime); if (mydiff < (0 - CLOCKCHANGE) || mydiff > CLOCKCHANGE) x = 2; if (x != 0) /* display and log these messages if MYDEBUG is set or if the MSF time is significantly different to the local system clock */ { strftime(timebuffer, 80, "%c", &mytime ); sprintf(logbuffer, "[parsetimesignal] time from MSF receiver is: %s DST=%i\n", timebuffer, mytime.tm_isdst); b = logmessage(logbuffer); printf(logbuffer); strftime(timebuffer, 80, "%c", &mysystm ); sprintf(logbuffer, "[parsetimesignal] current host system time is: %s DST=%i\n", timebuffer, mysystm.tm_isdst); b = logmessage(logbuffer); printf(logbuffer); if (x == 2) { sprintf(logbuffer, "[parsetimesignal] difference is %f\n", mydiff); b = logmessage(logbuffer); printf(logbuffer); // // program must have super-user privilege in order to alter the system clock // if (getuid() != 0) { sprintf(logbuffer, "[parsetimesignal] clock can only be set by root\n"); b = logmessage(logbuffer); printf(logbuffer); } else { mytspec.tv_sec = mktime(&mytime); mytspec.tv_nsec = 0; sprintf(logbuffer, "[parsetimesignal] attempting to set clock\n"); b = logmessage(logbuffer); printf(logbuffer); r = clock_settime(CLOCK_REALTIME, &mytspec); if (r != 0) { sprintf(logbuffer, "[parsetimesignal] failed to set system clock: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } else { sprintf(logbuffer, "[parsetimesignal] system clock set\n"); b = logmessage(logbuffer); printf(logbuffer); } } } } } else { if (MYDEBUG != 0) { sprintf(logbuffer, "[parsetimesignal] failed to convert to C tm structure\n"); b = logmessage(logbuffer); printf(logbuffer); } } } else { if (MYDEBUG != 0) { sprintf(logbuffer, "[parsetimesignal] failed parity check\n"); b = logmessage(logbuffer); printf(logbuffer); } } } else { if (MYDEBUG != 0) { strcpy(logbuffer, "[parsetimesignal] bits 53A through 58A not all 1\n"); b = logmessage(logbuffer); printf(logbuffer); } r = 1; } } else { if (MYDEBUG != 0) { strcpy(logbuffer, "[parsetimesignal] bits 52A and 59A not both 0\n"); b = logmessage(logbuffer); printf(logbuffer); } r = -1; } return r; } // // look for a series of similar bits in the buffer, each representing a data sample for the period FREQNANOSEC // int lookahead(char *bufpointer, char *bufferend, int *whatbit) { char *startpointer; char *endpointer; int r, i, k, b; startpointer = bufpointer; b = -1; r = 0; k = 0; if (bufpointer >= bufferend) { k = 1; } else { b = *bufpointer; *whatbit = b; r = 1; while (k == 0) { startpointer++; if (startpointer >= bufferend) { k = 1; } else { i = *startpointer; if (i == b) { r++; /* this bit same as last - increment run length */ } else { k = 1; /* bit is different, end run */ } } } } return r; } // // find the minute markers // int findminutemarkers(char *bufpointer, int *firstone, int *lastone) { char *ptra; char *ptrb; int r, b, bitcount, zeroorone; int marker1, marker2, mmwork; marker1 = -1; marker2 = -1; mmwork = 0; ptra = bufpointer; ptrb = ptra + currentlimit; zeroorone = 1; bitcount = 1; r = -1; ptr4 = -1; /* for smoothing message in lookahead */ while (ptra < ptrb && zeroorone >= 0 && bitcount != 0) { bitcount = lookahead(ptra, ptrb, &zeroorone); ptra = ptra + bitcount; mmwork = mmwork + bitcount; if (zeroorone == 0 && bitcount > MINUTEMARKERLOW && bitcount < MINUTEMARKERHIGH) { if (MYDEBUG != 0) { sprintf(logbuffer,"[findminutemarkers] found potential minute marker first half length %i ending at %i\n", bitcount, mmwork); b = logmessage(logbuffer); printf(logbuffer); } bitcount = lookahead(ptra, ptrb, &zeroorone); ptra = ptra + bitcount; mmwork = mmwork + bitcount; if (zeroorone == 1 && bitcount > MINUTEMARKERLOW && bitcount < MINUTEMARKERHIGH) { if (MYDEBUG != 0) { sprintf(logbuffer,"[findminutemarkers] found minute marker second half length %i ending at %i\n", bitcount, mmwork); b = logmessage(logbuffer); printf(logbuffer); } if (marker1 == -1) { marker1 = mmwork - bitcount; } else { if (marker2 == -1) marker2 = mmwork - bitcount; } } } if (marker1 != -1 && marker2 != -1) { zeroorone = -1; /* stop parsing */ mmwork = marker2 - marker1; if (mmwork < MINUTELOW || mmwork > MINUTEHIGH) { if (MYDEBUG != 0) { sprintf(logbuffer, "[findminutemarkers] calculated time between minute markers %i was out of range\n", mmwork); b = logmessage(logbuffer); printf(logbuffer); } } else { *firstone = marker1; *lastone = marker2; r = 0; } } } return r; } // // parsing at stage 2, the buffer of samples should now start with the minute marker; // convert to a series of numbers in buf4 each representing a series of similar samples // from lookahead, e.g. 000000000011111111111111111111 yields -10,20 // int parsebuffer(char *bufpointer) { char *ptra; char *ptrb; int r, b, zeroorone, bitcount, p1, p2; ptra = bufpointer; ptrb = ptra + currentlimit; zeroorone = 1; bitcount = 9999; ptr4 = 0; r = 0; while (ptra < ptrb && zeroorone >= 0 && bitcount != 0) { bitcount = lookahead(ptra, ptrb, &zeroorone); ptra = ptra + bitcount; if (ptr4 < FOURBYONEMIN) { if (zeroorone == 1) buf4[ptr4] = bitcount; if (zeroorone == 0) buf4[ptr4] = 0 - bitcount; ptr4++; } else { zeroorone = -1; /* stop parsing */ r = -1; if (MYDEBUG != 0) { sprintf(logbuffer, "[parsebuffer] sample exceeds length of one minute buffer (perhaps due to noise)\n"); b = logmessage(logbuffer); printf(logbuffer); } } } return r; } // // function to check the signal level // int getmodem() { // interrogate the serial port and return the status of DCD and DSR // return 0 if DSR and DCD are both off which implies the radio receiver is switched off // return 1 is DCD is on but DSR is off, which should not really happen unless the wiring is faulty // return 2 if DSR is on and DCD is off, which implies a 0 or 1 signal depending if inverted or not // return 3 if DSR is on and DCD is on, which implies a 1 or 0 signal depending if inverted or not // return -1 if an error occurred int r, x, b; x = 0; r = ioctl(serial_port, TIOCMGET, &serial); if (r != 0 ) { x = -1; internal_error = 1; shutdown_flag = 1; sprintf(logbuffer, "[getmodem] Error %i from ioctl: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } else { if (serial & TIOCM_DSR) x = 2; else x = 0; if (serial & TIOCM_CAR) x = x + 1; } return x; } // // spawned from timer_handler as a separate thread // static void *thread_start(void *arg) { int dcd; // // lock the buffers // pthread_mutex_lock(&lock); // // are we skipping? // if (skipcount != 0) { skipcount--; } else { // // check status of DCD, add sample to buffer // dcd = getmodem(); if (dcd < 2) { internal_error = 2; shutdown_flag = 1; } else { if (dcd == 3) /* you may have to change this if DSR is not used */ { msf = 1; if (INVERTING != 0) msf = 0; } else { msf = 0; if (INVERTING != 0) msf = 1; } if (whichptr == 0) { if (ptr1 < currentlimit) buf1[ptr1] = msf; ptr1++; if (ptr1 >= currentlimit) { whichptr = 1; ptr2 = 0; } } else { if (ptr2 < currentlimit) buf2[ptr2] = msf; ptr2++; if (ptr2 >= currentlimit) { whichptr = 0; ptr1 = 0; } } } } /* end if skipping */ // // release the semaphore // pthread_mutex_unlock(&lock); return 0; } // // timer handler - called every few msec to spawn the thread which interrogates the signal and fills a buffer // void timer_handler(int signum, siginfo_t *info, void *ptr) { int b, s; // // for timer_handler's separate thread // pthread_t thread_id; pthread_attr_t attr; pthread_attr_t *attrp; attrp = &attr; s = pthread_attr_init(&attr); if (s != 0) { sprintf(logbuffer, "[timer_handler] Error %i from pthread_attr_init: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (s != 0) { sprintf(logbuffer, "[timer_handler] Error %i from pthread_attr_setdetachstate: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } // // spawn the timer handler thread // s = pthread_create(&thread_id, attrp, &thread_start, NULL); if (s != 0) { sprintf(logbuffer, "[timer_handler] Error %i from pthread_create: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } if (attrp != NULL) { s = pthread_attr_destroy(attrp); if (s != 0) { sprintf(logbuffer, "[timer_handler] Error %i from pthread_attr_destroy: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } } } // // handler for break or kill signals // void sig_term_handler(int signum, siginfo_t *info, void *ptr) { shutdown_flag = 1; } // // function to initialise handlers for break or kill // void catch_sig() { int b; memset(&_sigact, 0, sizeof(_sigact)); _sigact.sa_sigaction = sig_term_handler; _sigact.sa_flags = SA_SIGINFO; if (sigaction(SIGINT, &_sigact, NULL) == -1) { sprintf(logbuffer, "[catch_sig] Error %i from sigaction [break]: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } if (sigaction(SIGTERM, &_sigact, NULL) == -1) { sprintf(logbuffer, "[catch_sig] Error %i from sigaction [kill]: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } } // // create or destroy a timer to sample the signal and fill the buffers // int msfsampler(int startorstop) { int b; if (startorstop == 0) { sprintf(logbuffer, "[msfsampler] initialising\n"); b = logmessage(logbuffer); printf(logbuffer); freq_nanosecs = FREQNANOSEC; // // create a timer to check the signals every few milliseconds // sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = timer_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGRTMIN, &sa, NULL) == -1) { sprintf(logbuffer, "Error %i from sigaction: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } // Block timer signal temporarily sigemptyset(&mask); sigaddset(&mask, SIGRTMIN); if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { sprintf(logbuffer, "Error %i from sigprocmask [blocking]: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGRTMIN; sev.sigev_value.sival_ptr = &timerid; if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) { sprintf(logbuffer, "Error %i from timer_create: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } its.it_value.tv_sec = 0; its.it_value.tv_nsec = freq_nanosecs; its.it_interval.tv_sec = its.it_value.tv_sec; its.it_interval.tv_nsec = its.it_value.tv_nsec; if (timer_settime(timerid, 0, &its, NULL) == -1) { sprintf(logbuffer, "Error %i from timer_settime: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) { sprintf(logbuffer, "Error %i from sigprocmask [unblocking]: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); } sprintf(logbuffer, "[msfsampler] initialisation complete\n"); b = logmessage(logbuffer); printf(logbuffer); } else { // // closing down - so block the timer // sigemptyset(&mask); sigaddset(&mask, SIGRTMIN); sigprocmask(SIG_SETMASK, &mask, NULL); // // delete the timer and exit. // timer_delete(timerid); } return 0; } // ============================================================= // // main process, which parses the buffers when they are full // // ============================================================= int main() { int b, i, j, x, oldmsf, syncstage, fullbuffer; char str[80]; char *bufreadptr; int msfa, ptr1a, ptr2a; int mm1, mm2, p1, p2, r; logcount = 0; debug_counter = 1000; oldmsf = -1; logcount = 0; b = logmessage("Started\n"); // // initialise buffers // i = bufferinit(); ptr4 = 0; if (pthread_mutex_init(&lock, NULL) != 0) { sprintf(logbuffer, "mutex init failed\n"); b = logmessage(logbuffer); printf(logbuffer); return 1; } // // in stage 1 we look for the 500ms minute marker, syncstage is incremented after buffers aligned // syncstage = 1; // // 6.5 millisecond timeout for use by nanosleep // struct timespec mytimer = { 0, 6500000 }; struct timespec myremains = { 0, 0 }; // // set up the handlers to intercept kill and int signals // shutdown_flag = 0; internal_error = 0; catch_sig(); // // open the serial port // serial_port = open(USB_SERIAL_PORT, O_NONBLOCK | O_RDWR | O_NOCTTY); // Check for errors if (serial_port < 0) { sprintf(logbuffer, "Error %i from opening %s: %s\n", errno, USB_SERIAL_PORT, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); shutdown_flag = 1; } else { sprintf(logbuffer, "[main] %s opened on %i\n", USB_SERIAL_PORT, serial_port); b = logmessage(logbuffer); printf(logbuffer); if (MYDEBUG != 0) { i = ioctl(serial_port, TIOCMGET, &serial); if (i != 0 ) printf("[main] Error %i from ioctl: %s\n", errno, strerror(errno)); else printf("[main] ioctl: %i\n",serial); i = getmodem(); printf("[main] getmodem returned %i\n",i); } // // start the timer which fills the buffers // i = msfsampler(0); // // now loop until interrupted // while (shutdown_flag == 0) { // // start of continuous loop // pthread_mutex_lock(&lock); msfa = msf; ptr1a = ptr1; ptr2a = ptr2; // // is either buffer full? // fullbuffer = 0; if (ptr1 == currentlimit) { fullbuffer = 1; } if (ptr2 == currentlimit) { fullbuffer = 2; } // // assume if one buffer is full then sampler will be working on the other buffer // so it will be safe to release the semaphore // pthread_mutex_unlock(&lock); if (MYDEBUG != 0) { debug_counter++; if (debug_counter > 999) { debug_counter = 0; printf("[main] buffer 1: %i buffer 2: %i\n", ptr1a, ptr2a); if (MYDEBUG == 2) { sprintf(str, "ps -F -L %i\0", getpid()); i = system(str); } } if (msfa != oldmsf && MYDEBUG == 2) printf("[main] msf: %i\n", msfa); } oldmsf = msfa; // // was a buffer full? // if (fullbuffer != 0) { // // we have accumulated enough data to parse it // if (MYDEBUG != 0) { sprintf(logbuffer, "[main] buffer %i is full\n", fullbuffer); b = logmessage(logbuffer); printf(logbuffer); } if (fullbuffer == 1) bufreadptr = &buf1[0]; else bufreadptr = &buf2[0]; // // are we looking for minute markers? // if (syncstage == 1) { if (0 == findminutemarkers(bufreadptr, &mm1, &mm2)) { if (MYDEBUG != 0) { sprintf(logbuffer, "[main] first minute marker mid-point is at %i\n", mm1); b = logmessage(logbuffer); printf(logbuffer); sprintf(logbuffer, "[main] second minute marker mid-point is at %i\n", mm2); b = logmessage(logbuffer); printf(logbuffer); sprintf(logbuffer, "[main] distance between minute markers is %i samples\n", (mm2 - mm1)); b = logmessage(logbuffer); printf(logbuffer); } syncstage = 2; // // re-align the buffers // pthread_mutex_lock(&lock); p1 = ptr1; p2 = ptr2; r = bufferinit(); r = mm2 - mm1; /* length of a minute */ skipcount = currentlimit - mm2; /* remaining samples in buffer */ skipcount = skipcount % r; /* remainder after chopping into minutes */ skipcount = r - skipcount; /* what would be in the next buffer */ skipcount = skipcount; /* hopefully gets us to the centre of the next minute marker */ if (whichptr == 0) { skipcount = skipcount - ptr1; } else { skipcount = skipcount - ptr2; } currentlimit = mm2 - mm1; /* reduce sample size to distance between minute markers */ r = skipcount; pthread_mutex_unlock(&lock); // // now the buffers should have become aligned with the minute marker // if (MYDEBUG != 0) { sprintf(logbuffer, "[main] buffers realigned, pointers were %i %i skipcount was %i\n", p1, p2, r); b = logmessage(logbuffer); printf(logbuffer); } } else { pthread_mutex_lock(&lock); r = bufferinit(); skipcount = SKIPABIT; /* move the capture window a bit to facilitate re-synchronising */ pthread_mutex_unlock(&lock); if (MYDEBUG != 0) { sprintf(logbuffer, "[main] unable to synchronise\n"); b = logmessage(logbuffer); printf(logbuffer); } } } else { // // syncstage 2 - no longer looking for minute markers // if (0 == parsebuffer(bufreadptr)) { i = 0; j = 0; x = 0; while (i < FOURBYONEMIN && j < 61 && x == 0) { // // parse the data in buf4 // x = -1; /* assume error */ if (i == 0) { if (MYDEBUG != 0) { sprintf(logbuffer, "[main] first three samples of minute are %i %i %i\n", buf4[i], buf4[i + 1], buf4[i + 2]); b = logmessage(logbuffer); printf(logbuffer); } while (buf4[i] < 0) i++; /* may have caught tail end of first half of minute marker */ if (buf4[i] > (MINUTEMARKERLOW - 10) && buf4[i] < MINUTEMARKERHIGH) /* but should be second half of minute marker */ { x = 0; /* minute should start with minute marker */ } else { pthread_mutex_lock(&lock); i = bufferinit(); skipcount = SKIPABIT; /* move the capture window a bit to facilitate re-synchronising */ pthread_mutex_unlock(&lock); syncstage = 1; if (MYDEBUG != 0) { sprintf(logbuffer, "[main] minute didn't start with minute marker, resynchronising\n"); b = logmessage(logbuffer); printf(logbuffer); } i = FOURBYONEMIN; /* not strictly necessary */ } } else { if(buf4[i+1] > SECONDMARKERLOW && buf4[i+1] < SECONDMARKERHIGH) { x = 0; databita[j] = 0; databitb[j] = 0; if (buf4[i] < (0 - TWOBITS)) databita[j] = 1; if (buf4[i] < (0 - THREEBITS)) databitb[j] = 1; if (MYDEBUG != 0) { sprintf(logbuffer, "[main] second: %i sample %i: %i %i data: %i %i\n", (j + 1), i, buf4[i], buf4[i+1], databita[j], databitb[j]); b = logmessage(logbuffer); printf(logbuffer); } j++; i++; } else { if(buf4[i+3] > SECONDMARKERLOW && buf4[i+3] < SECONDMARKERHIGH && buf4[i] < 0 && buf4[i+1] > 0 && buf4[i+2] < 0) { x = 0; databita[j] = 0; databitb[j] = 1; if (MYDEBUG != 0) { sprintf(logbuffer, "[main] second: %i sample %i: %i %i %i %i data: %i %i\n", (j + 1), i, buf4[i], buf4[i+1], buf4[i+2], buf4[i+3], databita[j], databitb[j]); b = logmessage(logbuffer); printf(logbuffer); } j++; i = i + 3; } else { if (buf4[i] == 0 && j > 57) { x = 0; /* we've reached the end of a minute */ i = FOURBYONEMIN; } else { if (buf4[i] < (0 - MINUTEMARKERLOW) && buf4[i] > (0 - MINUTEMARKERHIGH)) /* next minute marker? */ { if (j > 57) { x = 0; /* we've reached the end of a minute */ i = FOURBYONEMIN; } } } } } if (MYDEBUG != 0 && x != 0) { sprintf(logbuffer, "[main] problem with sample number %i, second = %i samples: %i %i %i %i\n", i, (j + 1), buf4[i], buf4[i+1], buf4[i+2], buf4[i+3]); b = logmessage(logbuffer); printf(logbuffer); } if (x != 0 && j > 57) { pthread_mutex_lock(&lock); i = bufferinit(); skipcount = SKIPABIT; /* move the capture window a bit to facilitate re-synchronising */ pthread_mutex_unlock(&lock); syncstage = 1; if (MYDEBUG != 0) { sprintf(logbuffer, "[main] problem sample may be truncated minute marker, resynchronising\n"); b = logmessage(logbuffer); printf(logbuffer); } i = FOURBYONEMIN; /* not strictly necessary */ } } i++; // // end while looking in buf4 // } j++; ptr4 = j; if (x == 0 && j > 58 && j < 62) { if (MYDEBUG != 0) { sprintf(logbuffer, "[main] successfully decoded data stream of %i seconds\n", ptr4); b = logmessage(logbuffer); printf(logbuffer); } b = parsetimesignal(); } else { if (MYDEBUG != 0) { sprintf(logbuffer, "[main] failed to decode data stream\n"); b = logmessage(logbuffer); printf(logbuffer); } } // // end if parsebuffer returned 0 // } else { if (MYDEBUG != 0) { sprintf(logbuffer, "[main] parsebuffer overrun, resynchronising\n"); b = logmessage(logbuffer); printf(logbuffer); } pthread_mutex_lock(&lock); i = bufferinit(); skipcount = SKIPABIT; /* move the capture window a bit to facilitate re-synchronising */ pthread_mutex_unlock(&lock); syncstage = 1; } // // whether parsebuffer returned 0 or not // clear the data for next time around // for (i = 0; i < FOURBYONEMIN; i++) buf4[i] = 0; pthread_mutex_lock(&lock); if (fullbuffer == 1) ptr1 = 0; else ptr2 = 0; pthread_mutex_unlock(&lock); // // end syncstage 2 // } // // end if full buffer // } // // wait for a few msec - note this nanosleep will be interrupted every few milliseconds when the sampling timer fires // so if it is, wait again // i = nanosleep(&mytimer, &myremains); j = 0; while (i != 0) { if (errno != EINTR) { sprintf(logbuffer, "[main] Error %i from nanosleep: %s\n", errno, strerror(errno)); b = logmessage(logbuffer); printf(logbuffer); j = 99; } else { i = nanosleep(&mytimer, &myremains); } j++; if (j > 99) i = 0; } if (j > 5) /* tends to run 3 times when user presses ctrl-C */ { sprintf(logbuffer, "[main] nanosleep ran %i times\n", j); b = logmessage(logbuffer); printf(logbuffer); } // // end while (shutdown_flag == 0) // } // // shutdown_flag can be set by a called function or by an interrupt signal // if (internal_error != 0) { sprintf(logbuffer, "Internal error %i\n", internal_error); b = logmessage(logbuffer); printf(logbuffer); } if (internal_error == 2) { sprintf(logbuffer, "Clock receiver appears to be powered off\n"); b = logmessage(logbuffer); printf(logbuffer); } printf("\n\nClosing down\n"); // // destroy the timer and close the serial port // i = msfsampler(1); sleep(1); close(serial_port); pthread_mutex_destroy(&lock); } // // close the log file // b = logmessage("Finished\n"); logcount = -1; b = logmessage(""); exit(EXIT_SUCCESS); }