1
0
Fork 0
fgdata/Aircraft/c172p/Nasal/c172-electrical.nas
jmt 42832d3e79 From Dave Perry:
Patch goals:
1. Implement instrument and pannel lights controled by the nav-light switch.
2. Use Instruments-3d/vor which includes GS flag and filters for smooth movement of cdi and gs needles to and from parked position.
3. Adjust model and pilot view per the agreed to changes in Re: [Flightgear-devel] c172p pitch at cruise question, 12/06/2008 02:27 PM.  These changes were never committed.
2009-11-18 20:35:40 +00:00

473 lines
12 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
#
battery = nil;
alternator = nil;
last_time = 0.0;
vbus_volts = 0.0;
ebus1_volts = 0.0;
ebus2_volts = 0.0;
ammeter_ave = 0.0;
##
# Initialize the electrical system
#
init_electrical = func {
print("Initializing Nasal Electrical System");
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 fuction be called next frame
settimer(update_electrical, 0);
}
##
# Battery model class.
#
BatteryClass = {};
BatteryClass.new = func {
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 ) {
amphrs_used = amps * dt / 3600.0;
percent_used = amphrs_used / me.amp_hours;
me.charge_percent -= percent_used;
if ( me.charge_percent < 0.0 ) {
me.charge_percent = 0.0;
} elsif ( me.charge_percent > 1.0 ) {
me.charge_percent = 1.0;
}
# print( "battery percent = ", me.charge_percent);
return me.amp_hours * me.charge_percent;
}
##
# Return output volts based on percent charged. Currently based on a simple
# polynomal percent charge vs. volts function.
#
BatteryClass.get_output_volts = func {
x = 1.0 - me.charge_percent;
tmp = -(3.0 * x - 1.0);
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 {
x = 1.0 - me.charge_percent;
tmp = -(3.0 * x - 1.0);
factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
return me.ideal_amps * factor;
}
##
# Alternator model class.
#
AlternatorClass = {};
AlternatorClass.new = func {
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.
rpm = getprop( me.rpm_source );
factor = rpm / me.rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
# print( "alternator amps = ", me.ideal_amps * factor );
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.
rpm = getprop( me.rpm_source );
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.
rpm = getprop( me.rpm_source );
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 {
time = getprop("/sim/time/elapsed-sec");
dt = time - last_time;
last_time = time;
update_virtual_bus( dt );
# Request that the update fuction 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 ) {
serviceable = getprop("/systems/electrical/serviceable");
if ( serviceable ) {
battery_volts = battery.get_output_volts();
alternator_volts = alternator.get_output_volts();
} else {
battery_volts = 0.0;
alternator_volts = 0.0;
}
external_volts = 0.0;
load = 0.0;
# switch state
master_bat = getprop("/controls/engines/engine[0]/master-bat");
master_alt = getprop("/controls/engines/engine[0]/master-alt");
# determine power source
bus_volts = 0.0;
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 > 1) {
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
ammeter = 0.0;
if ( bus_volts > 1.0 ) {
# normal load
load += 15.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);
vbus_volts = bus_volts;
return load;
}
electrical_bus_1 = func() {
# we are fed from the "virtual" bus
bus_volts = vbus_volts;
load = 0.0;
# Cabin Lights Power
if ( getprop("/controls/circuit-breakers/cabin-lights-pwr") ) {
setprop("/systems/electrical/outputs/cabin-lights", bus_volts);
} 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);
} else {
setprop("/systems/electrical/outputs/fuel-pump", 0.0);
}
# Landing Light Power
if ( getprop("/controls/switches/landing-light") ) {
setprop("/systems/electrical/outputs/landing-light", bus_volts);
} else {
setprop("/systems/electrical/outputs/landing-light", 0.0 );
}
# Beacon Power
if ( getprop("/controls/switches/flashing-beacon" ) ) {
setprop("/systems/electrical/outputs/beacon", bus_volts);
if ( bus_volts > 1.0 ) { load += 7.5; }
} 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
bus_volts = vbus_volts;
load = 0.0;
# Turn Coordinator Power
setprop("/systems/electrical/outputs/turn-coordinator", bus_volts);
# Map Lights Power
if ( getprop("/controls/switches/nav-lights" ) ) {
setprop("/systems/electrical/outputs/map-lights", bus_volts);
if ( bus_volts > 1.0 ) { load += 7.0; }
} else {
setprop("/systems/electrical/outputs/map-lights", 0.0);
}
# Instrument Lights Power
setprop("/systems/electrical/outputs/instrument-lights", bus_volts);
# Strobe Lights Power
if ( getprop("/controls/switches/strobe-lights" ) ) {
setprop("/systems/electrical/outputs/strobe-lights", bus_volts);
} else {
setprop("/systems/electrical/outputs/strobe-lights", 0.0);
}
# Taxi Lights Power
if ( getprop("/controls/switches/taxi-lights" ) ) {
setprop("/systems/electrical/outputs/taxi-lights", bus_volts);
} else {
setprop("/systems/electrical/outputs/taxi-lights", 0.0);
}
# Pitot Heat Power
if ( getprop("/controls/switches/pitot-heat" ) ) {
setprop("/systems/electrical/outputs/pitot-heat", bus_volts);
} 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
if ( ebus1_volts > ebus2_volts ) {
bus_volts = ebus1_volts;
} else {
bus_volts = ebus2_volts;
}
load = 0.0;
setprop("/systems/electrical/outputs/annunciators", bus_volts);
# return cumulative load
return load;
}
avionics_bus_1 = func() {
master_av = getprop("/controls/switches/master-avionics");
# we are fed from the electrical bus 1
if ( master_av ) {
bus_volts = ebus1_volts;
} else {
bus_volts = 0.0;
}
load = 0.0;
# 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() {
master_av = getprop("/controls/switches/master-avionics");
# we are fed from the electrical bus 2
if ( master_av ) {
bus_volts = ebus2_volts;
} else {
bus_volts = 0.0;
}
load = 0.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);