emexec: rewrite rate adjust logic
Change to calculate the frame rate ourselves because /sim/frame-rate doesn't take into account freeze and has a transient ridiculous value after a pause. Instead we calculate the average rate over a period and then LP filter this. This also removes the annoying frame rate update messages Cofiguration now comes from /sim/emexec * /sim/emexec/monitor-period is the period to reset the average; the LP filter isn't reset. * /sim/emexec/max-rate-hz is the upper limit on the emexec update rate. defaults to 50 and a model can override this. It's probably not a user setting output of * current emexec rate into /sim/emexec/rate-hz * smoothed / LP filtered frame rate into /sim/emexec/frame-rate
This commit is contained in:
parent
7274ec886a
commit
aeae504d47
2 changed files with 70 additions and 24 deletions
|
@ -85,7 +85,7 @@ var FrameNotification =
|
|||
new_class.monitored[notification.variable] = root_node.getNode(notification.property,1);
|
||||
new_class.properties[notification.property] = notification.variable;
|
||||
|
||||
logprint(3,"(",notification.module,") FrameNotification.",notification.variable, " = ",notification.property, " -> ", new_class.monitored[notification.variable].getPath() );
|
||||
logprint(4,"(",notification.module,") FrameNotification.",notification.variable, " = ",notification.property, " -> ", new_class.monitored[notification.variable].getPath() );
|
||||
return emesary.Transmitter.ReceiptStatus_OK;
|
||||
}
|
||||
return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
||||
|
@ -97,7 +97,7 @@ var FrameNotification =
|
|||
foreach (var mp; keys(me.monitored)){
|
||||
if(me.monitored[mp] != nil){
|
||||
if (FrameNotification.debug > 1)
|
||||
logprint(4," ",mp, " = ",me.monitored[mp].getValue());
|
||||
logprint(5," ",mp, " = ",me.monitored[mp].getValue());
|
||||
me[mp] = me.monitored[mp].getValue();
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,47 @@ var FrameNotificationAddProperty =
|
|||
},
|
||||
};
|
||||
|
||||
#
|
||||
# The way that the core measures frame rate gives invalid values after a pause so instead
|
||||
# we will measure specific to our needs.
|
||||
var PerformanceMeasurement = {
|
||||
new : func {
|
||||
return {
|
||||
parents: [PerformanceMeasurement],
|
||||
lp : aircraft.lowpass.new(0.1),
|
||||
period : props.globals.getNode("/sim/emexec/monitor-period",1),
|
||||
frame_count_node : props.globals.getNode("/sim/frame-number"),
|
||||
frame_count : getprop("/sim/frame-number") or 0,
|
||||
emexec_frame_rate_node : props.globals.getNode("/sim/emexec/frame-rate",1),
|
||||
start_time : systime(),
|
||||
frame_rate : 30,
|
||||
};
|
||||
},
|
||||
update: func {
|
||||
# calculate the average (mean) rate for the monitored period and then let this
|
||||
# filter into our output frame rate
|
||||
me.delta_seconds = systime() - me.start_time;
|
||||
me.frame_count_delta = me.frame_count_node.getValue() - me.frame_count;
|
||||
me.frame_rate = me.frame_count_delta / me.delta_seconds;
|
||||
me.elapsed_seconds= systime();
|
||||
me.emexec_frame_rate_node.setDoubleValue((int)(me.lp.filter(me.frame_rate)));
|
||||
|
||||
# reset at the end of the defined period as over a long period the average isn't as relevant for our purposes
|
||||
if (me.delta_seconds > me.period.getValue()){
|
||||
me.start_time = systime();
|
||||
me.frame_count = me.frame_count_node.getValue();
|
||||
}
|
||||
},
|
||||
start : func {
|
||||
me.timer = maketimer(2, me, func { me.update() });
|
||||
me.timer.simulatedTime = 0;
|
||||
me.timer.start();
|
||||
return me;
|
||||
}
|
||||
};
|
||||
|
||||
performanceMeasurement = PerformanceMeasurement.new();
|
||||
|
||||
#
|
||||
# the main exeuctive class.
|
||||
# There will be one of these as emexec.ExceModule however multiple instances could be
|
||||
|
@ -136,18 +177,21 @@ var EmesaryExecutive = {
|
|||
var new_class = {
|
||||
parents: [EmesaryExecutive],
|
||||
Ident: _ident,
|
||||
lp : aircraft.lowpass.new(3),
|
||||
frameNotification : FrameNotification.new(1, transmitter),
|
||||
emexecRate : props.globals.getNode("/sim/emexec/rate-hz",1),
|
||||
emexecMaxRate : props.globals.getNode("/sim/emexec/max-rate-hz",1),
|
||||
frame_inc : 0,
|
||||
cur_frame_inc : 0.033, # start off at 33hz
|
||||
};
|
||||
new_class.transmitter = transmitter;
|
||||
|
||||
new_class.set_rate(30);
|
||||
|
||||
# setup the properties to monitor for this system
|
||||
var exec_prop_list = {
|
||||
frame_rate : "/sim/frame-rate",
|
||||
frame_rate_worst : "/sim/frame-rate-worst",
|
||||
elapsed_seconds : "/sim/time/elapsed-sec",
|
||||
eframe_rate : "/sim/emexec/frame-rate",
|
||||
};
|
||||
new_class.monitor_properties(exec_prop_list);
|
||||
|
||||
|
@ -167,11 +211,18 @@ var EmesaryExecutive = {
|
|||
|
||||
return new_class;
|
||||
},
|
||||
set_rate : func(ratehz){
|
||||
me.emexecRate.setValue(ratehz);
|
||||
me.cur_frame_inc = 1.0/ratehz;
|
||||
me.frame_inc = me.cur_frame_inc;
|
||||
},
|
||||
start : func {
|
||||
me.execTimer.start();
|
||||
performanceMeasurement.start();
|
||||
},
|
||||
stop : func {
|
||||
me.execTimer.stop();
|
||||
performanceMeasurement.start();
|
||||
},
|
||||
# request monitoring of a list of hash value pairs.
|
||||
monitor_properties : func(input){
|
||||
|
@ -226,34 +277,23 @@ var EmesaryExecutive = {
|
|||
me.frameNotification.curT = me.frameNotification.elapsed_seconds;
|
||||
|
||||
me.transmitter.NotifyAll(me.frameNotification);
|
||||
|
||||
me.frameNotification.FrameCount = me.frameNotification.FrameCount + 1;
|
||||
me.frameNotification.filtered_frame_rate_worst = (int)(me.lp.filter(me.frameNotification.frame_rate_worst));
|
||||
|
||||
# this permits us to go up to 1/32 rate (which could be less than 1hz)
|
||||
if (me.frameNotification.FrameCount > 32) {
|
||||
me.frameNotification.FrameCount = 0;
|
||||
# adjust exec rate based on frame rate.
|
||||
if (me.frameNotification.filtered_frame_rate_worst < 5) {
|
||||
me.frame_inc = 0.25;#4 Hz
|
||||
} elsif (me.frameNotification.filtered_frame_rate_worst < 10) {
|
||||
me.frame_inc = 0.125;#8 Hz
|
||||
} elsif (me.frameNotification.filtered_frame_rate_worst < 15) {
|
||||
me.frame_inc = 0.10;#10 Hz
|
||||
} elsif (me.frameNotification.filtered_frame_rate_worst < 20) {
|
||||
me.frame_inc = 0.075;#13.3 Hz
|
||||
} elsif (me.frameNotification.filtered_frame_rate_worst < 25) {
|
||||
me.frame_inc = 0.05;#20 Hz
|
||||
} elsif (me.frameNotification.filtered_frame_rate_worst < 40) {
|
||||
me.frame_inc = 0.0333;#30 Hz
|
||||
} else {
|
||||
me.frame_inc = 0.02;#50 Hz
|
||||
}
|
||||
|
||||
# adjust exec rate based on frame rate.
|
||||
# calculate exec update rate based on frame rate; this a quadratic function from a curve fit.
|
||||
me.frame_inc = (math.round((0.33227017+0.10041432*me.frameNotification.eframe_rate+0.01681707*me.frameNotification.eframe_rate*me.frameNotification.eframe_rate)/5)*5+5);
|
||||
|
||||
# limit to: 1 <= update rate <= maxRate (default 50)
|
||||
me.frame_inc = math.max(1,math.min(me.emexecMaxRate.getValue(), me.frame_inc));
|
||||
me.frame_inc = 1/me.frame_inc;
|
||||
|
||||
# Adjust timer if new value
|
||||
if (me.frame_inc != me.cur_frame_inc) {
|
||||
logprint(3, me.Ident~": Adjust frequency to ",1/me.frame_inc, " Hz");
|
||||
me.cur_frame_inc = me.frame_inc;
|
||||
me.set_rate(1.0/me.frame_inc);
|
||||
me.execTimer.restart(me.cur_frame_inc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1180,6 +1180,12 @@ Started September 2000 by David Megginson, david@megginson.com
|
|||
<shininess type="double">-0.5</shininess>
|
||||
</livery>
|
||||
</model>
|
||||
<emexec>
|
||||
<monitor-period type="double">10</monitor-period>
|
||||
<max-rate-hz type="int">50</max-rate-hz>
|
||||
<rate-hz type="int">30</rate-hz>
|
||||
</emexec>
|
||||
|
||||
</sim>
|
||||
<!-- accelerations -->
|
||||
<accelerations>
|
||||
|
|
Loading…
Add table
Reference in a new issue