Newer
Older
/*----------------------------------------------------------------------*/
/* */
/* Module Name: tconv */
/* */
/* Procedure Description: prvides functions to convert TAI and UTC; */
/* and functions to handle TAI (international atomic time) */
/* */
/*----------------------------------------------------------------------*/
/* Header File List: */
/* VxWorks */
#ifdef OS_VXWORKS
#include <vxWorks.h>
#include <inetLib.h>
#include <timers.h>
#if defined( PROCESSOR_BAJA47 ) && defined( _USE_POSIX_TIMER )
#include <heurikon.h>
#define IOBASE 0xb0000000
#define DS1286_BASE ( IOBASE + 0x0f010600 )
#define DS1286_INTERVAL 8
#include <drv/rtc/ds1286.h>
#endif
#if !defined( _USE_POSIX_TIMER )
#include "gpsclk.h"
#define _GPS_BOARD_ID 0
#endif
#else
#include <sys/byteorder.h>
#endif
#include <string.h>
#include <time.h>
#include <time.h>
#include "tconv.h"
#include <stdio.h>
/* define some time constants */
#define SECS_PER_HOUR ( 60 * 60 )
#define SECS_PER_DAY ( SECS_PER_HOUR * 24 )
/* define leap year macros */
#define ISLEAP( year ) \
( ( year ) % 4 == 0 && ( ( year ) % 100 != 0 || ( year ) % 400 == 0 ) )
#define LEAPS_THRU_END_OF( y ) ( ( y ) / 4 - ( y ) / 100 + ( y ) / 400 )
/* defines conversion coefficients between TAI and UTC */
#define OFFS_YEAR 1972
#define OFFS_LEAP 10
#define OFFS_TAI ( ( ( 72 - 58 ) * 365 + 3 ) * SECS_PER_DAY + OFFS_LEAP )
#define OFFS_WDAY 6 /* January 1, 1972 was a Saturday */
/* How many days come before each month (0-12) */
static const unsigned short int mon_yday[ 2 ][ 13 ] = {
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
/* This table contains every TAI second which is a leap second
the second argument is the correction to apply, relative to
Jan. 1, 1972. The 10sec difference at Jan. 1, 1972 is already
included in OFFS_TAI. If a leap second has to be removed its
TAI has to be the second just prior to the occurance of the leap.
There must one entry for every added leap second.
This list has to be in ascending order of time. */
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
static size_t num_leaps = 22;
static leap_t leaps[ 100 ] = {
{ OFFS_TAI + 182 * SECS_PER_DAY, +1 }, /* Jul. 1, 1972 */
{ OFFS_TAI + ( 365 + 1 ) * SECS_PER_DAY + 1, +2 }, /* Jan. 1, 1973 */
{ OFFS_TAI + ( 2 * 365 + 1 ) * SECS_PER_DAY + 2, +3 }, /* Jan. 1, 1974 */
{ OFFS_TAI + ( 3 * 365 + 1 ) * SECS_PER_DAY + 3, +4 }, /* Jan. 1, 1975 */
{ OFFS_TAI + ( 4 * 365 + 1 ) * SECS_PER_DAY + 4, +5 }, /* Jan. 1, 1976 */
{ OFFS_TAI + ( 5 * 365 + 2 ) * SECS_PER_DAY + 5, +6 }, /* Jan. 1, 1977 */
{ OFFS_TAI + ( 6 * 365 + 2 ) * SECS_PER_DAY + 6, +7 }, /* Jan. 1, 1978 */
{ OFFS_TAI + ( 7 * 365 + 2 ) * SECS_PER_DAY + 7, +8 }, /* Jan. 1, 1979 */
{ OFFS_TAI + ( 8 * 365 + 2 ) * SECS_PER_DAY + 8, +9 }, /* Jan. 1, 1980 */
{ OFFS_TAI + ( 9 * 365 + 3 + 181 ) * SECS_PER_DAY + 9,
+10 }, /* Jul. 1, 1981 */
{ OFFS_TAI + ( 10 * 365 + 3 + 181 ) * SECS_PER_DAY + 10,
+11 }, /* Jul. 1, 1982 */
{ OFFS_TAI + ( 11 * 365 + 3 + 181 ) * SECS_PER_DAY + 11,
+12 }, /* Jul. 1, 1983 */
{ OFFS_TAI + ( 13 * 365 + 4 + 181 ) * SECS_PER_DAY + 12,
+13 }, /* Jul. 1, 1985 */
{ OFFS_TAI + ( 16 * 365 + 4 ) * SECS_PER_DAY + 13, +14 }, /* Jan. 1, 1988 */
{ OFFS_TAI + ( 18 * 365 + 5 ) * SECS_PER_DAY + 14, +15 }, /* Jan. 1, 1990 */
{ OFFS_TAI + ( 19 * 365 + 5 ) * SECS_PER_DAY + 15, +16 }, /* Jan. 1, 1991 */
{ OFFS_TAI + ( 20 * 365 + 5 + 182 ) * SECS_PER_DAY + 16,
+17 }, /* Jul. 1, 1992 */
{ OFFS_TAI + ( 21 * 365 + 6 + 181 ) * SECS_PER_DAY + 17,
+18 }, /* Jul. 1, 1993 */
{ OFFS_TAI + ( 22 * 365 + 6 + 181 ) * SECS_PER_DAY + 18,
+19 }, /* Jul. 1, 1994 */
{ OFFS_TAI + ( 24 * 365 + 6 ) * SECS_PER_DAY + 19, +20 }, /* Jan. 1, 1996 */
{ OFFS_TAI + ( 25 * 365 + 7 + 181 ) * SECS_PER_DAY + 20,
+21 }, /* Jul. 1, 1997 */
{ OFFS_TAI + ( 27 * 365 + 7 ) * SECS_PER_DAY + 21, +22 }
}; /* Jan. 1, 1999 */
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: TAItoUTC */
/* */
/* Procedure Description: converts TAI to UTC */
/* */
/* */
/* Procedure Arguments: taisec_t t, utc_t* utc_ptr */
/* */
/* Procedure Returns: utc_t* or NULL when failed */
/* */
/*----------------------------------------------------------------------*/
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
utc_t*
TAItoUTC( taisec_t t, utc_t* utc_ptr )
{
long tt; /* seconds since Jan. 1, OFFS_YEAR */
long int leap_correction;
int leap_extra_secs;
long int days;
long int rem;
long int y;
register int i;
const unsigned short int* ip;
t += TAIatGPSzero;
if ( ( utc_ptr == NULL ) || ( t < OFFS_TAI ) )
{
return NULL;
}
/* find the last leap second correction transition time before t */
i = num_leaps;
do
{
i--;
} while ( ( i >= 0 ) && ( t < leaps[ i ].transition ) );
/* apply leap correction. */
if ( i < 0 )
{
/* before 1st leap second */
leap_correction = 0;
leap_extra_secs = 0;
}
else
{
leap_correction = leaps[ i ].change;
/* exactly at transition time ? */
if ( ( t == leaps[ i ].transition ) &&
( ( ( i == 0 ) && ( leaps[ i ].change > 0 ) ) ||
( leaps[ i ].change > leaps[ i - 1 ].change ) ) )
{
leap_extra_secs = 1; /* sec = 60 */
/* more than one leap second ? */
while (
( i > 0 ) &&
( leaps[ i ].transition == leaps[ i - 1 ].transition + 1 ) &&
( leaps[ i ].change == leaps[ i - 1 ].change + 1 ) )
{
leap_extra_secs++;
i--;
}
else
{
leap_extra_secs = 0;
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
}
}
/* calculate utc from TAI */
tt = t - OFFS_TAI - leap_correction;
/* first separate days from the rest */
days = tt / SECS_PER_DAY;
rem = tt % SECS_PER_DAY;
while ( rem < 0 )
{
rem += SECS_PER_DAY;
days--;
}
while ( rem >= SECS_PER_DAY )
{
rem -= SECS_PER_DAY;
days++;
}
/* fill in hours, minutes, sec */
utc_ptr->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
utc_ptr->tm_min = rem / 60;
utc_ptr->tm_sec = rem % 60;
/* fill in week day */
utc_ptr->tm_wday = ( OFFS_WDAY + days ) % 7;
if ( utc_ptr->tm_wday < 0 )
{
utc_ptr->tm_wday += 7;
}
/* calculate year */
y = OFFS_YEAR;
while ( ( days < 0 ) || ( days >= ( ISLEAP( y ) ? 366 : 365 ) ) )
{
/* Guess a corrected year, assuming 365 days per year. */
long int yg = y + days / 365 - ( days % 365 < 0 );
/* Adjust DAYS and Y to match the guessed year. */
days -= ( ( yg - y ) * 365 + LEAPS_THRU_END_OF( yg - 1 ) -
LEAPS_THRU_END_OF( y - 1 ) );
y = yg;
}
/* fill in year, days of year */
utc_ptr->tm_year = y - 1900;
utc_ptr->tm_yday = days;
/* fill in month and day of month */
ip = mon_yday[ ISLEAP( y ) ];
for ( y = 11; days < ip[ y ]; --y )
{
continue;
}
days -= ip[ y ];
utc_ptr->tm_mon = y;
utc_ptr->tm_mday = days + 1;
/* adjust extra seconds when leap occures */
utc_ptr->tm_sec += leap_extra_secs;
return utc_ptr;
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: TAIntoUTC */
/* */
/* Procedure Description: converts TAI to UTC */
/* */
/* */
/* Procedure Arguments: tainsec_t t, utc_t* utc_ptr */
/* */
/* Procedure Returns: utc_t* or NULL when failed */
/* */
/*----------------------------------------------------------------------*/
utc_t*
TAIntoUTC( tainsec_t t, utc_t* utc_ptr )
{
taisec_t tai;
if ( ( tai = TAIsec( t, NULL ) ) == 0 )
{
return NULL;
}
return TAItoUTC( tai, utc_ptr );
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: UTCtoTAI */
/* */
/* Procedure Description: converts UTC to TAI */
/* */
/* */
/* Procedure Arguments: const utc_t* utc_ptr */
/* */
/* Procedure Returns: taisec_t or 0 when failed */
/* */
/*----------------------------------------------------------------------*/
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
taisec_t
UTCtoTAI( const utc_t* utc_ptr )
{
int mon_remainder;
int mon_years;
int year;
int ydays;
int days;
taisec_t tai;
int leap_correction;
int leap_extra_secs;
int i;
if ( utc_ptr == NULL )
{
return 0;
}
/* Ensure that month is in range */
mon_remainder = utc_ptr->tm_mon % 12;
mon_years = utc_ptr->tm_mon / 12;
if ( mon_remainder < 0 )
{
mon_remainder += 12;
mon_years--;
}
/* caluclate year and days in year */
year = ( utc_ptr->tm_year + mon_years ) + 1900;
ydays = mon_yday[ ISLEAP( year ) ][ mon_remainder ] + utc_ptr->tm_mday - 1;
/* calculate days since OFFS_YEAR */
days = ydays + 365 * ( year - OFFS_YEAR ) + LEAPS_THRU_END_OF( year - 1 ) -
LEAPS_THRU_END_OF( OFFS_YEAR - 1 );
if ( days < 0 )
{
return 0;
}
/* calculate TAI neglecting leap seconds */
tai = ( (taisec_t)days ) * SECS_PER_DAY + OFFS_TAI +
utc_ptr->tm_hour * SECS_PER_HOUR + utc_ptr->tm_min * 60 +
utc_ptr->tm_sec;
/* correct for leap seconds */
leap_extra_secs = ( utc_ptr->tm_sec > 59 ) ? ( utc_ptr->tm_sec - 59 ) : 0;
leap_correction = 0;
i = 0;
while ( ( i < num_leaps ) &&
( ( ( leap_extra_secs > 0 ) && /* handles leap seconds */
( tai + leap_correction - leap_extra_secs >
leaps[ i ].transition ) ) ||
( ( leap_extra_secs == 0 ) && /* handles non-leap seconds */
( tai + leap_correction >= leaps[ i ].transition ) ) ) )
{
leap_correction = leaps[ i ].change;
i++;
}
return ( tai + leap_correction ) - TAIatGPSzero;
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: UTCtoTAIn */
/* */
/* Procedure Description: converts UTC to TAI */
/* */
/* */
/* Procedure Arguments: const utc_t* utc_ptr */
/* */
/* Procedure Returns: tainsec_t or 0 when failed */
/* */
/*----------------------------------------------------------------------*/
tainsec_t
UTCtoTAIn( const utc_t* utc_ptr )
{
tai_t t;
t.tai = UTCtoTAI( utc_ptr );
if ( t.tai == 0 )
{
return 0;
}
t.nsec = 0;
return TAInsec( &t );
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: TAInow */
/* */
/* Procedure Description: returns the current TAI */
/* */
/* */
/* Procedure Arguments: void */
/* */
/* Procedure Returns: tainsec_t */
/* */
/*----------------------------------------------------------------------*/
tainsec_t
TAInow( void )
{
/* can be implementation dependent */
/* use current time returned by time.h, uses POSIX */
struct tm utc;
struct timespec now;
tai_t tai;
/* for Bajas initialize the posix clock with the on-board
real-time clock (DS1286 chip) */
#if defined( OS_VXWORKS ) && defined( PROCESSOR_BAJA47 ) && \
defined( _USE_POSIX_TIMER )
#define sysBcdToBin( bcd ) ( ( ( ( bcd ) >> 4 ) * 10 ) + ( (bcd)&0xf ) )
{
static _initRTC = 0;
/* sysRtcGet (&utc); */
if ( _initRTC == 0 )
{
/* latch the time via the transfer enable bit */
/* this does not stop the clock */
*DS1286_CR &= ~DS1286_CR_TE;
now.tv_nsec = 10000000 * sysBcdToBin( *DS1286_MSEC );
utc.tm_sec = sysBcdToBin( *DS1286_SEC );
utc.tm_min = sysBcdToBin( *DS1286_MIN );
utc.tm_hour = sysBcdToBin( *DS1286_HOURS );
utc.tm_mday = sysBcdToBin( *DS1286_DATE );
utc.tm_mon = sysBcdToBin( *DS1286_MONTH & 0x3f ) - 1;
utc.tm_year = sysBcdToBin( *DS1286_YEAR );
if ( utc.tm_year < 96 )
{
utc.tm_year += 100;
}
/* unlatch the time */
*DS1286_CR |= DS1286_CR_TE;
/* initialze POSIX timer */
now.tv_sec = mktime( &utc );
clock_settime( CLOCK_REALTIME, &now );
_initRTC = 1;
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
}
}
#endif
#if defined( OS_VXWORKS ) && !defined( _USE_POSIX_TIMER )
/* VMESYNCCLOCK stuff here */
return (tainsec_t)gpsTimeNow( _GPS_BOARD_ID );
#else
if ( clock_gettime( CLOCK_REALTIME, &now ) != 0 )
{
return 0;
}
/* gmtime_r returns int on VxWorks and
a ptr to the 2nd arg on UNIX */
#ifdef OS_VXWORKS
if ( gmtime_r( &now.tv_sec, &utc ) != 0 )
{
return 0;
}
#else
if ( gmtime_r( &now.tv_sec, &utc ) == NULL )
{
return 0;
}
#endif
#endif
tai.tai = UTCtoTAI( &utc );
tai.nsec = now.tv_nsec;
return TAInsec( &tai );
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: TAInsec */
/* */
/* Procedure Description: converts between tai_t and TAInsec */
/* */
/* */
/* Procedure Arguments: tai_t* t */
/* */
/* Procedure Returns: tainsec_t */
/* */
/*----------------------------------------------------------------------*/
tainsec_t
TAInsec( const tai_t* t )
{
if ( t == NULL )
{
return 0;
}
return _ONESEC * (tainsec_t)t->tai + (tainsec_t)t->nsec;
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: TAIsec */
/* */
/* Procedure Description: converts from TAInsec to TAIsec and */
/* tai_t (if tai != NULL) */
/* */
/* Procedure Arguments: const tainsec_t t, tai_t* tai */
/* */
/* Procedure Returns: taisec_t */
/* */
/*----------------------------------------------------------------------*/
taisec_t
TAIsec( tainsec_t t, tai_t* tai )
{
tai_t t0;
t0.tai = ( taisec_t )( t / _ONESEC );
t0.nsec = ( taisec_t )( t % _ONESEC );
if ( tai != NULL )
{
*tai = t0;
}
/* round to next second */
if ( t0.nsec < 500000000L )
{
return t0.tai;
}
else
{
return t0.tai + 1;
}
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: htonTAI */
/* */
/* Procedure Description: converts TAI to network byte order */
/* */
/* */
/* Procedure Arguments: tainsec_t t, char* buf (needs 8 bytes) */
/* */
/* Procedure Returns: char* to buf or NULL if failed */
/* */
/*----------------------------------------------------------------------*/
char*
htonTAI( tainsec_t t, char* buf )
{
tai_t tai;
if ( ( buf == NULL ) || ( TAIsec( t, &tai ) == 0 ) )
{
return NULL;
}
tai.tai = htonl( tai.tai );
tai.nsec = htonl( tai.nsec );
memcpy( buf, &tai, sizeof( tai_t ) );
return buf;
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: ntohTAI */
/* */
/* Procedure Description: converts TAI from network byte order */
/* */
/* */
/* Procedure Arguments: const char* buf (reads 8 bytes) */
/* */
/* Procedure Returns: tainsec_t or 0 if failed */
/* */
/*----------------------------------------------------------------------*/
tainsec_t
ntohTAI( const char* buf )
{
tai_t tai;
if ( buf == NULL )
{
return 0;
}
memcpy( &tai, buf, sizeof( tai_t ) );
tai.tai = ntohl( tai.tai );
tai.nsec = ntohl( tai.nsec );
return TAInsec( &tai );
}
/*----------------------------------------------------------------------*/
/* */
/* External Procedure Name: getNextLeap */
/* */
/* Procedure Description: returns the next leap second */
/* only looks for leaps > t */
/* */
/* Procedure Arguments: taisec_t t, struct leap* nextleap */
/* */
/* Procedure Returns: struct leap* (or NULL when no leap left */
/* */
/*----------------------------------------------------------------------*/
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
leap_t*
getNextLeap( taisec_t t, leap_t* nextleap )
{
int i;
if ( nextleap == NULL )
{
return NULL;
}
/* determine last index in leap table with transition <= t */
i = 0;
while ( ( i < num_leaps ) && ( ( (taisec_t)leaps[ i ].transition ) <= t ) )
{
i++;
}
/* return next leap if exists */
if ( i < num_leaps )
{
*nextleap = leaps[ i ];
nextleap->change += OFFS_LEAP;
return nextleap;
}
else
{
nextleap->transition = 0;
nextleap->change = 0;
return NULL;
}
}