1
0
Fork 0
fgdata/Aircraft/c172p/Nasal/c172-electrical.nas
James Turner 563c098452 Change some Nasal logging to use logprint()
- avoids console output at default (WARN) log level from the C172P
2015-06-08 10:33:31 +01:00

490 lines
13 KiB
Text

##
# Procedural model of a Cessna 172S electrical system. Includes a
# preliminary battery charge/discharge model and realistic ammeter
# gauge modeling.
#
##
# Initialize internal values
#
var battery = nil;
var alternator = nil;
var last_time = 0.0;
var vbus_volts = 0.0;
var ebus1_volts = 0.0;
var ebus2_volts = 0.0;
var ammeter_ave = 0.0;
##
# Initialize the electrical system
#
init_electrical = func {
battery = BatteryClass.new();
alternator = AlternatorClass.new();
# set initial switch positions
setprop("/controls/engines/engine[0]/master-bat", 1);
setprop("/controls/engines/engine[0]/master-alt", 1);
setprop("/controls/switches/master-avionics", 1);
setprop("/systems/electrical/outputs/autopilot",0.0);
# Request that the update function be called next frame
settimer(update_electrical, 0);
logprint(3, "Electrical system initialized");
}
##
# Battery model class.
#
BatteryClass = {};
BatteryClass.new = func {
var obj = { parents : [BatteryClass],
ideal_volts : 24.0,
ideal_amps : 30.0,
amp_hours : 12.75,
charge_percent : 1.0,
charge_amps : 7.0 };
return obj;
}
##
# Passing in positive amps means the battery will be discharged.
# Negative amps indicates a battery charge.
#
BatteryClass.apply_load = func( amps, dt ) {
var amphrs_used = amps * dt / 3600.0;
var percent_used = amphrs_used / me.amp_hours;
var charge_percent = me.charge_percent;
charge_percent -= percent_used;
if ( charge_percent < 0.0 ) {
charge_percent = 0.0;
} elsif ( charge_percent > 1.0 ) {
charge_percent = 1.0;
}
if ((charge_percent < 0.1)and(me.charge_percent >= 0.1))
{
print("Warning: Low battery! Enable alternator or apply external power to recharge battery.");
}
me.charge_percent = charge_percent;
setprop("/systems/electrical/battery-charge-percent", charge_percent);
# print( "battery percent = ", charge_percent);
return me.amp_hours * charge_percent;
}
##
# Return output volts based on percent charged. Currently based on a simple
# polynomial percent charge vs. volts function.
#
BatteryClass.get_output_volts = func {
var x = 1.0 - me.charge_percent;
var tmp = -(3.0 * x - 1.0);
var factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
return me.ideal_volts * factor;
}
##
# Return output amps available. This function is totally wrong and should be
# fixed at some point with a more sensible function based on charge percent.
# There is probably some physical limits to the number of instantaneous amps
# a battery can produce (cold cranking amps?)
#
BatteryClass.get_output_amps = func {
var x = 1.0 - me.charge_percent;
var tmp = -(3.0 * x - 1.0);
var factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
return me.ideal_amps * factor;
}
##
# Alternator model class.
#
AlternatorClass = {};
AlternatorClass.new = func {
var obj = { parents : [AlternatorClass],
rpm_source : "/engines/engine[0]/rpm",
rpm_threshold : 800.0,
ideal_volts : 28.0,
ideal_amps : 60.0 };
setprop( obj.rpm_source, 0.0 );
return obj;
}
##
# Computes available amps and returns remaining amps after load is applied
#
AlternatorClass.apply_load = func( amps, dt ) {
# Scale alternator output for rpms < 800. For rpms >= 800
# give full output. This is just a WAG, and probably not how
# it really works but I'm keeping things "simple" to start.
var rpm = getprop( me.rpm_source );
var factor = rpm / me.rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
# print( "alternator amps = ", me.ideal_amps * factor );
var available_amps = me.ideal_amps * factor;
return available_amps - amps;
}
##
# Return output volts based on rpm
#
AlternatorClass.get_output_volts = func {
# scale alternator output for rpms < 800. For rpms >= 800
# give full output. This is just a WAG, and probably not how
# it really works but I'm keeping things "simple" to start.
var rpm = getprop( me.rpm_source );
var factor = rpm / me.rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
# print( "alternator volts = ", me.ideal_volts * factor );
return me.ideal_volts * factor;
}
##
# Return output amps available based on rpm.
#
AlternatorClass.get_output_amps = func {
# scale alternator output for rpms < 800. For rpms >= 800
# give full output. This is just a WAG, and probably not how
# it really works but I'm keeping things "simple" to start.
var rpm = getprop( me.rpm_source );
var factor = rpm / me.rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
# print( "alternator amps = ", ideal_amps * factor );
return me.ideal_amps * factor;
}
##
# This is the main electrical system update function.
#
update_electrical = func {
var time = getprop("/sim/time/elapsed-sec");
var dt = time - last_time;
last_time = time;
update_virtual_bus( dt );
# Request that the update function be called again next frame
settimer(update_electrical, 0);
}
##
# Model the system of relays and connections that join the battery,
# alternator, starter, master/alt switches, external power supply.
#
update_virtual_bus = func( dt ) {
var serviceable = getprop("/systems/electrical/serviceable");
var external_volts = 0.0;
var load = 0.0;
var battery_volts = 0.0;
var alternator_volts = 0.0;
if ( serviceable ) {
battery_volts = battery.get_output_volts();
alternator_volts = alternator.get_output_volts();
}
# switch state
var master_bat = getprop("/controls/engines/engine[0]/master-bat");
var master_alt = getprop("/controls/engines/engine[0]/master-alt");
if (getprop("/controls/electric/external-power"))
{
external_volts = 28;
}
# determine power source
var bus_volts = 0.0;
var power_source = nil;
if ( master_bat ) {
bus_volts = battery_volts;
power_source = "battery";
}
if ( master_alt and (alternator_volts > bus_volts) ) {
bus_volts = alternator_volts;
power_source = "alternator";
}
if ( external_volts > bus_volts ) {
bus_volts = external_volts;
power_source = "external";
}
# print( "virtual bus volts = ", bus_volts );
# starter motor
var starter_switch = getprop("controls/switches/starter");
var starter_volts = 0.0;
if ( starter_switch ) {
starter_volts = bus_volts;
load += 12;
}
setprop("systems/electrical/outputs/starter[0]", starter_volts);
if (starter_volts > 12) {
setprop("controls/engines/engine[0]/starter",1);
setprop("controls/engines/engine[0]/magnetos",3);
} else {
setprop("controls/engines/engine[0]/starter",0);
}
# bus network (1. these must be called in the right order, 2. the
# bus routine itself determins where it draws power from.)
load += electrical_bus_1();
load += electrical_bus_2();
load += cross_feed_bus();
load += avionics_bus_1();
load += avionics_bus_2();
# system loads and ammeter gauge
var ammeter = 0.0;
if ( bus_volts > 1.0 ) {
# ammeter gauge
if ( power_source == "battery" ) {
ammeter = -load;
} else {
ammeter = battery.charge_amps;
}
}
# print( "ammeter = ", ammeter );
# charge/discharge the battery
if ( power_source == "battery" ) {
battery.apply_load( load, dt );
} elsif ( bus_volts > battery_volts ) {
battery.apply_load( -battery.charge_amps, dt );
}
# filter ammeter needle pos
ammeter_ave = 0.8 * ammeter_ave + 0.2 * ammeter;
# outputs
setprop("/systems/electrical/amps", ammeter_ave);
setprop("/systems/electrical/volts", bus_volts);
if (bus_volts > 12)
vbus_volts = bus_volts;
else
vbus_volts = 0.0;
return load;
}
electrical_bus_1 = func() {
# we are fed from the "virtual" bus
var bus_volts = vbus_volts;
var load = 0.0;
# Cabin Lights Power
if ( getprop("/controls/circuit-breakers/cabin-lights-pwr") ) {
setprop("/systems/electrical/outputs/cabin-lights", bus_volts);
load += bus_volts / 57;
} else {
setprop("/systems/electrical/outputs/cabin-lights", 0.0);
}
# Instrument Power
setprop("/systems/electrical/outputs/instr-ignition-switch", bus_volts);
# Fuel Pump Power
if ( getprop("/controls/engines/engine[0]/fuel-pump") ) {
setprop("/systems/electrical/outputs/fuel-pump", bus_volts);
load += bus_volts / 28;
} else {
setprop("/systems/electrical/outputs/fuel-pump", 0.0);
}
# Landing Light Power
if ( getprop("/controls/lighting/landing-lights") ) {
setprop("/systems/electrical/outputs/landing-lights", bus_volts);
load += bus_volts / 5;
} else {
setprop("/systems/electrical/outputs/landing-lights", 0.0 );
}
# Beacon Power
if ( getprop("/controls/lighting/beacon" ) ) {
setprop("/systems/electrical/outputs/beacon", bus_volts);
load += bus_volts / 28;
} else {
setprop("/systems/electrical/outputs/beacon", 0.0);
}
# Flaps Power
setprop("/systems/electrical/outputs/flaps", bus_volts);
# register bus voltage
ebus1_volts = bus_volts;
# return cumulative load
return load;
}
electrical_bus_2 = func() {
# we are fed from the "virtual" bus
var bus_volts = vbus_volts;
var load = 0.0;
# Nav Lights Power
if ( getprop("/controls/lighting/nav-lights" ) ) {
setprop("/systems/electrical/outputs/nav-lights", bus_volts);
load += bus_volts / 14;
} else {
setprop("/systems/electrical/outputs/nav-lights", 0.0);
}
# Instrument Lights Power
setprop("/systems/electrical/outputs/instrument-lights", bus_volts);
# Strobe Lights Power
if ( getprop("/controls/lighting/strobe" ) ) {
setprop("/systems/electrical/outputs/strobe", bus_volts);
load += bus_volts / 14;
} else {
setprop("/systems/electrical/outputs/strobe", 0.0);
}
# Taxi Lights Power
if ( getprop("/controls/lighting/taxi-light" ) ) {
setprop("/systems/electrical/outputs/taxi-light", bus_volts);
load += bus_volts / 10;
} else {
setprop("/systems/electrical/outputs/taxi-light", 0.0);
}
# Pitot Heat Power
if ( getprop("/controls/anti-ice/pitot-heat" ) ) {
setprop("/systems/electrical/outputs/pitot-heat", bus_volts);
load += bus_volts / 28;
} else {
setprop("/systems/electrical/outputs/pitot-heat", 0.0);
}
# register bus voltage
ebus2_volts = bus_volts;
# return cumulative load
return load;
}
cross_feed_bus = func() {
# we are fed from either of the electrical bus 1 or 2
var bus_volts = ebus2_volts;
if ( ebus1_volts > ebus2_volts ) {
bus_volts = ebus1_volts;
}
var load = 0.0;
setprop("/systems/electrical/outputs/annunciators", bus_volts);
# return cumulative load
return load;
}
avionics_bus_1 = func() {
var bus_volts = 0.0;
var load = 0.0;
# we are fed from the electrical bus 1
var master_av = getprop("/controls/switches/master-avionics");
if ( master_av ) {
bus_volts = ebus1_volts;
}
load += bus_volts / 20.0;
# Turn Coordinator Power
setprop("/systems/electrical/outputs/turn-coordinator", bus_volts);
# Directional Gyro Power
setprop("/systems/electrical/outputs/DG", bus_volts);
# Avionics Fan Power
setprop("/systems/electrical/outputs/avionics-fan", bus_volts);
# GPS Power
setprop("/systems/electrical/outputs/gps", bus_volts);
# HSI Power
setprop("/systems/electrical/outputs/hsi", bus_volts);
# NavCom 1 Power
setprop("/systems/electrical/outputs/nav[0]", bus_volts);
# DME Power
setprop("/systems/electrical/outputs/dme", bus_volts);
# Audio Panel 1 Power
setprop("/systems/electrical/outputs/audio-panel[0]", bus_volts);
# Com 1 Power
setprop("systems/electrical/outputs/comm[0]", bus_volts);
# return cumulative load
return load;
}
avionics_bus_2 = func() {
var master_av = getprop("/controls/switches/master-avionics");
# we are fed from the electrical bus 2
var bus_volts = 0.0;
if ( master_av ) {
bus_volts = ebus2_volts;
}
var load = bus_volts / 20.0;
# NavCom 2 Power
setprop("/systems/electrical/outputs/nav[1]", bus_volts);
# Audio Panel 2 Power
setprop("/systems/electrical/outputs/audio-panel[1]", bus_volts);
# Com 2 Power
setprop("systems/electrical/outputs/comm[1]", bus_volts);
# Transponder Power
setprop("/systems/electrical/outputs/transponder", bus_volts);
# Autopilot Power
setprop("/systems/electrical/outputs/autopilot", bus_volts);
# ADF Power
setprop("/systems/electrical/outputs/adf", bus_volts);
# return cumulative load
return load;
}
# Setup a timer based call to initialized the electrical system as
# soon as possible.
settimer(init_electrical, 0);