#include #include #include #include #include #include #include "logio.h" #include "vlbconst.h" #include "vlbutil.h" /* * Define calendar as days per month and name of month (0 - relative indexing) */ static char daytab[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static char *month_name[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /*....................................................................... * Convert a number in radians to hours, minutes and seconds. The * resulting hours will be constrained to be within 0 -> 24 hours. */ void radhms(double rad, int *hour, int *mins, double *secs) { double hr; /* rad converted to decimal hours */ double hr_int,hr_frc; /* Integral and fractional hours */ double min_int,min_frc; /* Integral and fractional minutes */ /* * Convert radians to decimal hours. */ hr = rad * rtoh; /* * Enforce hr to be between 0 -> 24 hours. */ if(hr > 24.0) { hr = fmod(hr, 24.0); } else if(hr < 0.0) { hr = 24.0 - fmod(fabs(hr), 24.0); if(hr >= 24.0) hr = 0.0; }; /* * Split decimal hours into integral and fractional parts. */ hr_frc = modf(hr, &hr_int); /* * Split the fractional hours into fractional and integral minutes. */ min_frc = modf(hr_frc * 60.0, &min_int); /* * Assign return values. */ *hour = hr_int; *mins = min_int; *secs = min_frc * 60.0; return; } /*....................................................................... * Given an angle in radians, compose a string of form HH MM SS.S... in * a provided output string, where HH is the number of degrees, MM the * number of minutes and SS.S... the number of seconds to a given * precision. * * Input: * rad double The angle, measured in radians. * precision int The number of decimal places to represent the seconds * argument with. * Input/Output: * string char * The output string. This must be at least * 10 + 'precision' characters long. */ char *sradhms(double rad, int precision, char *string) { int width; /* The width of the seconds field */ int hour; /* The integral number of hours */ int mins; /* The integral number of minutes */ double secs; /* The decimal number of seconds */ /* * Sanity checks. */ if(precision < 0) precision = 0; /* * Hand the conversion job to radhms(). */ radhms(rad, &hour, &mins, &secs); /* * Determine the width of the seconds field. Note that this includes * the decimal point character when precision != 0. */ width = precision == 0 ? 2 : (3 + precision); /* * Compose the string. */ sprintf(string, "%2.2d %2.2d %0*.*f", hour, mins, width, precision, secs); return string; } /*....................................................................... * Convert a number in radians to sign, degrees, minutes and seconds. */ void raddms(double rad, int *sgn, int *deg, int *mins, double *secs) { double deg_int,deg_frc; /* Integral and fractional degrees */ double min_int,min_frc; /* Integral and fractional arcminutes */ /* * Split decimal degrees into integral and fractional parts. */ deg_frc = modf(fabs(rad * rtod), °_int); /* * Split the fractional degrees into fractional and integral minutes. */ min_frc = modf(deg_frc * 60.0, &min_int); /* * Assign the return values. */ *sgn = rad >= 0.0 ? 1 : -1; *deg = deg_int; *mins = min_int; *secs = min_frc * 60.0; return; } /*....................................................................... * Given an angle in radians between -pi/2 and pi/2, compose a string of * form DDD MM SS.S... in a provided output string, where DDD is the * number of degrees including the sign, MM the number of arc-minutes * and SS.S... the number of arc-seconds to a given precision. * * Input: * rad double The angle, measured in radians (must be in the range * -pi/2 to pi/2). * precision int The number of decimal places to represent the seconds * argument with. * Input/Output: * string char * The output string. This must be at least * 11 + 'precision' characters long. */ char *sraddms(double rad, int precision, char *string) { int width; /* The width of the seconds field */ int sgn; /* The sign of the number */ int deg; /* The integral number of degrees */ int mins; /* The integral number of minutes */ double secs; /* The decimal number of seconds */ /* * Sanity checks. */ if(precision < 0) precision = 0; /* * Hand the conversion job to radhms(). */ raddms(rad, &sgn, °, &mins, &secs); /* * Determine the width of the seconds field. Note that this includes * the decimal point character when precision != 0. */ width = precision == 0 ? 2 : (3 + precision); /* * Compose the string. */ sprintf(string, "%c%2.2d %2.2d %0*.*f", sgn<0 ? '-' : '+', deg, mins, width, precision, secs); return string; } /*....................................................................... * Given the Gregorian year (eg. 1991) and a day number within that year * return the equivalent month and day within that year. * 'dayno' need not be within the range 1 -> 365. If it isn't then * before use, year will be incremented or decremented (as * appropriate) and dayno will be adjusted to the corresponding day * within that year. * * Input: * year int Year. * dayno int Day number within 'year' - starts at day 1. * Output: * day int * The day within month 'month'. * month int * The month within which 'dayno' falls. */ void daydate(int year, int dayno, int *day, int *month) { int i, leap; /* * Constrain dayno and year to legal values. */ if(dayno > 365) { year += (dayno-1) / 365; dayno = (dayno-1) % 365 + 1; } else if(dayno < 1) { year -= abs(dayno) / 365 + 1; dayno = 365 - abs(dayno) % 365; }; /* * Check whether 'year' is a leap year 'leap' will be used to * index into the corresponding row of the daytab array. */ leap = (year%4 == 0 && year%100 != 0) || year%400 == 0; /* * Find the month and day corresponding to dayno. */ for(i=0; dayno > daytab[leap][i]; i++) dayno -= daytab[leap][i]; *month = i+1; *day = dayno; return; } /*....................................................................... * Given the Gregorian year (eg. 1991) and a day number within that year * return a string representation of the corresponding year, month and * day. Eg. "1991 Sep 24". The string to hold the date string must be * provided by the user. The pointer to the date string is also returned * as the function's main return value. * 'dayno' need not be within the range 1 -> 365. If it isn't then * before use, year will be incremented or decremented (as * appropriate) and dayno will be adjusted to the corresponding day * within that year. * * Input: * year int Year. * dayno int Day number within 'year' - starts at day 1. * Output: * date char * You must supply a pointer here to a char array of at * least 12 characters. */ char *sdaydate(int year, int dayno, char *date) { int day, month; /* * Hand the conversion job to daydate(). */ daydate(year, dayno, &day, &month); /* * Encode the date in the user's string. */ if(date) sprintf(date, "%4.4d %3.3s %2.2d", year, month_name[month-1], day); else lprintf(stderr, "ERROR: NULL string pointer given to sdaydate()\n"); return date; } /*....................................................................... * Given a time, as the number of seconds of UTC wrt the beginning of the * year, and a character array of at least 12 characters - write the * corresponding date in the string, like "1991 Sep 24". The string * will be null terminated and returned. * * Input: * year int The year from the start of which, 'ut' seconds * were counted. * vlbut double Number of seconds since start of 'year'. * Output: * string char * The coresponding date will be written like * "1991 Sep 24" and null terminated. Here the caller * must provide a character array of at least 12 elements. */ char *sutdate(int year, double vlbut, char *string) { int dayno, hour, mins; double secs; /* * Get the day number via dayut(). */ dayut(vlbut, &dayno, &hour, &mins, &secs); /* * Print the string via sdaydate(). */ return sdaydate(year, dayno, string); } /*....................................................................... * Given a UT, as the number of seconds since the beginning of * the year - return the current day number and the hours,mins and seconds * into that day. If any of dayno, hour, mins, secs are not required send NULL. * * Input: * vlbut double UT in seconds from start of year 'year'. * Output: * dayno int * Day number within year 'year'. * hour int * Hour within day 'dayno'. * mins int * Minute within hour 'hour'. * secs float * Decimal seconds within minute 'mins'. */ void dayut(double vlbut, int *dayno, int *hour, int *mins, double *secs) { double ddays, dhour, dmins, dsecs; /* * How many seconds into year? */ dsecs = vlbut; /* * Minutes into year. */ dmins = floor(dsecs/60.0); /* * Seconds into minute 'dmins'. */ dsecs -= dmins * 60.0; /* * Hours into year. */ dhour = floor(dmins/60.0); /* * Minutes into hour 'dhour'. */ dmins -= dhour * 60.0; /* * Days into year. */ ddays = floor(dhour/24.0); /* * Hours into day 'ddays'. */ dhour -= ddays * 24.0; /* * Assign the return values. */ if(dayno) *dayno = ddays + 1.0; /* First day is day 1 */ if(hour) *hour = dhour; if(mins) *mins = dmins; if(secs) *secs = dsecs; return; } /*....................................................................... * Given a user provided char array with at least 13 elements, and a * ut, as the number of seconds of UTC wrt the start of the year, * return a string representation of the ut in the user's string. * The format produced is: DDD/HH:MM:SS where DDD is the day number. * * Input: * vlbut double Number of seconds since start of year. * nc int The number of characters available in string[] * (including space for '\0'). * Input/Output: * string char * User provided string. This should have at least * 13 characters. If the time can not be fitted within * nc characters (including '\0') then it will not be * be written. * Output: * return int The number of characters written (excluding '\0'). * This will be zero if the time could not be written * without truncation. */ int write_ut(double vlbut, int nc, char *string) { char stmp[20]; /* Temporary buffer to compose the output in */ int slen; /* The number of characters written */ int dayno, hour, mins; double secs; /* * Let dayut() do all the hard work converting vlbut to day number * and UT. */ dayut(vlbut, &dayno, &hour, &mins, &secs); /* * Initially write the string in stmp[] to find out how long it is. */ sprintf(stmp, "%d/%2.2d:%2.2d:%2.2d", dayno, hour, mins, (int) secs); slen = strlen(stmp); /* * Copy the time to the output string if there is room. */ if(slen >= nc || !string) { if(string && nc>0) *string = '\0'; return 0; }; strcpy(string, stmp); return slen; } /*....................................................................... * Decode a string of form DDD/HH:MM:SS into a date in seconds wrt the * start of the year. * * Input: * s char * The string to be decoded. * Input/Output: * endp char ** If endp!=NULL then *endp will point at the next * un-processed character following the date. * If endp==NULL trailing input following the date * will be treated as an error. * Output: * return double The date in seconds, or < 0 on error. * On error *endp will be left equal to 's'. */ double read_ut(char *s, char **endp) { int dayno=0; /* Day number within year */ int hour=0; /* Hour number */ int min=0; /* Minute number */ double sec=0.0; /* Seconds */ char *sptr; /* Pointer into s[] */ /* * Bad string? */ if(!s) { lprintf(stderr, "read_ut: NULL date string received.\n"); return -1.0; }; /* * Initialize the pointer to unprocessed input. */ if(endp) *endp = s; /* * Read each component in turn. */ sptr = s; /* * Get the day number. */ while(*sptr && isspace(*sptr)) sptr++; if(*sptr && isdigit(*sptr)) { char *eptr; dayno = (int) strtol(sptr, &eptr, 10); sptr = (*eptr=='/') ? eptr+1 : eptr; } else { lprintf(stderr, "read_ut: Missing day number in \"%s\".\n", s); return -1; }; /* * Get the hour. */ while(*sptr && isspace(*sptr)) sptr++; if(*sptr && isdigit(*sptr)) { hour = strtol(sptr, &sptr, 10); if(hour > 23) { lprintf(stderr, "read_ut: Hour value out of range in \"%s\".\n", s); return -1.0; }; if(*sptr==':') sptr++; }; /* * Get the minute. */ while(*sptr && isspace(*sptr)) sptr++; if(*sptr && isdigit(*sptr)) { min = strtol(sptr, &sptr, 10); if(min > 60) { lprintf(stderr, "read_ut: Minute value out of range in \"%s\".\n", s); return -1.0; }; if(*sptr==':') sptr++; }; /* * Get the second. */ while(*sptr && isspace(*sptr)) sptr++; if(*sptr && isdigit(*sptr)) sec = strtod(sptr, &sptr); if(sec > 60.0) { lprintf(stderr, "read_ut: Seconds value out of range in \"%s\".\n", s); return -1.0; }; /* * Find the end of the string. */ while(*sptr && isspace(*sptr)) sptr++; if(endp) { *endp = sptr; } else if(*sptr) { lprintf(stderr, "Bad date string: %s\n", s); return -1.0; }; return sec + 60.0 * (min + 60.0 * (hour + 24.0 * (dayno-1))); } /*....................................................................... * Convert a ut, measured as the number of seconds since the beginning of * the year 'year', into the corresponding julian day number and fractional * day from mean noon on that day. * * Input: * vlbut double Number of seconds since start of year. * year int The year from the start of which vlbut was formed. * Output: * jd long * The day corresponding julian day number. * jdfrc double * The fractional day since noon of julian day 'jd'. * je double * The julian day expressed as a Julian epoch. */ void julday(double vlbut, int year, long *jd, double *jdfrc, double *je) { int dayno, hour, mins, iyear, icent; long jd_jan0; double secs; /* * First decompose the UT into day number within the year and * hours, minutes and seconds. */ dayut(vlbut, &dayno, &hour, &mins, &secs); /* * Calculate the julian day number at noon on Jan 0 of 'year'. */ iyear = year - 1; icent = iyear / 100; jd_jan0 = 1721425L + 365*iyear + iyear/4 - icent + icent/4; /* * Work out the fraction of that day by which the ut follows * noon of the previous day. (Julian days start at noon). */ *jdfrc = 0.5 + (hour + (mins + secs / 60.0) / 60.0 ) / 24.0; /* * Turn dayno and frcday into the number of julian days since noon Jan * 0 and the fractional day following that julian day. */ if(*jdfrc < 1.0) dayno -= 1; else *jdfrc -= 1.0; /* * Work out the complete (integer) julian day number. */ *jd = jd_jan0 + dayno; /* * Turn this into the equivalent Julian Epoch. */ *je = 2000.0 + (*jd-2451545L)/365.25; return; } /*....................................................................... * Return a string containing the current date and time. * This function returns a copy of the string returned by the standard * library function asctime(), devoid of the annoying '\n'. * * Output: * return char * A '\0' terminated string containing the date and time. * On NULL return NULL. */ char *date_str(void) { static char str[81]; /* The return string */ time_t tp; char *nptr; /* Pointer to newline character in return string */ /* * Get the time. */ if(time(&tp) == -1) { lprintf(stderr, "date_str: No date available on this machine\n"); return NULL; }; /* * Get a formatted string version of this and copy it into str[]. */ strncpy(str, ctime(&tp), sizeof(str)-1); /* * Ensure that it is '\0' terminated. */ str[sizeof(str)-1] = '\0'; /* * If there is a '\n' character (as specified in the standard), * terminate the string there. */ nptr = strchr(str, '\n'); if(nptr) *nptr = '\0'; return &str[0]; }