1
0
Fork 0

Control: UFO Autopilot from Josh Davidson

This commit is contained in:
Stuart Buchanan 2022-04-09 17:26:31 +01:00
parent 7ee03dd9da
commit e4f578a842
3 changed files with 928 additions and 0 deletions

View file

@ -0,0 +1,471 @@
<?xml version="1.0"?>
<PropertyList>
<name>autopilot</name>
<layout>vbox</layout>
<!--
/autopilot/locks/altitude
/autopilot/locks/heading
/autopilot/locks/speed
/autopilot/settings/heading-bug-deg
/autopilot/settings/target-roll-deg
/autopilot/settings/target-agl-ft
/autopilot/settings/target-altitude-ft
/autopilot/settings/target-pitch-deg
/autopilot/settings/target-speed-kt
/autopilot/settings/target-speed-mach
/autopilot/settings/true-heading-deg
/autopilot/settings/vertical-speed-fpm
-->
<group>
<layout>hbox</layout>
<empty><stretch>1</stretch></empty>
<text>
<label>Autopilot Settings</label>
</text>
<empty><stretch>1</stretch></empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<default>1</default>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
<binding>
<command>dialog-apply</command>
</binding>
</button>
</group>
<hrule/>
<nasal>
<open>
## manage one AP property group with checkbox and radio buttons
#
Group = {
new : func(name, options) {
var m = { parents: [Group] };
m.name = name;
m.enabled = 0;
m.mode = options[0];
m.options = [];
var locks = props.globals.getNode("/autopilot/locks", 1);
if (locks.getNode(name) == nil or locks.getNode(name, 1).getValue() == nil) {
locks.getNode(name, 1).setValue("");
}
m.lock = locks.getNode(name);
m.active = dlg.getNode(name ~ "-active", 1);
foreach (var o; options) {
var node = dlg.getNode(o);
if (node == nil) {
node = dlg.getNode(o, 1);
node.setBoolValue(0);
}
append(m.options, node);
if (m.lock.getValue() == o) {
m.mode = o;
}
}
m.listener = setlistener(m.lock, func(n) { m.update(n.getValue()) }, 1);
return m;
},
del : func {
removelistener(me.listener);
},
## handle checkbox
#
enable : func {
me.enabled = me.active.getBoolValue();
me.lock.setValue(me.enabled ? me.mode : "");
},
## handle radiobuttons
#
set : func(mode) {
me.mode = mode;
foreach (var o; me.options) {
o.setBoolValue(o.getName() == mode);
}
if (me.enabled) {
me.lock.setValue(mode);
}
},
## update checkboxes/radiobuttons state from the AP (listener callback)
#
update : func(mode) {
me.enabled = (mode != "");
me.active.setBoolValue(me.enabled);
if (mode == "") {
mode = me.mode;
}
foreach (var o; me.options) {
o.setBoolValue(o.getName() == mode);
}
},
};
## create and initialize input field properties if necessary
#
var apset = props.globals.getNode("/autopilot/settings", 1);
foreach (var p; ["heading-bug-deg", "target-roll-deg", "true-heading-deg", "vertical-speed-fpm",
"target-pitch-deg", "target-fpa-deg", "target-altitude-ft",
"target-agl-ft", "target-speed-kt", "target-speed-mach"]) {
if ((var n = apset.getNode(p)) == nil or n.getType() == "NONE") {
apset.getNode(p, 1).setDoubleValue(0);
}
}
var dlg = props.globals.getNode("/sim/gui/dialogs/autopilot", 1);
# - first entry ("heading" etc.) is the target property in /autopilot/locks/ *and*
# the checkbox state property name (with "-active" appended);
# - second entry is a list of available options for the /autopilot/locks/* property
# and used as radio button state property; the first list entry is used as default
#
var hdg = Group.new("heading", ["dg-heading-hold", "wing-leveler", "true-heading-hold", "nav1-hold"]);
var vel = Group.new("speed", ["speed-with-throttle"]);
var alt = Group.new("altitude", ["altitude-hold", "vertical-speed-hold", "pitch-hold",
"agl-hold", "gs1-hold"]);
</open>
<close>
hdg.del();
vel.del();
alt.del();
</close>
</nasal>
<group>
<layout>hbox</layout>
<default-padding>8</default-padding>
<group>
<!-- Heading -->
<layout>vbox</layout>
<group>
<layout>hbox</layout>
<checkbox>
<label>Heading Control</label>
<halign>fill</halign>
<property>/sim/gui/dialogs/autopilot/heading-active</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script>hdg.enable()</script>
</binding>
</checkbox>
</group>
<group>
<layout>table</layout>
<text>
<label>Wings Level</label>
<halign>right</halign>
<row>0</row>
<col>0</col>
</text>
<radio>
<row>0</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/wing-leveler</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>hdg.set("wing-leveler")</script>
</binding>
</radio>
<text>
<label>Heading Bug</label>
<halign>right</halign>
<row>1</row>
<col>0</col>
</text>
<radio>
<row>1</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/dg-heading-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>hdg.set("dg-heading-hold")</script>
</binding>
</radio>
<input>
<row>1</row>
<col>2</col>
<property>/autopilot/settings/heading-bug-deg</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<label>True Heading</label>
<halign>right</halign>
<row>2</row>
<col>0</col>
<visible>
<not>
<property>/autopilot/settings/gps-driving-true-heading</property>
</not>
</visible>
</text>
<!-- alternate UI for above, when GPS is controlling -->
<text>
<visible>
<property>/autopilot/settings/gps-driving-true-heading</property>
</visible>
<label>GPS/FMS Heading</label>
<halign>right</halign>
<row>2</row>
<col>0</col>
</text>
<radio>
<row>2</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/true-heading-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>hdg.set("true-heading-hold")</script>
</binding>
</radio>
<input>
<visible>
<not>
<property>/autopilot/settings/gps-driving-true-heading</property>
</not>
</visible>
<row>2</row>
<col>2</col>
<property>/autopilot/settings/true-heading-deg</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<!-- alternate UI for above, when GPS is controlling -->
<text>
<visible>
<property>/autopilot/settings/gps-driving-true-heading</property>
</visible>
<halign>right</halign>
<row>2</row>
<col>2</col>
<label>MMMM</label>
<format>%3.0f*</format>
<property>/autopilot/settings/true-heading-deg</property>
<live>true</live>
</text>
<text>
<label>NAV1 CDI Course</label>
<halign>right</halign>
<row>3</row>
<col>0</col>
</text>
<radio>
<row>3</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/nav1-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>hdg.set("nav1-hold")</script>
</binding>
</radio>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<checkbox>
<label>Velocity Control</label>
<halign>fill</halign>
<property>/sim/gui/dialogs/autopilot/speed-active</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script>vel.enable()</script>
</binding>
</checkbox>
<input>
<rowspan>2</rowspan>
<property>/autopilot/settings/target-speed-kt</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
</group>
</group>
<!-- End of Heading/Speed -->
<vrule/>
<group>
<!-- Pitch/Altitude -->
<layout>vbox</layout>
<group>
<layout>hbox</layout>
<checkbox>
<label>Pitch/Altitude Control</label>
<halign>fill</halign>
<property>/sim/gui/dialogs/autopilot/altitude-active</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script>alt.enable()</script>
</binding>
</checkbox>
</group>
<group>
<layout>table</layout>
<text>
<label>Vertical Speed</label>
<halign>right</halign>
<row>0</row>
<col>0</col>
</text>
<radio>
<row>0</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/vertical-speed-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>alt.set("vertical-speed-hold")</script>
</binding>
</radio>
<input>
<row>0</row>
<col>2</col>
<property>/autopilot/settings/vertical-speed-fpm</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<label>Pitch Hold</label>
<halign>right</halign>
<row>1</row>
<col>0</col>
</text>
<radio>
<row>1</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/pitch-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>alt.set("pitch-hold")</script>
</binding>
</radio>
<input>
<row>1</row>
<col>2</col>
<property>/autopilot/settings/target-pitch-deg</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<label>Altitude Hold</label>
<halign>right</halign>
<row>2</row>
<col>0</col>
</text>
<radio>
<row>2</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/altitude-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>alt.set("altitude-hold")</script>
</binding>
</radio>
<input>
<row>2</row>
<col>2</col>
<property>/autopilot/settings/target-altitude-ft</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<label>AGL Hold</label>
<halign>right</halign>
<row>3</row>
<col>0</col>
</text>
<radio>
<row>3</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/agl-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>alt.set("agl-hold")</script>
</binding>
</radio>
<input>
<row>3</row>
<col>2</col>
<property>/autopilot/settings/target-agl-ft</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<label>NAV1 Glideslope</label>
<halign>right</halign>
<row>4</row>
<col>0</col>
</text>
<radio>
<row>4</row>
<col>1</col>
<property>/sim/gui/dialogs/autopilot/gs1-hold</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>alt.set("gs1-hold")</script>
</binding>
</radio>
</group>
<empty>
<stretch>true</stretch>
</empty>
</group>
<!-- End of Pitch/Altitude VBox -->
</group>
<hrule/>
<button>
<legend>Close</legend>
<default>true</default>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
</PropertyList>

