1
0
Fork 0
fgdata/Aircraft/Generic/century3.nas
mfranz 7253f8633b Dave PERRY:
"I have been working with Torsten Dryer to model
these three autopilots that are very common in aircraft manufactured in
the 1960s through the late 1980s.  The Altimatic IIIc in the SenecaII is
a variation on the Century III and uses the same nasal model."

mf: these should be in cvs since a while; somehow I missed them, sorry
2007-10-14 07:24:35 +00:00

737 lines
20 KiB
Text

##
# Century III Autopilot System
# Tries to behave like the Century III autopilot
# two axis
#
# One would also need the autopilot configuration file
# CENTURYIII.xml and the panel instrument configuration file
#
# Written by Dave Perry to match functionality described in
#
# CENTURY III
# AUTOPILOT FLIGHT SYSTEM
# PILOT'S OPERATING HANDBOOK
# NOVEMBER 1998 68S25
#
# Draws heavily from the kap140 system written by Roy Vegard Ovesen
##
# Properties
locks = "/autopilot/CENTURYIII/locks";
settings = "/autopilot/CENTURYIII/settings";
internal = "/autopilot/internal";
flightControls = "/controls/flight";
autopilotControls = "/autopilot/CENTURYIII/controls";
# locks
propLocks = props.globals.getNode(locks, 1);
lockAltHold = propLocks.getNode("alt-hold", 1);
lockPitchHold = propLocks.getNode("pitch-hold", 1);
lockAprHold = propLocks.getNode("apr-hold", 1);
lockGsHold = propLocks.getNode("gs-hold", 1);
lockHdgHold = propLocks.getNode("hdg-hold", 1);
lockNavHold = propLocks.getNode("nav-hold", 1);
lockOmniHold = propLocks.getNode("omni-hold", 1);
lockRevHold = propLocks.getNode("rev-hold", 1);
lockRollAxis = propLocks.getNode("roll-axis", 1);
lockRollMode = propLocks.getNode("roll-mode", 1);
lockPitchAxis = propLocks.getNode("pitch-axis", 1);
lockPitchMode = propLocks.getNode("pitch-mode", 1);
lockRollArm = propLocks.getNode("roll-arm", 1);
lockPitchArm = propLocks.getNode("pitch-arm", 1);
rollModes = { "OFF" : 0, "ROL" : 1, "HDG" : 2, "OMNI" : 3, "NAV" : 4, "REV" : 5, "APR" : 6 };
pitchModes = { "OFF" : 0, "VS" : 1, "ALT" : 2, "GS" : 3, "AOA" : 4 };
rollArmModes = { "OFF" : 0, "NAV" : 1, "OMNI" : 2, "APR" : 3, "REV" : 4 };
pitchArmModes = { "OFF" : 0, "ALT" : 1, "GS" : 2 };
# settings
propSettings = props.globals.getNode(settings, 1);
settingTargetAltPressure = propSettings.getNode("target-alt-pressure", 1);
settingTargetInterceptAngle = propSettings.getNode("target-intercept-angle", 1);
settingTargetPressureRate = propSettings.getNode("target-pressure-rate", 1);
settingTargetRollDeg = propSettings.getNode("target-roll-deg", 1);
settingRollKnobDeg = propSettings.getNode("roll-knob-deg", 1);
settingTargetPitchDeg = propSettings.getNode("target-pitch-deg", 1);
settingPitchWheelDeg = propSettings.getNode("pitch-wheel-deg", 1);
settingAutoPitchTrim = propSettings.getNode("auto-pitch-trim", 1);
settingGScaptured = propSettings.getNode("gs-captured", 1);
settingDeltaPitch = propSettings.getNode("delta-pitch", 1);
#Flight controls
propFlightControls = props.globals.getNode(flightControls, 1);
elevatorControl = propFlightControls.getNode("elevator", 1);
elevatorTrimControl = propFlightControls.getNode("elevator-trim", 1);
#Autopilot controls
propAutopilotControls = props.globals.getNode(autopilotControls, 1);
rollControl = propAutopilotControls.getNode("roll", 1);
# values 0 (ROLL switch off) 1 (ROLL switch on)
hdgControl = propAutopilotControls.getNode("hdg", 1);
# values 0 (HDG switch off) 1 (HDG switch on)
modeControl = propAutopilotControls.getNode("mode", 1);
altControl = propAutopilotControls.getNode("alt", 1);
# values 0 (ALT switch off) 1 (ALT switch on)
pitchControl = propAutopilotControls.getNode("pitch", 1);
# values 0 (PITCH switch off) 1 (PITCH switch on)
headingNeedleDeflection = "/instrumentation/nav/heading-needle-deflection";
gsNeedleDeflection = "/instrumentation/nav/gs-needle-deflection";
indicatedPitchDeg = "/instrumentation/attitude-indicator/indicated-pitch-deg";
staticPressure = "/systems/static/pressure-inhg";
altitudePressure = "/autopilot/CENTURYIII/settings/target-alt-pressure";
power="/systems/electrical/outputs/autopilot";
enableAutoTrim = "/sim/model/enable-auto-trim";
filteredHeadingNeedleDeflection = "/autopilot/internal/filtered-heading-needle-deflection";
pressureUnits = { "inHg" : 0, "hPa" : 1 };
altPressure = 0.0;
gsTimeCheck = 0.0;
valueTest = 0;
lastValue = 0;
newValue = 0;
minVoltageLimit = 8.0;
oldMode = 2;
rollControl.setDoubleValue(0.0);
hdgControl.setDoubleValue(0.0);
altControl.setDoubleValue(0.0);
pitchControl.setDoubleValue(0.0);
modeControl.setDoubleValue(2.0);
settingTargetPitchDeg.setDoubleValue(0.0);
settingPitchWheelDeg.setDoubleValue(0.0);
settingDeltaPitch.setDoubleValue(0.0);
settingTargetPressureRate.setDoubleValue(0.0);
settingGScaptured.setDoubleValue(0.0);
# If you need to be able to enable/disable auto trim, make is a menue toggle.
# Auto trim enabled by default
setprop(enableAutoTrim, 1);
autoPitchTrim = 0.0;
apInit = func {
##print("ap init");
##
# Initialises the autopilot.
##
lockAltHold.setBoolValue(0);
lockAprHold.setBoolValue(0);
lockGsHold.setBoolValue(0);
lockHdgHold.setBoolValue(0);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockRollAxis.setBoolValue(0);
lockRollMode.setIntValue(rollModes["OFF"]);
lockPitchAxis.setBoolValue(0);
lockPitchHold.setBoolValue(0);
lockPitchMode.setIntValue(pitchModes["OFF"]);
lockRollArm.setIntValue(rollArmModes["OFF"]);
lockPitchArm.setIntValue(pitchArmModes["OFF"]);
# Reset the memory for power down or power up
settingTargetAltPressure.setDoubleValue(0.0);
settingTargetPitchDeg.setDoubleValue(0.0);
settingTargetPressureRate.setDoubleValue(0.0);
settingTargetInterceptAngle.setDoubleValue(0.0);
settingTargetRollDeg.setDoubleValue(0.0);
settingAutoPitchTrim.setDoubleValue(0.0);
settingGScaptured.setDoubleValue(0.0);
settingRollKnobDeg.setDoubleValue(0.0);
}
apPower = func {
## Monitor autopilot power
## Call apInit if the power is too low
if (getprop(power) < minVoltageLimit) {
newValue = 0;
} else {
newValue = 1;
}
valueTest = newValue - lastValue;
# print("v_test = ", v_test);
if (valueTest > 0.5) {
# autopilot just powered up
print("power up");
apInit();
} elsif (valueTest < -0.5) {
# autopilot just lost power
print("power lost");
apInit();
# note: all button and knobs disabled in functions below
}
lastValue = newValue;
# Update difference between pitch wheel target and indicated pitch.
# Used to animate the Pitch Trim meter to the left of pitch wheel
if (rollControl.getValue() ) {
settingDeltaPitch.setDoubleValue(settingPitchWheelDeg.getValue()
- getprop(indicatedPitchDeg));
} else {
settingDeltaPitch.setDoubleValue(0.0);
}
inrange0 = getprop("/instrumentation/nav[0]/in-range");
# Shut off autopilot if HDG switch on and mode != 2 when NAV flag is on
if ( !inrange0 ) {
if ( hdgControl.getValue() and (modeControl.getValue() != 2)) {
rollControl.setDoubleValue(0.0);
apRollControl();
}
}
settimer(apPower, 0.5);
}
apRollControl = func {
if (rollControl.getValue() ) {
rollButton(1);
} else {
# A/P on/off switch was turned off, so turn off other AP switches
hdgControl.setDoubleValue(0.0); #hdgButton(0);
altControl.setDoubleValue(0.0); #altButton(0);
pitchControl.setDoubleValue(0.0); #pitchButton(0);
# rollButton(0);
apInit();
}
}
apHdgControl = func {
if (hdgControl.getValue() ) {
# hdg switch turned on sets roll
rollControl.setDoubleValue(1.0);
rollButton(1);
##
# hdg switch is on so check which roll mode is set
##
apModeControlsSet();
} else {
# hdg switch turned off resets alt and pitch
hdgControl.setDoubleValue(0.0); hdgButton(0);
altControl.setDoubleValue(0.0); altButton(0);
pitchControl.setDoubleValue(0.0); pitchButton(0);
}
}
apAltControl = func {
if ( altControl.getValue() ){
# Alt switch on so set ROLL, HDG, and PITCH
rollControl.setDoubleValue(1.0);
rollButton(1);
hdgControl.setDoubleValue(1.0);
# roll and hdg switches on so check which roll mode is set
apModeControlsSet();
pitchControl.setDoubleValue(1.0);
pitchButton(1);
altButton(1);
} else {
altButton(0);
}
}
apPitchControl = func {
if ( pitchControl.getValue() ) {
# Pitch switch on so set ROLL and HDG
rollControl.setDoubleValue(1.0);
rollButton(1);
hdgControl.setDoubleValue(1.0);
# roll and hdg switches on so check which roll mode is set
apModeControlsSet();
pitchButton(1);
} else {
altControl.setDoubleValue(0.0);
altButton(0);
pitchButton(0);
}
}
rollKnobUpdate = func {
if ( rollControl.getValue() and !hdgControl.getValue() ) {
settingTargetRollDeg.setDoubleValue( settingRollKnobDeg.getValue() );
}
}
pitchWheelUpdate = func {
if ( rollControl.getValue() and !altControl.getValue() ) {
settingTargetPitchDeg.setDoubleValue( settingPitchWheelDeg.getValue() );
}
}
apModeControlsChange = func {
##
# Delay mode change to allow time for multi-mode rotation
##
settimer(apModeControlsSet, 2);
}
apModeControlsSet = func {
newMode = modeControl.getValue();
##
# Decouple GS if the mode selector is switched from LOC NORM
##
if (oldMode == rollModes["APR"] and newMode != rollModes["APR"])
{
if (lockPitchMode.getValue() == pitchModes["GS"])
{
lockPitchMode.setIntValue(pitchModes["OFF"]);
}
if (lockPitchArmModes.getValue() == pitchArmModes["GS"])
{
lockPitchArmMode.setIntValue(pitchModes["OFF"]);
}
lockGsHold.setBoolValue(0);
settingGScoupled.setDoubleValue(0.0);
}
oldMode = newMode;
#All modes entered from hdg mode
if ( hdgControl.getValue() ) {
hdgButton(1);
if (newMode == 0 ){
navButton();
} elsif (newMode == 1 ) {
omniButton();
} elsif (newMode == 3 ) {
aprButton();
} elsif(newMode == 4 ) {
revButton();
}
} else {
return;
}
}
rollButton = func(switch_on) {
##print("rollButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
if ( switch_on ) {
##
# Engage the autopilot in Wings level mode (ROL) and set the turn rate
# from the "ROLL Knob".
##
lockAprHold.setBoolValue(0);
lockHdgHold.setBoolValue(0);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollMode.setIntValue(rollModes["ROL"]);
lockRollArm.setIntValue(rollArmModes["OFF"]);
} else {
lockAprHold.setBoolValue(0);
lockHdgHold.setBoolValue(0);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockRollAxis.setBoolValue(0);
lockRollMode.setIntValue(rollModes["OFF"]);
lockRollArm.setIntValue(rollArmModes["OFF"]);
}
}
hdgButton = func(switch_on) {
##print("hdgButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
if (switch_on) {
##
# Engage the heading mode (HDG).
##
lockAprHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollMode.setIntValue(rollModes["HDG"]);
lockRollArm.setIntValue(rollArmModes["OFF"]);
settingTargetInterceptAngle.setDoubleValue(0.0);
} else {
lockHdgHold.setBoolValue(0);
rollKnobUpdate();
if ( rollControl.getValue() ) {
lockRollMode.setIntValue(rollModes["ROL"]);
} else {
lockRollMode.setIntValue(rollModes["OFF"]);
}
}
}
navButton = func {
##print("navButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
##
# The Mode Selector and DG Course Selector should be set before switching the HDG
# rocker switch to "on". The DG Course Selector should be set to the OBS "to" or
# "from" bearing.
# Set up NAV mode and switch to the 45 degree angle intercept NAV mode
##
lockAprHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollArm.setIntValue(rollArmModes["NAV"]);
lockRollMode.setIntValue(rollModes["NAV"]);
navArmFromHdg();
}
navArmFromHdg = func
{
##
# Abort the NAV-ARM mode if something has changed the arm mode to something
# else than NAV-ARM.
##
if (lockRollArm.getValue() != rollArmModes["NAV"])
{
return;
}
##
# Activate the nav-hold controller and check the needle deviation.
##
lockNavHold.setBoolValue(1);
deviation = getprop(headingNeedleDeflection);
##
# If the deflection is more than 9.95 degrees wait 5 seconds and check again.
##
if (abs(deviation) > 9.95)
{
#print("deviation");
settimer(navArmFromHdg, 5);
return;
}
##
# If the deviation is less than 10 degrees turn off the NAV-ARM. End of NAV-ARM sequence.
##
elsif (abs(deviation) < 10.0)
{
#print("capture");
lockRollArm.setIntValue(rollArmModes["OFF"]);
}
}
omniButton = func {
##print("navButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
##
# The Mode Selector and DG Course Selector should be set before switching the HDG
# rocker switch to "on". The DG Course Selector should be set to the OBS "to" or
# "from" bearing.
# Set up OMNI mode and switch to the 45 degree angle intercept OMNI mode
##
lockAprHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollArm.setIntValue(rollArmModes["OMNI"]);
lockRollMode.setIntValue(rollModes["OMNI"]);
omniArmFromHdg();
}
omniArmFromHdg = func
{
##
# Abort the OMNI-ARM mode if something has changed the arm mode to something
# else than OMNI-ARM.
##
if (lockRollArm.getValue() != rollArmModes["OMNI"])
{
return;
}
##
# Activate the omni-hold controller and check the needle deviation.
##
lockOmniHold.setBoolValue(1);
deviation = getprop(filteredHeadingNeedleDeflection);
##
# If the deflection is more than 9.95 degrees wait 5 seconds and check again.
##
if (abs(deviation) > 9.95)
{
#print("deviation");
settimer(omniArmFromHdg, 5);
return;
}
##
# If the deviation is less than 10 degrees turn off the OMNI-ARM. End of OMNI-ARM sequence.
##
elsif (abs(deviation) < 10.0)
{
#print("capture");
lockRollArm.setIntValue(rollArmModes["OFF"]);
}
}
aprButton = func {
##print("aprButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
##
# The Mode Selector and DG Course Selector should be set before switching the HDG
# rocker switch to "on". Set the DG Course Selector to the LOC inbound heading.
# Set up APR mode and switch to the 45 degree angle intercept APR mode
##
lockAprHold.setBoolValue(1);
lockRevHold.setBoolValue(0);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollArm.setIntValue(rollArmModes["APR"]);
lockRollMode.setIntValue(rollModes["APR"]);
gsTimeCheck = -1;
aprArmFromHdg();
}
aprArmFromHdg = func
{
##
# Abort the APR-ARM mode if something has changed the arm mode to something
# else than APR-ARM.
##
if (lockRollArm.getValue() != rollArmModes["APR"]
or !lockAltHold.getValue()
or getprop(gsNeedleDeflection) < 0.0)
{
return;
}
gsTimeCheck = gsTimeCheck + 1;
if (gsTimeCheck < 20)
{
settimer(aprArmFromHdg, 1.0);
}
##
# Activate the apr-hold controller and check the needle deviation.
##
lockAprHold.setBoolValue(1);
deviation = getprop(headingNeedleDeflection);
##
# If the deflection is more than 2.5 degrees wait 5 seconds and check again.
##
if (abs(deviation) > 2.495)
{
#print("deviation");
settimer(aprArmFromHdg, 5);
return;
}
##
# If the deviation is less than 2.5 degrees, start the GS-ARM sequence
##
elsif (abs(deviation) < 2.5)
{
lockPitchArm.setIntValue(pitchArmModes["GS"]);
gsArm();
}
}
gsArm = func {
##
# Abort the GS-ARM mode if something has changed the arm mode to something
# else than GS-ARM.
##
if (lockPitchArm.getValue() != pitchArmModes["GS"])
{
return;
}
deviation = getprop(gsNeedleDeflection);
##
# If the deflection is more than 0.25 degrees wait 5 seconds and check again.
##
if (abs(deviation) > 0.25)
{
#print("deviation");
settimer(gsArm, 5);
return;
}
##
# If the deviation is less than 1 then activate the GS pitch mode.
##
elsif (abs(deviation) < 0.251)
{
#print("capture");
lockAltHold.setBoolValue(0);
lockGsHold.setBoolValue(1);
lockPitchMode.setIntValue(pitchModes["GS"]);
lockPitchArm.setIntValue(pitchArmModes["OFF"]);
settingGScaptured.setDoubleValue(1.0);
}
}
revButton = func {
##print("revButton");
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
##
# The Mode Selector and DG Course Selector should be set before switching the HDG
# rocker switch to "on". Set the DG Course Selector to the LOC outbound
# (or reverse) heading.
# Set up REV mode and switch to the 45 degree angle intercept REV mode
##
lockAprHold.setBoolValue(0);
lockRevHold.setBoolValue(0);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollArm.setIntValue(rollArmModes["REV"]);
revArmFromHdg();
}
revArmFromHdg = func
{
##
# Abort the REV-ARM mode if something has changed the arm mode to something
# else than REV-ARM.
##
if (lockRollArm.getValue() != rollArmModes["REV"])
{
return;
}
##
# Activate the rev-hold controller and check the needle deviation.
##
lockRevHold.setBoolValue(1);
deviation = getprop(headingNeedleDeflection);
##
# If the deflection is more than 2.5 degrees wait 5 seconds and check again.
##
if (abs(deviation) > 2.495)
{
#print("deviation");
settimer(revArmFromHdg, 5);
return;
}
##
# If the deviation is less than 2.5 - End of REV-ARM sequence.
##
elsif (abs(deviation) < 2.5)
{
#print("capture");
lockRollArm.setIntValue(rollArmModes["OFF"]);
lockAprHold.setBoolValue(0);
lockRevHold.setBoolValue(1);
lockHdgHold.setBoolValue(1);
lockNavHold.setBoolValue(0);
lockOmniHold.setBoolValue(0);
lockRollAxis.setBoolValue(1);
lockRollMode.setIntValue(rollModes["REV"]);
lockRollArm.setIntValue(rollArmModes["OFF"]);
}
}
altButton = func(switch_on) {
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
if (switch_on) {
lockAltHold.setBoolValue(1);
lockPitchAxis.setBoolValue(1);
# lockPitchMode.setIntValue(pitchModes["ALT"]);
altPressure = getprop(staticPressure);
settingTargetAltPressure.setDoubleValue(altPressure);
# print("enableAutoTrim = ", getprop(enableAutoTrim));
if ( getprop(enableAutoTrim) ) {
settingAutoPitchTrim.setDoubleValue(1);
}
} else {
lockAltHold.setBoolValue(0);
lockPitchAxis.setBoolValue(0);
lockPitchMode.setIntValue(pitchModes["OFF"]);
lockPitchArm.setIntValue(pitchArmModes["OFF"]);
pitchWheelUpdate();
settingTargetPressureRate.setDoubleValue(0.0);
# alt switch is off so make sure the glide slope is disabled
settingGScaptured.setDoubleValue(0.0);
lockGsHold.setBoolValue(0);
}
}
pitchButton = func(switch_on) {
# Disable button if too little power
if (getprop(power) < minVoltageLimit) { return; }
if (switch_on) {
lockPitchHold.setBoolValue(1);
# lockPitchAxis.setBoolValue(1);
# lockPitchMode.setIntValue(pitchModes["AOA"]);
# print("enableAutoTrim = ", getprop(enableAutoTrim));
if ( getprop(enableAutoTrim) ) {
settingAutoPitchTrim.setDoubleValue(1);
}
} else {
lockPitchHold.setBoolValue(0);
lockPitchAxis.setBoolValue(0);
lockPitchMode.setIntValue(pitchModes["OFF"]);
settingAutoPitchTrim.setDoubleValue(0);
}
}
touchPower = func{
setprop(power,apVolts);
}
apVolts = getprop(power);
if ( apVolts == nil or apVolts < minVoltageLimit ) {
# Wait for autopilot to be powered up
var L = setlistener(power, func {
apPower();
removelistener(L);
});
} else {
# Skip the setlistener since autopilot is already powered up
settimer(touchPower ,10);
apPower();
}