Open 5H1N0B11 opened 3 years ago
The F-15 I think is the reference implementation of my three main Nasal optimisation techniques;
The FrameNotification is a notification sent out at defined intervals that contains a hash with property values.
Use the FrameNotificationAddProperty to request that certain properties are contained within the FrameNotification.
notifications.FrameNotificationAddProperty.new("F15-HUD", [HASH-Name], [property-string])
e.g.
emesary.GlobalTransmitter.NotifyAll(notifications.FrameNotificationAddProperty.new("F15-HUD", "AirspeedIndicatorIndicatedMach", "instrumentation/airspeed-indicator/indicated-mach"));
although more typically used by having a hash that contains the keys and properties that you want to monitor and then using a loop to send out the FrameNotificationAddProperty; e.g.:
input = {
AirspeedIndicatorIndicatedMach : "instrumentation/airspeed-indicator/indicated-mach",
Alpha : "orientation/alpha-indicated-deg",
AltimeterIndicatedAltitudeFt : "instrumentation/altimeter/indicated-altitude-ft",
ArmamentAgmCount : "sim/model/f15/systems/armament/agm/count",
ArmamentAim120Count : "sim/model/f15/systems/armament/aim120/count",
ArmamentAim7Count : "sim/model/f15/systems/armament/aim7/count",
ArmamentAim9Count : "sim/model/f15/systems/armament/aim9/count",
ArmamentRounds : "sim/model/f15/systems/gun/rounds",
AutopilotRouteManagerActive : "autopilot/route-manager/active",
AutopilotRouteManagerWpDist : "autopilot/route-manager/wp/dist",
AutopilotRouteManagerWpEtaSeconds : "autopilot/route-manager/wp/eta-seconds",
CadcOwsMaximumG : "fdm/jsbsim/systems/cadc/ows-maximum-g",
ControlsArmamentMasterArmSwitch : "sim/model/f15/controls/armament/master-arm-switch",
ControlsArmamentWeaponSelector : "sim/model/f15/controls/armament/weapon-selector",
ControlsGearBrakeParking : "controls/gear/brake-parking",
ControlsGearGearDown : "controls/gear/gear-down",
ControlsHudBrightness : "sim/model/f15/controls/HUD/brightness",
ControlsHudSymRej : "sim/model/f15/controls/HUD/sym-rej",
ElectricsAcLeftMainBus : "fdm/jsbsim/systems/electrics/ac-left-main-bus",
HudNavRangeDisplay : "sim/model/f15/instrumentation/hud/nav-range-display",
HudNavRangeETA : "sim/model/f15/instrumentation/hud/nav-range-eta",
OrientationHeadingDeg : "orientation/heading-deg",
OrientationPitchDeg : "orientation/pitch-deg",
OrientationRollDeg : "orientation/roll-deg",
OrientationSideSlipDeg : "orientation/side-slip-deg",
RadarActiveTargetAvailable : "sim/model/f15/instrumentation/radar-awg-9/active-target-available",
RadarActiveTargetCallsign : "sim/model/f15/instrumentation/radar-awg-9/active-target-callsign",
RadarActiveTargetClosure : "sim/model/f15/instrumentation/radar-awg-9/active-target-closure",
RadarActiveTargetDisplay : "sim/model/f15/instrumentation/radar-awg-9/active-target-display",
RadarActiveTargetRange : "sim/model/f15/instrumentation/radar-awg-9/active-target-range",
RadarActiveTargetType : "sim/model/f15/instrumentation/radar-awg-9/active-target-type",
InstrumentedG : "instrumentation/g-meter/instrumented-g",
VelocitiesAirspeedKt : "velocities/airspeed-kt",
};
foreach (var name; keys(input)) {
emesary.GlobalTransmitter.NotifyAll(notifications.FrameNotificationAddProperty.new("F15-HUD", name, input[name]));
}
The idea of the FrameNotification is that it provides an easy way to run your update logic; simply by registering with the GlobalTransmitter and then processing the FrameNotification you can call your update logic in an efficient manner;
e.g.
var HUDRecipient =
{
new: func(_ident)
{
var new_class = emesary.Recipient.new(_ident);
new_class.MainHUD = nil;
new_class.Receive = func(notification)
{
if (notification.NotificationType == "FrameNotification")
{
if (new_class.MainHUD == nil)
new_class.MainHUD = F15HUD.new("Nasal/HUD/HUD.svg", "HUDImage1");
if (!math.mod(notifications.frameNotification.FrameCount,2)){
new_class.MainHUD.update(notification);
}
return emesary.Transmitter.ReceiptStatus_OK;
}
return emesary.Transmitter.ReceiptStatus_NotProcessed;
};
return new_class;
},
};
and then register that function with the Emesary Global transmitter.
emesary.GlobalTransmitter.Register(HUDRecipient.new("F15-HUD"));
This will monitor one or more property, or values from a hash and call a function (or inline function) when the value or a value changes more than a defined amount.
e.g.
obj.update_items = [
props.UpdateManager.FromHashList(["ElectricsAcLeftMainBus","ControlsHudBrightness"] , 0.01, func(val)
{
if (val.ElectricsAcLeftMainBus <= 0
or val.ControlsHudBrightness <= 0) {
obj.svg.setVisible(0);
} else {
obj.svg.setVisible(1);
}
}),
props.UpdateManager.FromHashValue("AltimeterIndicatedAltitudeFt", 1, func(val)
{
obj.alt_range.setTranslation(0, val * alt_range_factor);
}),
props.UpdateManager.FromHashValue("VelocitiesAirspeedKt", 0.1, func(val)
{
obj.ias_range.setTranslation(0, val * ias_range_factor);
}),
props.UpdateManager.FromHashValue("ControlsHudSymRej", 0.1, func(val)
{
obj.symbol_reject = val;
}),
...
and then in the update method
foreach(var update_item; me.update_items)
{
update_item.update(notification);
}
As you can see above the update method is called from the recipient of the frame notification.
When working with lists (arrays) of data that can get quite long (e.g. targets from radar) the partition processor is an easy way to only process a part of that list each frame.
What it does is to manage the processing of data in a manner suitable for real time operations. Given a data array [0..size] this will process a number of array elements each time it is called This allows for a simple way to split up intensive processing across multiple frames.
The limit is the number of elements to process per frame (invocation of .process method) or to limit the processing to a specified amount of time using a Nasal timestamp to measure the amount (in usec)
example usage (object);
var VSD_Device =
{
new : func(designation, model_element, target_module_id, root_node)
{
...
obj.process_targets = PartitionProcessor.new("VSD-targets", 20, nil);
obj.process_targets.set_max_time_usec(500);
...
me.process_targets.set_timestamp(notification.Timestamp);
then invoke.
me.process_targets.process(me, awg_9.tgts_list,
func(pp, obj, data){
initialisation; called before processing element[0]
params
pp is the partition processor that called this
obj is the reference object (first argument in the .process)
data is the entire data array.
}
,
func(pp, obj, element){
proces individual element;
params
pp is the partition processor that called this
obj is the reference object (first argument in the .process)
element is the element data[pp.data_index]
return 0 to stop processing any more elements and call the completed method
return 1 to continue processing.
},
func(pp, obj, data)
{
completed; called after the last element processed
params
pp is the partition processor that called this
obj is the reference object (first argument in the .process)
data is the entire data array.
});
Awesome. Thanks. I will work on it.
HUD need to use Richard's method for refreshing properties : we can take inspiration from the f16.