View file

@ -0,0 +1,451 @@
<?xml version="1.0"?>
<!-- UFO Autopilot -->
<!-- Copyright (c) 2022 Josh Davidson (Octal450) -->
<PropertyList>
<!-- Logic -->
<logic> <!-- Roll channel active -->
<input>
<or>
<equals>
<property>/autopilot/locks/heading</property>
<value>wing-leveler</value>
</equals>
<equals>
<property>/autopilot/locks/heading</property>
<value>dg-heading-hold</value>
</equals>
<equals>
<property>/autopilot/locks/heading</property>
<value>true-heading-hold</value>
</equals>
<equals>
<property>/autopilot/locks/heading</property>
<value>nav1-hold</value>
</equals>
</or>
</input>
<output>/autopilot/internal/roll-active</output>
</logic>
<logic> <!-- Basic pitch hold active -->
<input>
<equals>
<property>/autopilot/locks/altitude</property>
<value>pitch-hold</value>
</equals>
</input>
<output>/autopilot/internal/pitch-hold-active</output>
</logic>
<logic> <!-- Pitch channel active -->
<input>
<or>
<equals>
<property>/autopilot/locks/altitude</property>
<value>vertical-speed-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>pitch-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>altitude-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>agl-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>gs1-hold</value>
</equals>
</or>
</input>
<output>/autopilot/internal/pitch-active</output>
</logic>
<logic> <!-- Fpm channel active -->
<input>
<or>
<equals>
<property>/autopilot/locks/altitude</property>
<value>vertical-speed-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>altitude-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>agl-hold</value>
</equals>
<equals>
<property>/autopilot/locks/altitude</property>
<value>gs1-hold</value>
</equals>
</or>
</input>
<output>/autopilot/internal/vs-active</output>
</logic>
<logic> <!-- Throttle channel active -->
<input>
<equals>
<property>/autopilot/locks/speed</property>
<value>speed-with-throttle</value>
</equals>
</input>
<output>/autopilot/internal/throttle-active</output>
</logic>
<filter> <!-- Heading error computer -->
<name>Heading/Nav Error Deg</name>
<type>gain</type>
<gain>1.0</gain>
<input>
<condition>
<equals>
<property>/autopilot/locks/heading</property>
<value>nav1-hold</value>
</equals>
</condition>
<property>/instrumentation/nav[0]/radials/target-auto-hdg-deg</property>
<offset>
<property>/orientation/heading-deg</property>
<scale>-1.0</scale>
</offset>
</input>
<input>
<condition>
<equals>
<property>/autopilot/locks/heading</property>
<value>true-heading-hold</value>
</equals>
</condition>
<property>/autopilot/settings/true-heading-deg</property>
<offset>
<property>/orientation/heading-deg</property>
<scale>-1.0</scale>
</offset>
</input>
<input>
<condition>
<equals>
<property>/autopilot/locks/heading</property>
<value>dg-heading-hold</value>
</equals>
</condition>
<property>/autopilot/settings/heading-bug-deg</property>
<offset>
<property>/orientation/heading-magnetic-deg</property>
<scale>-1.0</scale>
</offset>
</input>
<input>0</input>
<output>/autopilot/internal/heading-error-deg</output>
<period>
<min>-180</min>
<max>180</max>
</period>
</filter>
<filter> <!-- Clamp glideslope target fpm -->
<name>G/S FPM Calc</name>
<type>gain</type>
<gain>1.0</gain>
<input>/instrumentation/nav[0]/gs-rate-of-climb-fpm</input>
<output>/autopilot/internal/nav1-rate-of-climb-fpm</output>
<min>-10000</min>
<max>0</max>
</filter>
<!-- Roll Axis -->
<filter> <!-- Copy target roll angle, 0 by default for wingn leveller -->
<name>IT-CONTROLLER: Roll Deg/Wings Level</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<equals>
<property>/autopilot/locks/heading</property>
<value>wing-leveler</value>
</equals>
</condition>
</enable>
<input>/autopilot/settings/target-roll-deg</input>
<output>/autopilot/internal/target-roll-deg</output>
</filter>
<filter> <!-- Compute target roll angle from heading error -->
<name>IT-CONTROLLER: Heading</name>
<type>gain</type>
<gain>0.5</gain>
<enable>
<condition>
<or>
<equals>
<property>/autopilot/locks/heading</property>
<value>dg-heading-hold</value>
</equals>
<equals>
<property>/autopilot/locks/heading</property>
<value>true-heading-hold</value>
</equals>
<equals>
<property>/autopilot/locks/heading</property>
<value>nav1-hold</value>
</equals>
</or>
</condition>
</enable>
<input>/autopilot/internal/heading-error-deg</input>
<reference>0</reference>
<output>/autopilot/internal/target-roll-deg</output>
<min>-90</min>
<max>90</max>
</filter>
<filter> <!-- Smooth engagement by syncing when disabled -->
<name>System Command: Roll Target Inactive Sync</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<not><property>/autopilot/internal/roll-active</property></not>
</condition>
</enable>
<input>/orientation/roll-deg</input>
<output>/autopilot/internal/target-roll-deg</output>
<min>-90</min>
<max>90</max>
</filter>
<filter> <!-- Aileron elevator controller -->
<name>System Command: Aileron</name>
<type>noise-spike</type>
<feedback-if-disabled>true</feedback-if-disabled>
<initialize-to>output</initialize-to>
<enable>
<condition>
<property>/autopilot/internal/roll-active</property>
</condition>
</enable>
<input>
<expression>
<div>
<property>/autopilot/internal/target-roll-deg</property>
<value>90</value>
</div>
</expression>
</input>
<output>/controls/flight/aileron</output>
<max-rate-of-change>1.2</max-rate-of-change>
</filter>
<!-- Pitch Axis -->
<filter> <!-- Compute target fpm from altitude error -->
<name>IT-CONTROLLER: Altitude Capture/Hold</name>
<type>gain</type>
<gain>-20</gain>
<input>/position/altitude-ft</input>
<reference>/autopilot/settings/target-altitude-ft</reference>
<output>/autopilot/internal/target-fpm-alt</output>
</filter>
<filter> <!-- Compute target fpm from agl error -->
<name>IT-CONTROLLER: AGL Capture/Hold</name>
<type>gain</type>
<gain>-20</gain>
<input>/position/altitude-agl-ft</input>
<reference>/autopilot/settings/target-agl-ft</reference>
<output>/autopilot/internal/target-fpm-agl</output>
</filter>
<filter> <!-- Copy target pitch angle -->
<name>IT-CONTROLLER: Pitch Deg</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<property>/autopilot/internal/pitch-hold-active</property>
</condition>
</enable>
<input>/autopilot/settings/target-pitch-deg</input>
<output>/autopilot/internal/target-pitch-deg</output>
</filter>
<filter> <!-- Select target vertical speed -->
<name>IT-CONTROLLER: Pitch Deg</name>
<type>gain</type>
<gain>1.0</gain>
<input>
<condition>
<equals>
<property>/autopilot/locks/altitude</property>
<value>altitude-hold</value>
</equals>
</condition>
<property>/autopilot/internal/target-fpm-alt</property>
</input>
<input>
<condition>
<equals>
<property>/autopilot/locks/altitude</property>
<value>agl-hold</value>
</equals>
</condition>
<property>/autopilot/internal/target-fpm-agl</property>
</input>
<input>
<condition>
<equals>
<property>/autopilot/locks/altitude</property>
<value>gs1-hold</value>
</equals>
</condition>
<property>/autopilot/internal/nav1-rate-of-climb-fpm</property>
</input>
<input>/autopilot/settings/vertical-speed-fpm</input>
<output>/autopilot/internal/target-fpm</output>
</filter>
<filter> <!-- Main fpm controller -->
<name>IT-CONTROLLER: FPM Hold</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<property>/autopilot/internal/vs-active</property>
</condition>
</enable>
<input> <!-- Reversed from UFO.cxx -->
<expression>
<rad2deg>
<asin>
<div>
<div>
<div>
<property>/autopilot/internal/target-fpm</property>
<value>60</value> <!-- Fps to fpm -->
</div>
<value>3.28084</value> <!-- Meters to feet -->
</div>
<max>
<product>
<property>/velocities/airspeed-kt</property>
<value>0.514444</value> <!-- Knots to meters/sec -->
</product>
<value>0.0000001</value> <!-- Prevent divide by 0 -->
</max>
</div>
</asin>
</rad2deg>
</expression>
</input>
<output>/autopilot/internal/target-pitch-deg</output>
</filter>
<filter> <!-- Copy target pitch angle -->
<name>IT-CONTROLLER: Pitch Deg</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<property>/autopilot/internal/pitch-hold-active</property>
</condition>
</enable>
<input>/autopilot/settings/target-pitch-deg</input>
<output>/autopilot/internal/target-pitch-deg</output>
</filter>
<filter> <!-- Smooth engagement by syncing when disabled -->
<name>System Command: Pitch Target Inactive Sync</name>
<type>gain</type>
<gain>1.0</gain>
<enable>
<condition>
<not><property>/autopilot/internal/pitch-active</property></not>
</condition>
</enable>
<input>/orientation/pitch-deg</input>
<output>/autopilot/internal/target-pitch-deg</output>
<min>-90</min>
<max>90</max>
</filter>
<filter> <!-- Main elevator controller -->
<name>System Command: Elevator</name>
<type>noise-spike</type>
<feedback-if-disabled>true</feedback-if-disabled>
<initialize-to>output</initialize-to>
<enable>
<condition>
<property>/autopilot/internal/pitch-active</property>
</condition>
</enable>
<input>
<expression>
<div>
<property>/autopilot/internal/target-pitch-deg</property>
<value>-90</value>
</div>
</expression>
</input>
<output>/controls/flight/elevator</output>
<max-rate-of-change>1.2</max-rate-of-change>
</filter>
<!-- Throttle Axis -->
<filter> <!-- Main throttle controller -->
<name>System Command: Throttle</name>
<type>noise-spike</type>
<feedback-if-disabled>true</feedback-if-disabled>
<initialize-to>output</initialize-to>
<enable>
<condition>
<property>/autopilot/internal/throttle-active</property>
</condition>
</enable>
<input>
<expression>
<div>
<product>
<property>/autopilot/settings/target-speed-kt</property>
<value>0.514444</value> <!-- Knots to meters/sec -->
</product>
<max>
<property>/engines/engine[0]/speed-max-mps</property>
<value>0.0000001</value> <!-- Prevent divide by 0 -->
</max>
</div>
</expression>
</input>
<output>/controls/engines/engine[0]/throttle</output>
<max-rate-of-change>0.3</max-rate-of-change>
</filter>
<!-- Trim Axis -->
<filter> <!-- Centers all the trims otherwise the system can't work -->
<name>System Command: Trim</name>
<type>gain</type>
<gain>0.0</gain>
<enable>
<condition>
<property>/autopilot/internal/pitch-active</property>
</condition>
</enable>
<input>0</input>
<output>
<property>/controls/flight/aileron-trim</property>
<property>/controls/flight/elevator-trim</property>
<property>/controls/flight/rudder-trim</property>
</output>
<min>-1.0</min>
<max>1.0</max>
</filter>
</PropertyList>

View file

@ -248,6 +248,12 @@
<line>In the adjustment dialog (= key) holding the Ctrl or Shift key down</line>
<line>makes slider effects coarser/finer.</line>
</help>
<systems>
<autopilot n="0">
<path>Aircraft/ufo/ufo-autopilot.xml</path>
</autopilot>
</systems>
</sim>
<controls>
<lighting>