00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifdef HAVE_CONFIG_H
00025 # include <simgear_config.h>
00026 #endif
00027
00028 #include <simgear/compiler.h>
00029
00030 #include <errno.h>
00031
00032 #include <cstdio>
00033 #include <cstdlib>
00034 #include <ctime>
00035
00036 #include <string>
00037
00038 #ifdef HAVE_SYS_TIME_H
00039 # include <sys/time.h>
00040 #endif
00041 #ifdef HAVE_SYS_TIMEB_H
00042 # include <sys/timeb.h>
00043 #endif
00044 #ifdef HAVE_UNISTD_H
00045 # include <unistd.h>
00046 #endif
00047
00048 #include <math.h>
00049
00050 #include <simgear/constants.h>
00051 #include <simgear/debug/logstream.hxx>
00052 #include <simgear/misc/sg_path.hxx>
00053
00054 #include "sg_time.hxx"
00055 #include "timezone.h"
00056 #include "lowleveltime.h"
00057
00058 #define DEGHR(x) ((x)/15.)
00059 #define RADHR(x) DEGHR(x*SGD_RADIANS_TO_DEGREES)
00060
00061
00062 static const double MJD0 = 2415020.0;
00063 static const double J2000 = 2451545.0 - MJD0;
00064 static const double SIDRATE = 0.9972695677;
00065
00066
00067 void SGTime::init( double lon_rad, double lat_rad,
00068 const string& root, time_t init_time )
00069 {
00070 SG_LOG( SG_EVENT, SG_INFO, "Initializing Time" );
00071
00072 gst_diff = -9999.0;
00073
00074 if ( init_time ) {
00075 cur_time = init_time;
00076 } else {
00077 cur_time = time(NULL);
00078 }
00079
00080 SG_LOG( SG_EVENT, SG_INFO,
00081 "Current greenwich mean time = " << asctime(gmtime(&cur_time)));
00082 SG_LOG( SG_EVENT, SG_INFO,
00083 "Current local time = " << asctime(localtime(&cur_time)));
00084
00085 if ( !root.empty()) {
00086 SGPath zone( root );
00087 zone.append( "zone.tab" );
00088 SG_LOG( SG_EVENT, SG_INFO, "Reading timezone info from: "
00089 << zone.str() );
00090 tzContainer = new SGTimeZoneContainer( zone.c_str() );
00091
00092 SGGeoCoord location( SGD_RADIANS_TO_DEGREES * lat_rad, SGD_RADIANS_TO_DEGREES * lon_rad );
00093 SGGeoCoord* nearestTz = tzContainer->getNearest(location);
00094
00095 SGPath name( root );
00096 name.append( nearestTz->getDescription() );
00097 zonename = name.str();
00098 SG_LOG( SG_EVENT, SG_INFO, "Using zonename = " << zonename );
00099 } else {
00100 SG_LOG( SG_EVENT, SG_INFO, "*** NO TIME ZONE NAME ***" );
00101 tzContainer = NULL;
00102 zonename.erase();
00103 }
00104 }
00105
00106 SGTime::SGTime( double lon_rad, double lat_rad, const string& root,
00107 time_t init_time )
00108 {
00109 init( lon_rad, lat_rad, root, init_time );
00110 }
00111
00112
00113 SGTime::SGTime( const string& root ) {
00114 init( 0.0, 0.0, root, 0 );
00115 }
00116
00117
00118 SGTime::SGTime() {
00119 init( 0.0, 0.0, "", 0 );
00120 }
00121
00122
00123 SGTime::~SGTime()
00124 {
00125 delete tzContainer;
00126 }
00127
00128
00129
00130
00131
00132
00133 static double sidereal_precise( double mjd, double lng )
00134 {
00135
00136
00137
00138
00139 lng *= SGD_DEGREES_TO_RADIANS;
00140
00141
00142 double gst = sgTimeCalcGST( mjd );
00143 double lst = gst - RADHR( lng );
00144 lst -= 24.0 * floor( lst / 24.0 );
00145
00146
00147 return lst;
00148 }
00149
00150
00151
00152 static double sidereal_course( time_t cur_time, const struct tm *gmt, double lng )
00153 {
00154 time_t start_gmt, now;
00155 double diff, part, days, hours, lstTmp;
00156 char tbuf[64];
00157
00158 now = cur_time;
00159 start_gmt = sgTimeGetGMT(gmt->tm_year, 2, 21, 12, 0, 0);
00160
00161 SG_LOG( SG_EVENT, SG_DEBUG, " COURSE: GMT = "
00162 << sgTimeFormatTime(gmt, tbuf) );
00163 SG_LOG( SG_EVENT, SG_DEBUG, " March 21 noon (GMT) = " << start_gmt );
00164
00165 diff = (now - start_gmt) / (3600.0 * 24.0);
00166
00167 SG_LOG( SG_EVENT, SG_DEBUG,
00168 " Time since 3/21/" << gmt->tm_year << " GMT = " << diff );
00169
00170 part = fmod(diff, 1.0);
00171 days = diff - part;
00172 hours = gmt->tm_hour + gmt->tm_min/60.0 + gmt->tm_sec/3600.0;
00173
00174 lstTmp = (days - lng)/15.0 + hours - 12;
00175
00176 while ( lstTmp < 0.0 ) {
00177 lstTmp += 24.0;
00178 }
00179
00180 SG_LOG( SG_EVENT, SG_DEBUG,
00181 " days = " << days << " hours = " << hours << " lon = "
00182 << lng << " lst = " << lstTmp );
00183
00184 return lstTmp;
00185 }
00186
00187
00188
00189 void SGTime::update( double lon_rad, double lat_rad,
00190 time_t ct, long int warp )
00191 {
00192 double gst_precise, gst_course;
00193
00194
00195 tm * gmt = &m_gmt;
00196
00197
00198 SG_LOG( SG_EVENT, SG_DEBUG, "Updating time" );
00199
00200
00201
00202 if ( ct ) {
00203 cur_time = ct + warp;
00204 } else {
00205 cur_time = time(NULL) + warp;
00206 }
00207 SG_LOG( SG_EVENT, SG_DEBUG,
00208 " Current Unix calendar time = " << cur_time
00209 << " warp = " << warp );
00210
00211
00212
00213 memcpy( gmt, gmtime(&cur_time), sizeof(tm) );
00214 SG_LOG( SG_EVENT, SG_DEBUG,
00215 " Current GMT = " << gmt->tm_mon+1 << "/"
00216 << gmt->tm_mday << "/" << (1900 + gmt->tm_year) << " "
00217 << gmt->tm_hour << ":" << gmt->tm_min << ":"
00218 << gmt->tm_sec );
00219
00220
00221 mjd = sgTimeCurrentMJD( ct, warp );
00222
00223
00224 mjd += (gmt->tm_hour / 24.0) + (gmt->tm_min / (24.0 * 60.0)) +
00225 (gmt->tm_sec / (24.0 * 60.0 * 60.0));
00226
00227
00228 jd = mjd + MJD0;
00229 SG_LOG( SG_EVENT, SG_DEBUG, " Current Julian Date = " << jd );
00230
00231
00232
00233
00234 if ( gst_diff < -100.0 ) {
00235
00236
00237 SG_LOG( SG_EVENT, SG_INFO, " First time, doing precise gst" );
00238 gst_precise = gst = sidereal_precise( mjd, 0.00 );
00239 gst_course = sidereal_course( cur_time, gmt, 0.00 );
00240
00241 gst_diff = gst_precise - gst_course;
00242
00243 lst = sidereal_course( cur_time, gmt,
00244 -(lon_rad * SGD_RADIANS_TO_DEGREES) ) + gst_diff;
00245 } else {
00246
00247 gst = sidereal_course( cur_time, gmt, 0.00 ) + gst_diff;
00248 lst = sidereal_course( cur_time, gmt,
00249 -(lon_rad * SGD_RADIANS_TO_DEGREES) ) + gst_diff;
00250 }
00251
00252 SG_LOG( SG_EVENT, SG_DEBUG,
00253 " Current lon=0.00 Sidereal Time = " << gst );
00254 SG_LOG( SG_EVENT, SG_DEBUG,
00255 " Current LOCAL Sidereal Time = " << lst << " ("
00256 << sidereal_precise( mjd, -(lon_rad * SGD_RADIANS_TO_DEGREES) )
00257 << ") (diff = " << gst_diff << ")" );
00258 }
00259
00260
00261
00262 void SGTime::updateLocal( double lon_rad, double lat_rad, const string& root ) {
00263
00264 if ( lon_rad < -SGD_PI || lon_rad> SGD_PI ) {
00265
00266 lon_rad = 0.0;
00267 }
00268 if ( lat_rad < -SGD_PI_2 || lat_rad > SGD_PI_2 ) {
00269
00270 lat_rad = 0.0;
00271 }
00272 if ( lon_rad != lon_rad ) {
00273
00274 SG_LOG( SG_EVENT, SG_ALERT,
00275 " Detected lon_rad == nan, resetting to 0.0" );
00276 lon_rad = 0.0;
00277 }
00278 if ( lat_rad != lat_rad ) {
00279
00280 SG_LOG( SG_EVENT, SG_ALERT,
00281 " Detected lat_rad == nan, resetting to 0.0" );
00282 lat_rad = 0.0;
00283 }
00284 time_t currGMT;
00285 time_t aircraftLocalTime;
00286 SGGeoCoord location( SGD_RADIANS_TO_DEGREES * lat_rad,
00287 SGD_RADIANS_TO_DEGREES * lon_rad );
00288 SGGeoCoord* nearestTz = tzContainer->getNearest(location);
00289 SGPath zone( root );
00290 zone.append ( nearestTz->getDescription() );
00291 zonename = zone.str();
00292
00293
00294 if (zonename[zonename.size()-1] == '\r')
00295 {
00296 zonename[zonename.size()-1]=0;
00297 zone.set( zonename );
00298 }
00299
00300 currGMT = sgTimeGetGMT( gmtime(&cur_time) );
00301 aircraftLocalTime = sgTimeGetGMT( (fgLocaltime(&cur_time, zone.c_str())) );
00302 local_offset = aircraftLocalTime - currGMT;
00303
00304
00305 }
00306
00307
00308
00309
00310
00311 double sgTimeCalcMJD(int mn, double dy, int yr) {
00312 double mjd;
00313
00314
00315 static double last_mjd, last_dy;
00316 static int last_mn, last_yr;
00317
00318 int b, d, m, y;
00319 long c;
00320
00321 if (mn == last_mn && yr == last_yr && dy == last_dy) {
00322 mjd = last_mjd;
00323 }
00324
00325 m = mn;
00326 y = (yr < 0) ? yr + 1 : yr;
00327 if (mn < 3) {
00328 m += 12;
00329 y -= 1;
00330 }
00331
00332 if (yr < 1582 || (yr == 1582 && (mn < 10 || (mn == 10 && dy < 15)))) {
00333 b = 0;
00334 } else {
00335 int a;
00336 a = y/100;
00337 b = 2 - a + a/4;
00338 }
00339
00340 if (y < 0) {
00341 c = (long)((365.25*y) - 0.75) - 694025L;
00342 } else {
00343 c = (long)(365.25*y) - 694025L;
00344 }
00345
00346 d = (int)(30.6001*(m+1));
00347
00348 mjd = b + c + d + dy - 0.5;
00349
00350 last_mn = mn;
00351 last_dy = dy;
00352 last_yr = yr;
00353 last_mjd = mjd;
00354
00355 return mjd;
00356 }
00357
00358
00359
00360
00361 double sgTimeCurrentMJD( time_t ct, long int warp ) {
00362
00363 struct tm m_gmt;
00364 struct tm *gmt = &m_gmt;
00365
00366
00367
00368 time_t cur_time;
00369 if ( ct ) {
00370 cur_time = ct + warp;
00371 } else {
00372 cur_time = time(NULL) + warp;
00373 }
00374 SG_LOG( SG_EVENT, SG_DEBUG,
00375 " Current Unix calendar time = " << cur_time
00376 << " warp = " << warp );
00377
00378
00379 memcpy( gmt, gmtime(&cur_time), sizeof(tm) );
00380 SG_LOG( SG_EVENT, SG_DEBUG,
00381 " Current GMT = " << gmt->tm_mon+1 << "/"
00382 << gmt->tm_mday << "/" << (1900 + gmt->tm_year) << " "
00383 << gmt->tm_hour << ":" << gmt->tm_min << ":"
00384 << gmt->tm_sec );
00385
00386
00387
00388
00389 double mjd = sgTimeCalcMJD( (int)(gmt->tm_mon+1), (double)gmt->tm_mday,
00390 (int)(gmt->tm_year + 1900) );
00391
00392 return mjd;
00393 }
00394
00395
00396
00397 double sgTimeCalcGST( double mjd ) {
00398 double gst;
00399
00400 double day = floor(mjd-0.5)+0.5;
00401 double hr = (mjd-day)*24.0;
00402 double T, x;
00403
00404 T = ((int)(mjd - 0.5) + 0.5 - J2000)/36525.0;
00405 x = 24110.54841 + (8640184.812866 + (0.093104 - 6.2e-6 * T) * T) * T;
00406 x /= 3600.0;
00407 gst = (1.0/SIDRATE)*hr + x;
00408
00409 SG_LOG( SG_EVENT, SG_DEBUG, " gst => " << gst );
00410
00411 return gst;
00412 }
00413
00414
00415 #if defined( HAVE_TIMEGM )
00416
00417 #elif defined( MK_TIME_IS_GMT )
00418
00419 #else // ! defined ( MK_TIME_IS_GMT )
00420
00421
00422 static long int fix_up_timezone( long int timezone_orig ) {
00423 # if !defined( HAVE_GETTIMEOFDAY ) && defined( HAVE_FTIME )
00424
00425 struct timeb current;
00426 ftime(¤t);
00427 return( current.timezone * 60 );
00428 # else
00429 return( timezone_orig );
00430 # endif
00431 }
00432 #endif
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466 time_t sgTimeGetGMT(int year, int month, int day, int hour, int min, int sec)
00467 {
00468 struct tm mt;
00469
00470 mt.tm_mon = month;
00471 mt.tm_mday = day;
00472 mt.tm_year = year;
00473 mt.tm_hour = hour;
00474 mt.tm_min = min;
00475 mt.tm_sec = sec;
00476 mt.tm_isdst = -1;
00477
00478
00479
00480
00481
00482 #if !defined(HAVE_DAYLIGHT)
00483 # define MK_TIME_IS_GMT 1
00484 #endif
00485
00486 #if defined( HAVE_TIMEGM )
00487 return ( timegm(&mt) );
00488 #elif defined( MK_TIME_IS_GMT )
00489 time_t ret = mktime(&mt);
00490
00491 #ifdef __CYGWIN__
00492 ret -= _timezone;
00493 #endif
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 errno = 0;
00506
00507 return ret;
00508 #else // ! defined ( MK_TIME_IS_GMT )
00509
00510
00511 # if defined( __linux__ ) || defined(__sun) ||defined(__CYGWIN__)
00512 # define TIMEZONE_OFFSET_WORKS 1
00513 # endif
00514
00515 #if defined(__CYGWIN__)
00516 #define TIMEZONE _timezone
00517 #else
00518 #define TIMEZONE timezone
00519 #endif
00520
00521 time_t start = mktime(&mt);
00522
00523 SG_LOG( SG_EVENT, SG_DEBUG, "start1 = " << start );
00524
00525
00526
00527 SG_LOG( SG_EVENT, SG_DEBUG, "(tm_isdst = " << mt.tm_isdst << ")" );
00528
00529 TIMEZONE = fix_up_timezone( TIMEZONE );
00530
00531 # if defined( TIMEZONE_OFFSET_WORKS )
00532 SG_LOG( SG_EVENT, SG_DEBUG,
00533 "start = " << start << ", timezone = " << TIMEZONE );
00534 return( start - TIMEZONE );
00535 # else // ! defined( TIMEZONE_OFFSET_WORKS )
00536
00537 daylight = mt.tm_isdst;
00538 if ( daylight > 0 ) {
00539 daylight = 1;
00540 } else if ( daylight < 0 ) {
00541 SG_LOG( SG_EVENT, FG_WARN,
00542 "OOOPS, problem in sg_time.cxx, no daylight savings info." );
00543 }
00544
00545 long int offset = -(TIMEZONE / 3600 - daylight);
00546
00547 SG_LOG( SG_EVENT, SG_DEBUG, " Raw time zone offset = " << TIMEZONE );
00548 SG_LOG( SG_EVENT, SG_DEBUG, " Daylight Savings = " << daylight );
00549 SG_LOG( SG_EVENT, SG_DEBUG, " Local hours from GMT = " << offset );
00550
00551 long int start_gmt = start - TIMEZONE + (daylight * 3600);
00552
00553 SG_LOG( SG_EVENT, SG_DEBUG, " March 21 noon (CST) = " << start );
00554
00555 return ( start_gmt );
00556 # endif // ! defined( TIMEZONE_OFFSET_WORKS )
00557 #endif // ! defined ( MK_TIME_IS_GMT )
00558 }
00559
00560
00561
00562 char* sgTimeFormatTime( const struct tm* p, char* buf )
00563 {
00564 sprintf( buf, "%d/%d/%2d %d:%02d:%02d",
00565 p->tm_mon, p->tm_mday, p->tm_year,
00566 p->tm_hour, p->tm_min, p->tm_sec);
00567 return buf;
00568 }