14#include <simgear/misc/sg_path.hxx>
15#include <simgear/timing/lowleveltime.h>
16#include <simgear/structure/commands.hxx>
17#include <simgear/timing/sg_time.hxx>
18#include <simgear/math/SGMath.hxx>
29static bool do_timeofday (
const SGPropertyNode * arg, SGPropertyNode * root)
31 const std::string &offset_type = arg->getStringValue(
"timeofday",
"noon");
32 int offset = arg->getIntValue(
"offset", 0);
34 if (offset_type ==
"real") {
53 globals->get_commands()->removeCommand(
"timeofday");
67 _mpProtocolClock = _steadyClock = 0.0;
68 _adjustWarpOnUnfreeze =
false;
70 _maxDtPerFrame =
fgGetNode(
"/sim/max-simtime-per-frame",
true);
71 _clockFreeze =
fgGetNode(
"/sim/freeze/clock",
true);
72 _timeOverride =
fgGetNode(
"/sim/time/cur-time-override",
true);
73 _warp =
fgGetNode(
"/sim/time/warp",
true);
74 _warp->addChangeListener(
this);
75 _maxFrameRate =
fgGetNode(
"/sim/frame-rate-throttle-hz",
true);
76 _localTimeStringNode =
fgGetNode(
"/sim/time/local-time-string",
true);
77 _localTimeZoneNode =
fgGetNode(
"/sim/time/local-timezone",
true);
78 _warpDelta =
fgGetNode(
"/sim/time/warp-delta",
true);
79 _frameNumber =
fgGetNode(
"/sim/frame-number",
true);
80 _simFixedDt =
fgGetNode(
"/sim/time/fixed-dt",
true);
82 SGPath zone(
globals->get_fg_root());
83 zone.append(
"Timezone");
85 _impl =
new SGTime(
globals->get_aircraft_position(), zone, _timeOverride->getLongValue());
87 _warpDelta->setDoubleValue(0.0);
90 _impl->update(
globals->get_aircraft_position(), _timeOverride->getLongValue(),
91 _warp->getIntValue());
92 globals->set_time_params(_impl);
95 _frameRate =
fgGetNode(
"/sim/frame-rate",
true);
96 _frameLatency =
fgGetNode(
"/sim/frame-latency-max-ms",
true);
97 _frameRateWorst =
fgGetNode(
"/sim/frame-rate-worst",
true);
99 _frameLatencyMax = 0.0;
102 _sceneryLoaded =
fgGetNode(
"sim/sceneryloaded",
true);
103 _modelHz =
fgGetNode(
"sim/model-hz",
true);
104 _timeDelta =
fgGetNode(
"sim/time/delta-realtime-sec",
true);
105 _simTimeDelta =
fgGetNode(
"sim/time/delta-sec",
true);
106 _mpProtocolClockNode =
fgGetNode(
"sim/time/mp-clock-sec",
true);
107 _steadyClockNode =
fgGetNode(
"sim/time/steady-clock-sec",
true);
108 _frameTimeOffsetNode =
fgGetNode(
"sim/time/frame-time-offset-ms",
true);
109 _dtRemainderNode =
fgGetNode(
"sim/time/dt-remainder-sec",
true);
110 _mpClockOffset =
fgGetNode(
"sim/time/mp-clock-offset-sec",
true);
111 _steadyClockDrift =
fgGetNode(
"sim/time/steady-clock-drift-ms",
true);
112 _computeDrift =
fgGetNode(
"sim/time/compute-clock-drift",
true);
113 _frameWait =
fgGetNode(
"sim/time/frame-wait-ms",
true);
114 _simTimeFactor =
fgGetNode(
"/sim/speed-up",
true);
116 if (!_simTimeDelta->hasValue()) {
117 _simTimeFactor->setDoubleValue(1.0);
119 if (!_mpClockOffset->hasValue()) {
120 _mpClockOffset->setDoubleValue(0.0);
122 _computeDrift->setBoolValue(
true);
124 _simpleTimeEnabledPrev =
false;
125 _simpleTimeEnabled =
fgGetNode(
"/sim/time/simple-time/enabled",
true);
126 _simpleTimeUtc =
fgGetNode(
"/sim/time/simple-time/utc",
true);
127 _simpleTimeFdm =
fgGetNode(
"/sim/time/simple-time/fdm",
true);
128 _simple_time_utc = 0;
129 _simple_time_fdm = 0;
134 _maxDtPerFrame.clear();
135 _clockFreeze.clear();
136 _timeOverride.clear();
140 _frameLatency.clear();
141 _frameRateWorst.clear();
143 _maxFrameRate.clear();
145 _sceneryLoaded.clear();
148 _simTimeDelta.clear();
149 _mpProtocolClockNode.clear();
150 _steadyClockNode.clear();
151 _frameTimeOffsetNode.clear();
152 _dtRemainderNode.clear();
153 _mpClockOffset.clear();
154 _steadyClockDrift.clear();
155 _computeDrift.clear();
156 _simTimeFactor.clear();
173 _warp->removeChangeListener(
this);
175 globals->set_time_params(NULL);
183 if (aProp == _warp) {
184 if (_clockFreeze->getBoolValue()) {
188 _adjustWarpOnUnfreeze =
false;
191 _impl->update(
globals->get_aircraft_position(),
192 _timeOverride->getLongValue(),
193 _warp->getIntValue());
206 auto t = std::chrono::system_clock::now().time_since_epoch();
207 typedef std::chrono::duration<double, std::ratio<1, 1>> duration_hz_fp;
208 auto ret = std::chrono::duration_cast<duration_hz_fp>(t);
215 double fixed_dt = _simFixedDt->getDoubleValue();
216 static double fixed_dt_prev = 0.0;
221 t = _simple_time_fdm + fixed_dt;
222 fixed_dt_prev = fixed_dt;
233 _simple_time_fdm = _simple_time_utc = t - fixed_dt_prev;
237 double modelHz = _modelHz->getDoubleValue();
238 bool scenery_loaded = _sceneryLoaded->getBoolValue();
241 _firstUpdate =
false;
242 _simple_time_utc = t;
243 _simple_time_fdm = t;
244 SGSubsystemGroup* fdmGroup =
globals->get_subsystem_mgr()->get_group(SGSubsystemMgr::FDM);
245 fdmGroup->set_fixed_update_time(1.0 / modelHz);
251 double sleep_time = 0;
252 if (scenery_loaded && !fixed_dt) {
253 double max_frame_rate = _maxFrameRate->getDoubleValue();
254 if (max_frame_rate != 0) {
255 double delay_end = _simple_time_utc + 1.0/max_frame_rate;
257 sleep_time = delay_end - t;
258 std::this_thread::sleep_for(std::chrono::milliseconds((
int) (sleep_time * 1000)));
273 double dt_fdm = floor( (t - _simple_time_fdm) * modelHz) / modelHz;
274 _simple_time_fdm += dt_fdm;
275 _frameLatencyMax = std::max(_frameLatencyMax, t - _simple_time_utc);
276 _simple_time_utc = t;
278 _simpleTimeUtc->setDoubleValue(_simple_time_utc);
279 _simpleTimeFdm->setDoubleValue(_simple_time_fdm);
283 if (_clockFreeze->getBoolValue() || !scenery_loaded) {
287 simDt = dt_fdm * _simTimeFactor->getDoubleValue();
290 globals->inc_sim_time_sec(simDt);
292 _mpProtocolClock = _simple_time_fdm;
293 _mpProtocolClockNode->setDoubleValue(_mpProtocolClock);
296 _steadyClock = _simple_time_fdm;
297 _steadyClockNode->setDoubleValue(_steadyClock);
301 _timeDelta->setDoubleValue(realDt);
302 _simTimeDelta->setDoubleValue(simDt);
304 SG_LOG(SG_GENERAL, SG_DEBUG,
""
305 << std::setprecision(5)
308 <<
" " << ((simDt >= 1.0) ?
"*" :
" ")
309 <<
" simDt=" << simDt
310 <<
" realDt=" << realDt
311 <<
" sleep_time=" << sleep_time
312 <<
" _simple_time_utc=" << _simple_time_utc
313 <<
" _simple_time_fdm=" << _simple_time_fdm
314 <<
" utc-fdm=" << (_simple_time_utc - _simple_time_fdm)
315 <<
" _steadyClock=" << _steadyClock
316 <<
" _mpProtocolClock=" << _mpProtocolClock
322 bool simple_time = _simpleTimeEnabled->getBoolValue();
323 if (simple_time != _simpleTimeEnabledPrev) {
324 _simpleTimeEnabledPrev = simple_time;
332 const double modelHz = _modelHz->getDoubleValue();
342 _systemStamp.systemClockHoursAndMinutes();
343 const double systemStamp = _systemStamp.toSecs();
344 _steadyClock = floor(systemStamp * modelHz) / modelHz;
347 const double frameOffsetMsec = _frameTimeOffsetNode->getDoubleValue();
348 _steadyClock += frameOffsetMsec / 1000.0;
351 _dtRemainder = systemStamp - _steadyClock;
353 _firstUpdate =
false;
354 _lastClockFreeze = _clockFreeze->getBoolValue();
357 bool wait_for_scenery = !_sceneryLoaded->getBoolValue();
358 if (!wait_for_scenery) {
359 throttleUpdateRate();
366 SGTimeStamp currentStamp;
367 currentStamp.stamp();
371 if (_computeDrift->getBoolValue()) {
372 _systemStamp.systemClockHoursAndMinutes();
373 double clockdrift = _steadyClock + (currentStamp - _lastStamp).toSecs()
374 + _dtRemainder - _systemStamp.toSecs();
375 _steadyClockDrift->setDoubleValue(clockdrift * 1000.0);
376 _computeDrift->setBoolValue(
false);
380 double fixed_dt = _simFixedDt->getDoubleValue();
381 double dt = (fixed_dt) ? fixed_dt : (currentStamp - _lastStamp).toSecs();
384 double mpProtocolDt = dt;
386 if (dt > _frameLatencyMax)
387 _frameLatencyMax = dt;
398 double dtMax = _maxDtPerFrame->getDoubleValue();
399 if (0 < dtMax && dtMax < dt) {
403 SGSubsystemGroup* fdmGroup =
404 globals->get_subsystem_mgr()->get_group(SGSubsystemMgr::FDM);
405 fdmGroup->set_fixed_update_time(1.0 / modelHz);
413 mpProtocolDt += _dtRemainder;
414 int multiLoop = long(floor(dt * modelHz));
415 multiLoop = SGMisc<long>::max(0, multiLoop);
416 _dtRemainder = dt - double(multiLoop)/modelHz;
417 dt = double(multiLoop)/modelHz;
418 mpProtocolDt -= _dtRemainder;
421 if (_clockFreeze->getBoolValue() || wait_for_scenery) {
425 simDt = dt * _simTimeFactor->getDoubleValue();
428 _lastStamp = currentStamp;
429 globals->inc_sim_time_sec(simDt);
430 _steadyClock += mpProtocolDt;
431 _mpProtocolClock = _steadyClock + _mpClockOffset->getDoubleValue();
433 _dtRemainderNode->setDoubleValue(_dtRemainder);
434 _steadyClockNode->setDoubleValue(_steadyClock);
435 _mpProtocolClockNode->setDoubleValue(_mpProtocolClock);
438 _timeDelta->setDoubleValue(realDt);
439 _simTimeDelta->setDoubleValue(simDt);
444 _frameNumber->setIntValue(_frameNumber->getIntValue() + 1);
445 bool freeze = _clockFreeze->getBoolValue();
446 time_t now = time(NULL);
450 if (_timeOverride->getLongValue() == 0) {
451 _timeOverride->setLongValue(now);
452 _adjustWarpOnUnfreeze =
true;
456 if (_lastClockFreeze) {
457 if (_adjustWarpOnUnfreeze) {
462 int adjust = _timeOverride->getLongValue() - now;
463 SG_LOG(SG_GENERAL, SG_DEBUG,
"adjusting on un-freeze:" << adjust);
464 _warp->setIntValue(_warp->getIntValue() + adjust);
466 _timeOverride->setLongValue(0);
475 double speedUp = _simTimeFactor->getDoubleValue() - 1.0;
476 if (speedUp != 0.0) {
477 double realDt = _timeDelta->getDoubleValue();
478 double speedUpOffset = speedUp * realDt;
479 _warp->setDoubleValue(_warp->getDoubleValue() + speedUpOffset);
485 int warpDelta = _warpDelta->getIntValue();
487 _adjustWarpOnUnfreeze =
false;
488 double warpOffset = warpDelta * _timeDelta->getDoubleValue();
489 _warp->setDoubleValue(_warp->getDoubleValue() + warpOffset);
492 const auto d2 = distSqr(_lastTimeZoneCheckPosition,
globals->get_aircraft_position_cart());
493 const auto oneNmSqr = SG_NM_TO_METER * SG_NM_TO_METER;
498 _lastClockFreeze = freeze;
499 _impl->update(
globals->get_aircraft_position(),
500 _timeOverride->getLongValue(),
501 _warp->getIntValue());
503 updateLocalTimeString();
507void TimeManager::computeFrameRate()
510 if ((_impl->get_cur_time() != _lastFrameTime)) {
511 _frameRate->setIntValue(_frameCount);
512 _frameLatency->setDoubleValue(_frameLatencyMax*1000);
513 if (_frameLatencyMax>0)
514 _frameRateWorst->setIntValue(1/_frameLatencyMax);
516 _frameLatencyMax = 0.0;
519 _lastFrameTime = _impl->get_cur_time();
523void TimeManager::throttleUpdateRate()
525 const double throttleHz = _maxFrameRate->getDoubleValue();
527 if (throttleHz <= 0) {
528 _frameWait->setDoubleValue(0);
532 const double modelHz = _modelHz->getDoubleValue();
533 SGTimeStamp frameWaitStart = SGTimeStamp::now();
537 const double t = (round(modelHz / throttleHz) / modelHz) - _dtRemainder;
538 SGTimeStamp::sleepUntil(_lastStamp + SGTimeStamp::fromSec(t));
539 _frameWait->setDoubleValue(frameWaitStart.elapsedMSec());
545 _lastTimeZoneCheckPosition = SGVec3d::zeros();
549void TimeManager::updateLocalTime()
551 _lastTimeZoneCheckPosition =
globals->get_aircraft_position_cart();
552 _impl->updateLocal(
globals->get_aircraft_position(),
globals->get_fg_root() /
"Timezone");
554 updateLocalTimeString();
557void TimeManager::updateLocalTimeString()
559 time_t cur_time = _impl->get_cur_time();
560 if (!_impl->get_zonename()) {
564 struct tm* aircraftLocalTime = fgLocaltime(&cur_time, _impl->get_zonename());
566 snprintf(buf, 16,
"%.2d:%.2d:%.2d",
567 aircraftLocalTime->tm_hour,
568 aircraftLocalTime->tm_min, aircraftLocalTime->tm_sec);
571 std::string s = _localTimeStringNode->getStringValue();
572 if (s != std::string(buf)) {
573 _localTimeStringNode->setStringValue(buf);
576 std::string zs = _localTimeZoneNode->getStringValue();
577 if (zs != std::string(_impl->get_description())) {
578 _localTimeZoneNode->setStringValue(_impl->get_description());
582void TimeManager::initTimeOffset()
585 long int offset =
fgGetLong(
"/sim/startup/time-offset");
586 std::string offset_type =
fgGetString(
"/sim/startup/time-offset-type");
593 int orig_warp = _warp->getIntValue();
594 time_t cur_time = _impl->get_cur_time();
595 time_t currGMT = sgTimeGetGMT( gmtime(&cur_time) );
596 time_t systemLocalTime = sgTimeGetGMT( localtime(&cur_time) );
597 time_t aircraftLocalTime =
598 sgTimeGetGMT( fgLocaltime(&cur_time, _impl->get_zonename() ) );
601 SGGeod loc =
globals->get_aircraft_position();
604 if ( offset_type ==
"real" ) {
606 }
else if ( offset_type ==
"dawn" ) {
608 }
else if ( offset_type ==
"morning" ) {
610 }
else if ( offset_type ==
"noon" ) {
612 }
else if ( offset_type ==
"afternoon" ) {
614 }
else if ( offset_type ==
"dusk" ) {
616 }
else if ( offset_type ==
"evening" ) {
618 }
else if ( offset_type ==
"midnight" ) {
620 }
else if ( offset_type ==
"system-offset" ) {
623 }
else if ( offset_type ==
"gmt-offset" ) {
624 warp = offset - (currGMT - systemLocalTime);
626 }
else if ( offset_type ==
"latitude-offset" ) {
627 warp = offset - (aircraftLocalTime - systemLocalTime);
629 }
else if ( offset_type ==
"system" ) {
630 warp = offset - (systemLocalTime - currGMT) - cur_time;
631 }
else if ( offset_type ==
"gmt" ) {
632 warp = offset - cur_time;
633 }
else if ( offset_type ==
"latitude" ) {
634 warp = offset - (aircraftLocalTime - currGMT)- cur_time;
636 SG_LOG( SG_GENERAL, SG_ALERT,
637 "TimeManager::setTimeOffset: unsupported offset: " << offset_type );
641 if(
fgGetBool(
"/sim/time/warp-easing",
false) && !
fgGetBool(
"/devices/status/keyboard/ctrl",
false)) {
642 double duration =
fgGetDouble(
"/sim/time/warp-easing-duration-secs", 5.0 );
643 const std::string easing =
fgGetString(
"/sim/time/warp-easing-method",
"swing" );
645 n.setDoubleValue( orig_warp + warp );
646 _warp->interpolate(
"numeric", n, duration, easing );
648 _warp->setIntValue( orig_warp + warp );
651 SG_LOG(SG_GENERAL, SG_INFO,
"After TimeManager::setTimeOffset(): " << offset_type <<
", warp = " << _warp->getIntValue());
656 return _simTimeFactor->getDoubleValue();
661 SGSubsystemMgr::INIT,
662 {{
"FDM", SGSubsystemMgr::Dependency::HARD}});
static bool do_timeofday(const SGPropertyNode *arg, SGPropertyNode *root)
SGSubsystemMgr::Registrant< TimeManager > registrantTimeManager(SGSubsystemMgr::INIT, {{"FDM", SGSubsystemMgr::Dependency::HARD}})
time_t fgTimeSecondsUntilBodyAngle(time_t cur_time, const SGGeod &loc, double target_angle_deg, bool ascending, bool sun_not_moon)
Given the current unix time in seconds, calculate seconds to the specified body angle (relative to st...
void setTimeOffset(const std::string &offset_type, long int offset)
double getSimSpeedUpFactor() const
void computeTimeDeltas(double &simDt, double &realDt)
void computeTimeDeltasSimple(double &simDt, double &realDt)
void valueChanged(SGPropertyNode *) override
void update(double dt) override
long fgGetLong(const char *name, long defaultValue)
Get a long value for a property.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
bool fgSetInt(const char *name, int val)
Set an int value for a property.
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.