1
0
Fork 0

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:
Richard Harrison 2023-01-29 14:27:55 +00:00
parent 7274ec886a
commit aeae504d47
2 changed files with 70 additions and 24 deletions

View file

@ -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);
}
}

View file

@ -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>