diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 38370e9f5..08a20044f 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -108,8 +108,9 @@ #include <Systems/system_mgr.hxx> #include <Time/FGEventMgr.hxx> #include <Time/light.hxx> -#include <Time/sunpos.hxx> #include <Time/moonpos.hxx> +#include <Time/sunpos.hxx> +#include <Time/sunsolver.hxx> #include <Time/tmp.hxx> #ifdef FG_MPLAYER_AS @@ -1255,38 +1256,6 @@ SGTime *fgInitTime() { zone.str(), cur_time_override->getLongValue() ); - // Handle potential user specified time offsets - time_t cur_time = t->get_cur_time(); - time_t currGMT = sgTimeGetGMT( gmtime(&cur_time) ); - time_t systemLocalTime = sgTimeGetGMT( localtime(&cur_time) ); - time_t aircraftLocalTime = - sgTimeGetGMT( fgLocaltime(&cur_time, t->get_zonename() ) ); - - // Okay, we now have six possible scenarios - int offset = fgGetInt("/sim/startup/time-offset"); - const string &offset_type = fgGetString("/sim/startup/time-offset-type"); - if (offset_type == "system-offset") { - globals->set_warp( offset ); - } else if (offset_type == "gmt-offset") { - globals->set_warp( offset - (currGMT - systemLocalTime) ); - } else if (offset_type == "latitude-offset") { - globals->set_warp( offset - (aircraftLocalTime - systemLocalTime) ); - } else if (offset_type == "system") { - globals->set_warp( offset - cur_time ); - } else if (offset_type == "gmt") { - globals->set_warp( offset - currGMT ); - } else if (offset_type == "latitude") { - globals->set_warp( offset - (aircraftLocalTime - systemLocalTime) - - cur_time ); - } else { - SG_LOG( SG_GENERAL, SG_ALERT, - "FG_TIME::Unsupported offset type " << offset_type ); - exit( -1 ); - } - - SG_LOG( SG_GENERAL, SG_INFO, "After time init, warp = " - << globals->get_warp() ); - globals->set_warp_delta( 0 ); t->update( 0.0, 0.0, @@ -1297,6 +1266,78 @@ SGTime *fgInitTime() { } +// set up a time offset (aka warp) if one is specified +void fgInitTimeOffset() { + static const SGPropertyNode *longitude + = fgGetNode("/position/longitude-deg"); + static const SGPropertyNode *latitude + = fgGetNode("/position/latitude-deg"); + static const SGPropertyNode *cur_time_override + = fgGetNode("/sim/time/cur-time-override", true); + + // Handle potential user specified time offsets + SGTime *t = globals->get_time_params(); + time_t cur_time = t->get_cur_time(); + time_t currGMT = sgTimeGetGMT( gmtime(&cur_time) ); + time_t systemLocalTime = sgTimeGetGMT( localtime(&cur_time) ); + time_t aircraftLocalTime = + sgTimeGetGMT( fgLocaltime(&cur_time, t->get_zonename() ) ); + + // Okay, we now have several possible scenarios + int offset = fgGetInt("/sim/startup/time-offset"); + int warp = 0; + const string &offset_type = fgGetString("/sim/startup/time-offset-type"); + if ( offset_type == "noon" ) { + warp = fgTimeSecondsUntilNoon( cur_time, + longitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS, + latitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS ); + } else if ( offset_type == "midnight" ) { + warp = fgTimeSecondsUntilMidnight( cur_time, + longitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS, + latitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS ); + } else if ( offset_type == "dawn" ) { + warp = fgTimeSecondsUntilDawn( cur_time, + longitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS, + latitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS ); + } else if ( offset_type == "dusk" ) { + warp = fgTimeSecondsUntilDusk( cur_time, + longitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS, + latitude->getDoubleValue() + * SGD_DEGREES_TO_RADIANS ); + } else if ( offset_type == "system-offset" ) { + warp = offset; + } else if ( offset_type == "gmt-offset" ) { + warp = offset - (currGMT - systemLocalTime); + } else if ( offset_type == "latitude-offset" ) { + warp = offset - (aircraftLocalTime - systemLocalTime); + } else if ( offset_type == "system" ) { + warp = offset - cur_time; + } else if ( offset_type == "gmt" ) { + warp = offset - currGMT; + } else if ( offset_type == "latitude" ) { + warp = offset - (aircraftLocalTime - systemLocalTime) - cur_time; + } else { + SG_LOG( SG_GENERAL, SG_ALERT, + "FG_TIME::Unsupported offset type " << offset_type ); + exit( -1 ); + } + globals->set_warp( warp ); + t->update( 0.0, 0.0, cur_time_override->getLongValue(), + globals->get_warp() ); + + SG_LOG( SG_GENERAL, SG_INFO, "After fgInitTimeOffset(): warp = " + << globals->get_warp() ); + + fgUpdateSkyAndLightingParams(); +} + // This is the top level init routine which calls all the other // initialization routines. If you are adding a subsystem to flight // gear, its initialization call should located in this routine. diff --git a/src/Main/fg_init.hxx b/src/Main/fg_init.hxx index 56325fca1..adfc4cc3c 100644 --- a/src/Main/fg_init.hxx +++ b/src/Main/fg_init.hxx @@ -90,6 +90,10 @@ bool fgInitPosition(); // returns a new instance of the SGTime class SGTime *fgInitTime(); +// set up a time offset (aka warp) if one is specified +void fgInitTimeOffset(); + + #endif // _FG_INIT_HXX diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 4f2947fbd..0a6a53f91 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -954,8 +954,8 @@ void fgUpdateTimeDepCalcs() { // Update solar system globals->get_ephem()->update( globals->get_time_params()->getMjd(), - globals->get_time_params()->getLst(), - cur_fdm_state->get_Latitude() ); + globals->get_time_params()->getLst(), + cur_fdm_state->get_Latitude() ); // Update radio stack model current_radiostack->update(delta_time_sec); @@ -1369,6 +1369,10 @@ static void fgIdleFunction ( void ) { idle_state++; } else if ( idle_state == 4 ) { + // Initialize the time offset (warp) after fgInitSubsystem + // (which initializes the lighting interpolation tables.) + fgInitTimeOffset(); + // setup OpenGL view parameters fgInitVisuals(); @@ -1381,6 +1385,9 @@ static void fgIdleFunction ( void ) { idle_state++; } else if ( idle_state == 6 ) { // sleep(1); + + fgUpdateSkyAndLightingParams(); + idle_state = 1000; SG_LOG( SG_GENERAL, SG_INFO, "Panel visible = " << fgPanelVisible() ); @@ -1725,11 +1732,11 @@ bool fgMainInit( int argc, char **argv ) { ephem_data_path.append( "Astro" ); SGEphemeris *ephem = new SGEphemeris( ephem_data_path.c_str() ); ephem->update( globals->get_time_params()->getMjd(), - globals->get_time_params()->getLst(), - 0.0 ); + globals->get_time_params()->getLst(), + 0.0 ); globals->set_ephem( ephem ); - // TODO: move to environment mgr + // TODO: move to environment mgr thesky = new SGSky; SGPath texture_path(globals->get_fg_root()); texture_path.append("Textures"); @@ -1739,7 +1746,6 @@ bool fgMainInit( int argc, char **argv ) { thesky->add_cloud_layer(layer); } - SGPath sky_tex_path( globals->get_fg_root() ); sky_tex_path.append( "Textures" ); sky_tex_path.append( "Sky" ); diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 0d178669f..006fea4e4 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1311,6 +1311,7 @@ struct OptionDesc { {"bpp", true, OPTION_FUNC, "", false, "", fgOptBpp }, {"units-feet", false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 }, {"units-meters", false, OPTION_STRING, "/sim/startup/units", false, "meters", 0 }, + {"timeofday", true, OPTION_STRING, "/sim/startup/time-offset-type", false, "noon", 0 }, {"time-offset", true, OPTION_FUNC, "", false, "", fgOptTimeOffset }, {"time-match-real", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "system-offset", 0 }, {"time-match-local", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "latitude-offset", 0 }, diff --git a/src/Model/acmodel.cxx b/src/Model/acmodel.cxx index 90a70f8f6..7096473e8 100644 --- a/src/Model/acmodel.cxx +++ b/src/Model/acmodel.cxx @@ -38,7 +38,7 @@ FGAircraftModel::FGAircraftModel () _selector(new ssgSelector), _scene(new ssgRoot), _nearplane(0.01f), - _farplane(100.0f) + _farplane(1000.0f) { } diff --git a/src/Time/Makefile.am b/src/Time/Makefile.am index 9a0be35ec..02e4ef5d3 100644 --- a/src/Time/Makefile.am +++ b/src/Time/Makefile.am @@ -6,6 +6,7 @@ libTime_a_SOURCES = \ light.cxx light.hxx \ moonpos.cxx moonpos.hxx \ sunpos.cxx sunpos.hxx \ + sunsolver.cxx sunsolver.hxx \ tmp.cxx tmp.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Time/moonpos.hxx b/src/Time/moonpos.hxx index 8551afebd..ab22d26d8 100644 --- a/src/Time/moonpos.hxx +++ b/src/Time/moonpos.hxx @@ -61,4 +61,4 @@ void fgUpdateMoonPos( void ); void fgMoonPosition(time_t ssue, double *lon, double *lat); -#endif /* _MOONPOS_H */ +#endif /* _MOONPOS_HXX */ diff --git a/src/Time/sunpos.cxx b/src/Time/sunpos.cxx index ebc45f9d5..3cd794c15 100644 --- a/src/Time/sunpos.cxx +++ b/src/Time/sunpos.cxx @@ -211,7 +211,7 @@ void fgSunPosition(time_t ssue, double *lon, double *lat) { * meridian (GST), compute position on the earth (lat, lon) such that * sun is directly overhead. (lat, lon are reported in radians */ -static void fgSunPositionGST(double gst, double *lon, double *lat) { +void fgSunPositionGST(double gst, double *lon, double *lat) { /* time_t ssue; seconds since unix epoch */ /* double *lat; (return) latitude */ /* double *lon; (return) longitude */ @@ -227,7 +227,7 @@ static void fgSunPositionGST(double gst, double *lon, double *lat) { globals->get_ephem()->get_sun()->getLat(), &alpha, &delta ); -// tmp = alpha - (SGD_2PI/24)*GST(ssue); + // tmp = alpha - (SGD_2PI/24)*GST(ssue); tmp = alpha - (SGD_2PI/24)*gst; if (tmp < -SGD_PI) { do tmp += SGD_2PI; diff --git a/src/Time/sunpos.hxx b/src/Time/sunpos.hxx index 7d38a9abf..b2919f044 100644 --- a/src/Time/sunpos.hxx +++ b/src/Time/sunpos.hxx @@ -60,5 +60,10 @@ void fgUpdateMoonPos( void ); void fgSunPosition(time_t ssue, double *lon, double *lat); +/* given a particular time expressed in side real time at prime + * meridian (GST), compute position on the earth (lat, lon) such that + * sun is directly overhead. (lat, lon are reported in radians */ +void fgSunPositionGST(double gst, double *lon, double *lat); -#endif /* _SUNPOS_H */ + +#endif /* _SUNPOS_HXX */ diff --git a/src/Time/sunsolver.cxx b/src/Time/sunsolver.cxx new file mode 100644 index 000000000..c04e366dd --- /dev/null +++ b/src/Time/sunsolver.cxx @@ -0,0 +1,238 @@ +/* + * sunsolver.cxx - given a location on earth and a time of day/date, + * find the number of seconds to various sun positions. + * + * Written by Curtis Olson, started September 2003. + * + * Copyright (C) 2003 Curtis L. Olson - curt@flightgear.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + + +#include <simgear/math/point3d.hxx> +#include <simgear/math/sg_geodesy.hxx> +#include <simgear/timing/sg_time.hxx> + +#include <Main/globals.hxx> + +#include "sunpos.hxx" + +#include "sunsolver.hxx" + + +const time_t day_secs = 86400; + +static double sun_angle( const SGTime &t, sgVec3 world_up, + double lon_rad, double lat_rad ) { + sgVec3 nup, nsun; + Point3D p, rel_sunpos; + + SG_LOG( SG_EVENT, SG_DEBUG, " Updating Sun position" ); + SG_LOG( SG_EVENT, SG_DEBUG, " Gst = " << t.getGst() ); + + double sun_lon, sun_gd_lat, sun_gc_lat, sl_radius; + fgSunPositionGST( t.getGst(), &sun_lon, &sun_gd_lat ); + + sgGeodToGeoc(sun_gd_lat, 0.0, &sl_radius, &sun_gc_lat); + + p = Point3D( sun_lon, sun_gc_lat, sl_radius ); + Point3D sunpos = sgPolarToCart3d(p); + + SG_LOG( SG_EVENT, SG_DEBUG, " t.cur_time = " << t.get_cur_time() ); + SG_LOG( SG_EVENT, SG_DEBUG, + " Sun Geodetic lat = " << sun_gd_lat + << " Geocentric lat = " << sun_gc_lat ); + + // calculate the sun's relative angle to local up + sgCopyVec3( nup, world_up ); + sgSetVec3( nsun, sunpos.x(), sunpos.y(), sunpos.z() ); + sgNormalizeVec3(nup); + sgNormalizeVec3(nsun); + // cout << "nup = " << nup[0] << "," << nup[1] << "," + // << nup[2] << endl; + // cout << "nsun = " << nsun[0] << "," << nsun[1] << "," + // << nsun[2] << endl; + + double sun_angle = acos( sgScalarProductVec3 ( nup, nsun ) ); + double sun_angle_deg = sun_angle * SG_RADIANS_TO_DEGREES; + while ( sun_angle_deg < -180 ) { sun_angle += 360; } + SG_LOG( SG_EVENT, SG_DEBUG, "sun angle relative to current location = " + << sun_angle_deg ); + + return sun_angle_deg; +} + + +/** + * Given the current unix time in seconds, calculate seconds to noon + */ +time_t fgTimeSecondsUntilNoon( time_t cur_time, + double lon_rad, + double lat_rad ) +{ + // cout << "location = " << lon_rad * SG_RADIANS_TO_DEGREES << ", " + // << lat_rad * SG_RADIANS_TO_DEGREES << endl; + Point3D geod( lon_rad, lat_rad, 0 ); + Point3D tmp = sgGeodToCart( geod ); + sgVec3 world_up; + sgSetVec3( world_up, tmp.x(), tmp.y(), tmp.z() ); + SGTime t = SGTime( lon_rad, lat_rad, "", 0 ); + + double best_angle = 180.0; + time_t best_time = cur_time; + + for ( time_t secs = cur_time; secs < cur_time + day_secs; secs += 300 ) { + t.update( lon_rad, lat_rad, secs, 0 ); + double angle = sun_angle( t, world_up, lon_rad, lat_rad ); + if ( angle < best_angle ) { + // cout << "best angle = " << angle << " offset = " + // << secs - cur_time << endl; + best_angle = angle; + best_time = secs; + } + } + + if ( best_time > day_secs / 2 ) { + best_time -= day_secs; + } + + return best_time - cur_time; +} + + +/** + * Given the current unix time in seconds, calculate seconds to midnight + */ +time_t fgTimeSecondsUntilMidnight( time_t cur_time, + double lon_rad, + double lat_rad ) +{ + // cout << "location = " << lon_rad * SG_RADIANS_TO_DEGREES << ", " + // << lat_rad * SG_RADIANS_TO_DEGREES << endl; + Point3D geod( lon_rad, lat_rad, 0 ); + Point3D tmp = sgGeodToCart( geod ); + sgVec3 world_up; + sgSetVec3( world_up, tmp.x(), tmp.y(), tmp.z() ); + SGTime t = SGTime( lon_rad, lat_rad, "", 0 ); + + double best_angle = 0.0; + time_t best_time = cur_time; + + for ( time_t secs = cur_time; secs < cur_time + day_secs; secs += 300 ) { + t.update( lon_rad, lat_rad, secs, 0 ); + double angle = sun_angle( t, world_up, lon_rad, lat_rad ); + if ( angle > best_angle ) { + // cout << "best angle = " << angle << " offset = " + // << secs - cur_time << endl; + best_angle = angle; + best_time = secs; + } + } + + if ( best_time > day_secs / 2 ) { + best_time -= day_secs; + } + + return best_time - cur_time; +} + + +/** + * Given the current unix time in seconds, calculate seconds to dusk + */ +time_t fgTimeSecondsUntilDusk( time_t cur_time, + double lon_rad, + double lat_rad ) +{ + // cout << "location = " << lon_rad * SG_RADIANS_TO_DEGREES << ", " + // << lat_rad * SG_RADIANS_TO_DEGREES << endl; + Point3D geod( lon_rad, lat_rad, 0 ); + Point3D tmp = sgGeodToCart( geod ); + sgVec3 world_up; + sgSetVec3( world_up, tmp.x(), tmp.y(), tmp.z() ); + SGTime t = SGTime( lon_rad, lat_rad, "", 0 ); + + double best_diff = 90.0; + double last_angle = -99999.0; + time_t best_time = cur_time; + + for ( time_t secs = cur_time; secs < cur_time + day_secs; secs += 300 ) { + t.update( lon_rad, lat_rad, secs, 0 ); + double angle = sun_angle( t, world_up, lon_rad, lat_rad ); + double diff = fabs( angle - 90.0 ); + if ( diff < best_diff ) { + if ( last_angle <= 180.0 && ( last_angle < angle ) ) { + // cout << "best angle = " << angle << " offset = " + // << secs - cur_time << endl; + best_diff = diff; + best_time = secs; + } + } + + last_angle = angle; + } + + if ( best_time > day_secs / 2 ) { + best_time -= day_secs; + } + + return best_time - cur_time; +} + + +/** + * Given the current unix time in seconds, calculate seconds to dawn + */ +time_t fgTimeSecondsUntilDawn( time_t cur_time, + double lon_rad, + double lat_rad ) +{ + // cout << "location = " << lon_rad * SG_RADIANS_TO_DEGREES << ", " + // << lat_rad * SG_RADIANS_TO_DEGREES << endl; + Point3D geod( lon_rad, lat_rad, 0 ); + Point3D tmp = sgGeodToCart( geod ); + sgVec3 world_up; + sgSetVec3( world_up, tmp.x(), tmp.y(), tmp.z() ); + SGTime t = SGTime( lon_rad, lat_rad, "", 0 ); + + double best_diff = 90.0; + double last_angle = -99999.0; + time_t best_time = cur_time; + + for ( time_t secs = cur_time; secs < cur_time + day_secs; secs += 300 ) { + t.update( lon_rad, lat_rad, secs, 0 ); + double angle = sun_angle( t, world_up, lon_rad, lat_rad ); + double diff = fabs( angle - 90.0 ); + if ( diff < best_diff ) { + if ( last_angle <= 180.0 && ( last_angle > angle ) ) { + // cout << "best angle = " << angle << " offset = " + // << secs - cur_time << endl; + best_diff = diff; + best_time = secs; + } + } + + last_angle = angle; + } + + if ( best_time > day_secs / 2 ) { + best_time -= day_secs; + } + + return best_time - cur_time; +} diff --git a/src/Time/sunsolver.hxx b/src/Time/sunsolver.hxx new file mode 100644 index 000000000..6ba4c4208 --- /dev/null +++ b/src/Time/sunsolver.hxx @@ -0,0 +1,77 @@ +/* + * sunsolver.hxx - given a location on earth and a time of day/date, + * find the number of seconds to various sun positions. + * + * Written by Curtis Olson, started September 2003. + * + * Copyright (C) 2003 Curtis L. Olson - curt@flightgear.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + + +#ifndef _SUNSOLVER_HXX +#define _SUNSOLVER_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include <simgear/compiler.h> + +#ifdef SG_HAVE_STD_INCLUDES +# include <ctime> +#else +# include <time.h> +#endif + +/** + * Given the current unix time in seconds, calculate seconds to + * highest sun angle. + */ +time_t fgTimeSecondsUntilNoon( time_t cur_time, + double lon_rad, + double lat_rad ); + + +/** + * Given the current unix time in seconds, calculate seconds to lowest + * sun angle. + */ +time_t fgTimeSecondsUntilMidnight( time_t cur_time, + double lon_rad, + double lat_rad ); + +/** + * Given the current unix time in seconds, calculate seconds to dusk + */ +time_t fgTimeSecondsUntilDusk( time_t cur_time, + double lon_rad, + double lat_rad ); + + +/** + * Given the current unix time in seconds, calculate seconds to dawn + */ +time_t fgTimeSecondsUntilDawn( time_t cur_time, + double lon_rad, + double lat_rad ); + + + +#endif /* _SUNSOLVER_HXX */