637 lines
20 KiB
C++
637 lines
20 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file clockObject.cxx
|
|
* @author drose
|
|
* @date 2000-02-17
|
|
*/
|
|
|
|
#include "clockObject.h"
|
|
#include "config_putil.h"
|
|
#include "configVariableEnum.h"
|
|
#include "string_utils.h"
|
|
#include "thread.h"
|
|
|
|
using std::istream;
|
|
using std::ostream;
|
|
using std::string;
|
|
|
|
void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait;
|
|
void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait;
|
|
void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait;
|
|
|
|
AtomicAdjust::Pointer ClockObject::_global_clock = nullptr;
|
|
TypeHandle ClockObject::_type_handle;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
ClockObject::
|
|
ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) {
|
|
_true_clock = TrueClock::get_global_ptr();
|
|
|
|
_start_short_time = _true_clock->get_short_time();
|
|
_start_long_time = _true_clock->get_long_time();
|
|
_actual_frame_time = 0.0;
|
|
|
|
ConfigVariableDouble max_dt
|
|
("max-dt", -1.0,
|
|
PRC_DESC("Sets a limit on the value returned by ClockObject::get_dt(). If "
|
|
"this value is less than zero, no limit is imposed; "
|
|
"otherwise, this is the maximum value that will ever "
|
|
"be returned by get_dt(), regardless of how much time "
|
|
"has actually elapsed between frames. See ClockObject::set_dt()."));
|
|
ConfigVariableDouble clock_frame_rate
|
|
("clock-frame-rate", 1.0,
|
|
PRC_DESC("In non-real-time clock mode, sets the number of frames per "
|
|
"second that we should appear to be running. In forced "
|
|
"mode or limited mode, sets our target frame rate. In "
|
|
"normal mode, this has no effect. See ClockObject::set_frame_rate()."));
|
|
ConfigVariableDouble clock_degrade_factor
|
|
("clock-degrade-factor", 1.0,
|
|
PRC_DESC("In degrade clock mode, returns the ratio by which the "
|
|
"performance is degraded. A value of 2.0 causes the "
|
|
"clock to be slowed down by a factor of two (reducing "
|
|
"performance to 1/2 what would be otherwise). See ClockObject::set_degrade_factor()."));
|
|
ConfigVariableDouble average_frame_rate_interval
|
|
("average-frame-rate-interval", 1.0,
|
|
PRC_DESC("See ClockObject::set_average_frame_rate_interval()."));
|
|
|
|
_max_dt = max_dt;
|
|
_user_frame_rate = clock_frame_rate;
|
|
_degrade_factor = clock_degrade_factor;
|
|
_average_frame_rate_interval = average_frame_rate_interval;
|
|
|
|
_error_count = _true_clock->get_error_count();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
ClockObject::
|
|
ClockObject(const ClockObject ©) :
|
|
_true_clock(copy._true_clock),
|
|
_mode(copy._mode),
|
|
_start_short_time(copy._start_short_time),
|
|
_start_long_time(copy._start_long_time),
|
|
_actual_frame_time(copy._actual_frame_time),
|
|
_max_dt(copy._max_dt),
|
|
_user_frame_rate(copy._user_frame_rate),
|
|
_degrade_factor(copy._degrade_factor),
|
|
_error_count(copy._error_count),
|
|
_average_frame_rate_interval(copy._average_frame_rate_interval),
|
|
_ticks(copy._ticks),
|
|
_cycler(copy._cycler)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Changes the mode of the clock. Normally, the clock is in mode M_normal.
|
|
* In this mode, each call to tick() will set the value returned by
|
|
* get_frame_time() to the current real time; thus, the clock simply reports
|
|
* time advancing.
|
|
*
|
|
* Other possible modes:
|
|
*
|
|
* M_non_real_time - the clock ignores real time completely; at each call to
|
|
* tick(), it pretends that exactly dt seconds have elapsed since the last
|
|
* call to tick(). You may set the value of dt with set_dt() or
|
|
* set_frame_rate().
|
|
*
|
|
* M_limited - the clock will run as fast as it can, as in M_normal, but will
|
|
* not run faster than the rate specified by set_frame_rate(). If the
|
|
* application would run faster than this rate, the clock will slow down the
|
|
* application.
|
|
*
|
|
* M_integer - the clock will run as fast as it can, but the rate will be
|
|
* constrained to be an integer multiple or divisor of the rate specified by
|
|
* set_frame_rate(). The clock will slow down the application a bit to
|
|
* guarantee this.
|
|
*
|
|
* M_integer_limited - a combination of M_limited and M_integer; the clock
|
|
* will not run faster than set_frame_rate(), and if it runs slower, it will
|
|
* run at a integer divisor of that rate.
|
|
*
|
|
* M_forced - the clock forces the application to run at the rate specified by
|
|
* set_frame_rate(). If the application would run faster than this rate, the
|
|
* clock will slow down the application; if the application would run slower
|
|
* than this rate, the clock slows down time so that the application believes
|
|
* it is running at the given rate.
|
|
*
|
|
* M_degrade - the clock runs at real time, but the application is slowed down
|
|
* by a set factor of its frame rate, specified by set_degrade_factor().
|
|
*
|
|
* M_slave - the clock does not advance, but relies on the user to call
|
|
* set_frame_time() and/or set_frame_count() each frame.
|
|
*/
|
|
void ClockObject::
|
|
set_mode(ClockObject::Mode mode) {
|
|
Thread *current_thread = Thread::get_current_thread();
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
CDWriter cdata(_cycler, current_thread);
|
|
|
|
_mode = mode;
|
|
|
|
// In case we have set the clock to one of the modes that uses
|
|
// _reported_frame_time_epoch, recompute the epoch.
|
|
switch (_mode) {
|
|
case M_non_real_time:
|
|
case M_forced:
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
cdata->_dt = 1.0 / _user_frame_rate;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the clock to the indicated time. This changes only the real time of
|
|
* the clock as reported by get_real_time(), but does not immediately change
|
|
* the time reported by get_frame_time()--that will change after the next call
|
|
* to tick(). Also see reset(), set_frame_time(), and set_frame_count().
|
|
*/
|
|
void ClockObject::
|
|
set_real_time(double time) {
|
|
#ifdef NOTIFY_DEBUG
|
|
// This is only a debug message, since it happens during normal development,
|
|
// particularly at startup, or whenever you break into the task loop.
|
|
if (util_cat.is_debug() && this == _global_clock) {
|
|
util_cat.debug()
|
|
<< "Adjusting global clock's real time by " << time - get_real_time()
|
|
<< " seconds.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
_start_short_time = _true_clock->get_short_time() - time;
|
|
_start_long_time = _true_clock->get_long_time() - time;
|
|
}
|
|
|
|
/**
|
|
* Changes the time as reported for the current frame to the indicated time.
|
|
* Normally, the way to adjust the frame time is via tick(); this function is
|
|
* provided only for occasional special adjustments.
|
|
*/
|
|
void ClockObject::
|
|
set_frame_time(double time, Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
#ifdef NOTIFY_DEBUG
|
|
if (this == _global_clock && _mode != M_slave) {
|
|
util_cat.warning()
|
|
<< "Adjusting global clock's frame time by " << time - get_frame_time()
|
|
<< " seconds.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
CDWriter cdata(_cycler, current_thread);
|
|
_actual_frame_time = time;
|
|
cdata->_reported_frame_time = time;
|
|
|
|
// Recompute the epoch in case we are in a mode that relies on this.
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
}
|
|
|
|
/**
|
|
* Resets the number of frames counted to the indicated number. Also see
|
|
* reset(), set_real_time(), and set_frame_time().
|
|
*/
|
|
void ClockObject::
|
|
set_frame_count(int frame_count, Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
#ifdef NOTIFY_DEBUG
|
|
if (this == _global_clock && _mode != M_slave) {
|
|
util_cat.warning()
|
|
<< "Adjusting global clock's frame count by "
|
|
<< frame_count - get_frame_count() << " frames.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
CDWriter cdata(_cycler, current_thread);
|
|
cdata->_frame_count = frame_count;
|
|
|
|
// Recompute the epoch in case we are in a mode that relies on this.
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
}
|
|
|
|
/**
|
|
* In non-real-time mode, sets the number of seconds that should appear to
|
|
* elapse between frames. In forced mode or limited mode, sets our target dt.
|
|
* In normal mode, this has no effect.
|
|
*
|
|
* Also see set_frame_rate(), which is a different way to specify the same
|
|
* quantity.
|
|
*/
|
|
void ClockObject::
|
|
set_dt(double dt) {
|
|
if (_mode == M_slave) {
|
|
// In M_slave mode, we can set any dt we like.
|
|
CDWriter cdata(_cycler, Thread::get_current_thread());
|
|
cdata->_dt = dt;
|
|
if (dt != 0.0) {
|
|
set_frame_rate(1.0 / dt);
|
|
}
|
|
|
|
} else {
|
|
// In any other mode, we can only set non-zero dt.
|
|
nassertv(dt != 0.0);
|
|
set_frame_rate(1.0 / dt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* In non-real-time mode, sets the number of frames per second that we should
|
|
* appear to be running. In forced mode or limited mode, sets our target
|
|
* frame rate. In normal mode, this has no effect.
|
|
*
|
|
* Also see set_dt(), which is a different way to specify the same quantity.
|
|
*/
|
|
void ClockObject::
|
|
set_frame_rate(double frame_rate) {
|
|
nassertv(frame_rate != 0.0);
|
|
|
|
Thread *current_thread = Thread::get_current_thread();
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
|
|
CDWriter cdata(_cycler, current_thread);
|
|
_user_frame_rate = frame_rate;
|
|
|
|
switch (_mode) {
|
|
case M_non_real_time:
|
|
case M_forced:
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
cdata->_dt = 1.0 / _user_frame_rate;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the average frame rate in number of frames per second over the last
|
|
* get_average_frame_rate_interval() seconds. This measures the virtual frame
|
|
* rate if the clock is in M_non_real_time mode.
|
|
*/
|
|
double ClockObject::
|
|
get_average_frame_rate(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
if (_ticks.size() <= 1) {
|
|
return 0.0;
|
|
} else {
|
|
return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum frame duration over the last
|
|
* get_average_frame_rate_interval() seconds.
|
|
*/
|
|
double ClockObject::
|
|
get_max_frame_duration(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
double max_duration = 0.0;
|
|
double cur_duration = 0.0;
|
|
size_t i;
|
|
for (i = 0; i < _ticks.size() - 1; i++) {
|
|
cur_duration = _ticks[i + 1] - _ticks[i];
|
|
if (cur_duration > max_duration) {
|
|
max_duration = cur_duration;
|
|
}
|
|
}
|
|
return max_duration;
|
|
}
|
|
|
|
/**
|
|
* Returns the standard deviation of the frame times of the frames rendered
|
|
* over the past get_average_frame_rate_interval() seconds. This number gives
|
|
* an estimate of the chugginess of the frame rate; if it is large, there is a
|
|
* large variation in the frame rate; if is small, all of the frames are
|
|
* consistent in length.
|
|
*
|
|
* A large value might also represent just a recent change in frame rate, for
|
|
* instance, because the camera has just rotated from looking at a simple
|
|
* scene to looking at a more complex scene.
|
|
*/
|
|
double ClockObject::
|
|
calc_frame_rate_deviation(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
if (_ticks.size() <= 1) {
|
|
return 0.0;
|
|
} else {
|
|
double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
|
|
size_t i;
|
|
double sum_squares = 0.0;
|
|
for (i = 0; i < _ticks.size() - 1; ++i) {
|
|
double delta = _ticks[i + 1] - _ticks[i];
|
|
double diff = (delta - mean);
|
|
sum_squares += (diff * diff);
|
|
}
|
|
double deviation_2 = sum_squares / (_ticks.size() - 1);
|
|
return sqrt(deviation_2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Instructs the clock that a new frame has just begun. In normal, real-time
|
|
* mode, get_frame_time() will henceforth report the time as of this instant
|
|
* as the current start-of-frame time. In non-real-time mode,
|
|
* get_frame_time() will be incremented by the value of dt.
|
|
*/
|
|
void ClockObject::
|
|
tick(Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
CDWriter cdata(_cycler, current_thread);
|
|
double old_reported_time = cdata->_reported_frame_time;
|
|
|
|
if (_mode != M_slave) {
|
|
double old_time = _actual_frame_time;
|
|
_actual_frame_time = get_real_time();
|
|
|
|
// In case someone munged the clock last frame and sent us backward in
|
|
// time, clamp the previous time to the current time to make sure we don't
|
|
// report anything strange (or wait interminably).
|
|
old_time = std::min(old_time, _actual_frame_time);
|
|
|
|
++cdata->_frame_count;
|
|
|
|
switch (_mode) {
|
|
case M_normal:
|
|
// Time runs as it will; we simply report time elapsing.
|
|
cdata->_dt = _actual_frame_time - old_time;
|
|
cdata->_reported_frame_time = _actual_frame_time;
|
|
break;
|
|
|
|
case M_non_real_time:
|
|
// Ignore real time. We always report the same interval having elapsed
|
|
// each frame.
|
|
cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
|
|
cdata->_frame_count / _user_frame_rate;
|
|
break;
|
|
|
|
case M_limited:
|
|
// If we are running faster than the desired interval, slow down.
|
|
{
|
|
double wait_until_time = old_time + 1.0 / _user_frame_rate;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = _actual_frame_time - old_time;
|
|
cdata->_reported_frame_time = std::max(_actual_frame_time, wait_until_time);
|
|
}
|
|
break;
|
|
|
|
case M_integer:
|
|
{
|
|
double dt = _actual_frame_time - old_time;
|
|
double target_dt = 1.0 / _user_frame_rate;
|
|
if (dt < target_dt) {
|
|
// We're running faster than the desired interval, so slow down to
|
|
// the next integer multiple of the frame rate.
|
|
target_dt = target_dt / floor(target_dt / dt);
|
|
} else {
|
|
// We're running slower than the desired interval, so slow down to
|
|
// the next integer divisor of the frame rate.
|
|
target_dt = target_dt * ceil(dt / target_dt);
|
|
}
|
|
double wait_until_time = old_time + target_dt;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = target_dt;
|
|
cdata->_reported_frame_time = wait_until_time;
|
|
}
|
|
break;
|
|
|
|
case M_integer_limited:
|
|
{
|
|
double dt = _actual_frame_time - old_time;
|
|
double target_dt = 1.0 / _user_frame_rate;
|
|
if (dt < target_dt) {
|
|
// We're running faster than the desired interval, so slow down to
|
|
// the target frame rate.
|
|
|
|
} else {
|
|
// We're running slower than the desired interval, so slow down to
|
|
// the next integer divisor of the frame rate.
|
|
target_dt = target_dt * ceil(dt / target_dt);
|
|
}
|
|
double wait_until_time = old_time + target_dt;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = target_dt;
|
|
cdata->_reported_frame_time = wait_until_time;
|
|
}
|
|
break;
|
|
|
|
case M_forced:
|
|
// If we are running faster than the desired interval, slow down. If we
|
|
// are running slower than the desired interval, ignore that and pretend
|
|
// we're running at the specified rate.
|
|
wait_until(old_time + 1.0 / _user_frame_rate);
|
|
cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
|
|
cdata->_frame_count / _user_frame_rate;
|
|
break;
|
|
|
|
case M_degrade:
|
|
// Each frame, wait a certain fraction of the previous frame's time to
|
|
// degrade performance uniformly.
|
|
cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
|
|
|
|
if (_degrade_factor < 1.0) {
|
|
// If the degrade_factor is less than one, we want to simulate a
|
|
// higher frame rate by incrementing the clock more slowly.
|
|
cdata->_reported_frame_time += cdata->_dt;
|
|
|
|
} else {
|
|
// Otherwise, we simulate a lower frame rate by waiting until the
|
|
// appropriate time has elapsed.
|
|
wait_until(old_time + cdata->_dt);
|
|
cdata->_reported_frame_time = _actual_frame_time;
|
|
}
|
|
|
|
break;
|
|
|
|
case M_slave:
|
|
// Handled above.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_average_frame_rate_interval > 0.0) {
|
|
_ticks.push_back(old_reported_time);
|
|
while (_ticks.size() > 2 &&
|
|
cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
|
|
_ticks.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the frame time to the current real time. This is similar to tick(),
|
|
* except that it does not advance the frame counter and does not affect dt.
|
|
* This is intended to be used in the middle of a particularly long frame to
|
|
* compensate for the time that has already elapsed.
|
|
*
|
|
* In non-real-time mode, this function has no effect (because in this mode
|
|
* all frames take the same length of time).
|
|
*/
|
|
void ClockObject::
|
|
sync_frame_time(Thread *current_thread) {
|
|
if (_mode == M_normal) {
|
|
CDWriter cdata(_cycler, current_thread);
|
|
cdata->_reported_frame_time = get_real_time();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waits at the end of a frame until the indicated time has arrived. This is
|
|
* used to implement M_forced and M_degrade.
|
|
*/
|
|
void ClockObject::
|
|
wait_until(double want_time) {
|
|
if (want_time <= _actual_frame_time) {
|
|
return;
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_start_clock_wait)();
|
|
#endif
|
|
|
|
double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
|
|
|
|
if (wait_interval > 0.0) {
|
|
Thread::sleep(wait_interval);
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_start_clock_busy_wait)();
|
|
#endif
|
|
|
|
// Now busy-wait until the actual time elapses.
|
|
while (_actual_frame_time < want_time) {
|
|
_actual_frame_time = get_real_time();
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_stop_clock_wait)();
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Called once per application to create the global clock object.
|
|
*/
|
|
void ClockObject::
|
|
make_global_clock() {
|
|
nassertv(_global_clock == nullptr);
|
|
|
|
ConfigVariableEnum<ClockObject::Mode> clock_mode
|
|
("clock-mode", ClockObject::M_normal,
|
|
PRC_DESC("Specifies the mode of the global clock. The default mode, normal, "
|
|
"is a real-time clock; other modes allow non-real-time special "
|
|
"effects like simulated reduced frame rate. See "
|
|
"ClockObject::set_mode()."));
|
|
|
|
ClockObject *clock = new ClockObject(clock_mode);
|
|
clock->local_object();
|
|
|
|
if (AtomicAdjust::compare_and_exchange_ptr(_global_clock, nullptr, clock) != nullptr) {
|
|
// Another thread beat us to it.
|
|
delete clock;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This no-op function is assigned as the initial pointer for
|
|
* _start_clock_wait and _stop_clock_wait, until the PStatClient comes along
|
|
* and replaces it.
|
|
*/
|
|
void ClockObject::
|
|
dummy_clock_wait() {
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
ClockObject::CData::
|
|
CData() {
|
|
_frame_count = 0;
|
|
_reported_frame_time = 0.0;
|
|
_reported_frame_time_epoch = 0.0;
|
|
_dt = 0.0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
CycleData *ClockObject::CData::
|
|
make_copy() const {
|
|
return new CData(*this);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
ostream &
|
|
operator << (ostream &out, ClockObject::Mode mode) {
|
|
switch (mode) {
|
|
case ClockObject::M_normal:
|
|
return out << "normal";
|
|
|
|
case ClockObject::M_non_real_time:
|
|
return out << "non-real-time";
|
|
|
|
case ClockObject::M_limited:
|
|
return out << "limited";
|
|
|
|
case ClockObject::M_integer:
|
|
return out << "integer";
|
|
|
|
case ClockObject::M_integer_limited:
|
|
return out << "integer_limited";
|
|
|
|
case ClockObject::M_forced:
|
|
return out << "forced";
|
|
|
|
case ClockObject::M_degrade:
|
|
return out << "degrade";
|
|
|
|
case ClockObject::M_slave:
|
|
return out << "slave";
|
|
};
|
|
|
|
return out << "**invalid ClockObject::Mode(" << (int)mode << ")**";
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
istream &
|
|
operator >> (istream &in, ClockObject::Mode &mode) {
|
|
string word;
|
|
in >> word;
|
|
|
|
if (cmp_nocase_uh(word, "normal") == 0) {
|
|
mode = ClockObject::M_normal;
|
|
} else if (cmp_nocase_uh(word, "non-real-time") == 0) {
|
|
mode = ClockObject::M_non_real_time;
|
|
} else if (cmp_nocase_uh(word, "limited") == 0) {
|
|
mode = ClockObject::M_limited;
|
|
} else if (cmp_nocase_uh(word, "integer") == 0) {
|
|
mode = ClockObject::M_integer;
|
|
} else if (cmp_nocase_uh(word, "integer_limited") == 0) {
|
|
mode = ClockObject::M_integer_limited;
|
|
} else if (cmp_nocase_uh(word, "forced") == 0) {
|
|
mode = ClockObject::M_forced;
|
|
} else if (cmp_nocase_uh(word, "degrade") == 0) {
|
|
mode = ClockObject::M_degrade;
|
|
} else if (cmp_nocase_uh(word, "slave") == 0) {
|
|
mode = ClockObject::M_slave;
|
|
} else {
|
|
util_cat->error()
|
|
<< "Invalid ClockObject::Mode: " << word << "\n";
|
|
mode = ClockObject::M_normal;
|
|
}
|
|
|
|
return in;
|
|
}
|