diff --git a/A320-100-CFM.xml b/A320-100-CFM.xml
index 4c179888..7d87dcdc 100644
--- a/A320-100-CFM.xml
+++ b/A320-100-CFM.xml
@@ -449,7 +449,6 @@ xsi:noNamespaceSchemaLocation="http://jsbsim.sourceforge.net/JSBSim.xsd">
-
diff --git a/A320-200-CFM.xml b/A320-200-CFM.xml
index 9b8bb8ff..f6f7fb5e 100644
--- a/A320-200-CFM.xml
+++ b/A320-200-CFM.xml
@@ -449,7 +449,6 @@ xsi:noNamespaceSchemaLocation="http://jsbsim.sourceforge.net/JSBSim.xsd">
-
diff --git a/A320-200-IAE.xml b/A320-200-IAE.xml
index 6cc6b204..c52cfa98 100644
--- a/A320-200-IAE.xml
+++ b/A320-200-IAE.xml
@@ -449,7 +449,6 @@ xsi:noNamespaceSchemaLocation="http://jsbsim.sourceforge.net/JSBSim.xsd">
-
diff --git a/A320-main.xml b/A320-main.xml
index 8b3c955c..1dc3821a 100644
--- a/A320-main.xml
+++ b/A320-main.xml
@@ -28,10 +28,27 @@
- 5000
- 200
- 500
- 0
+
+ 1
+
+ 0.0
+ 5000.0
+ 200.0
+ 25000.0
+ 500.0
+
+ -60.0
+ 0.0
+
+ 5.0
+ 5.0
+ 5.0
+ 0.05
+ 1.4
+ 7.0
+ 0.2
+ 2.5
+
@@ -231,7 +248,7 @@
dialog-show
- pushback
+ autopush
-
@@ -1455,6 +1472,22 @@
Aircraft/IDG-A32X/Nasal/icing.nas
+
+ Nasal/autopush.nas
+
+
+ Nasal/autopush_driver.nas
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/A320neo-CFM.xml b/A320neo-CFM.xml
index f240ddd4..c2344ea8 100644
--- a/A320neo-CFM.xml
+++ b/A320neo-CFM.xml
@@ -449,7 +449,6 @@ xsi:noNamespaceSchemaLocation="http://jsbsim.sourceforge.net/JSBSim.xsd">
-
diff --git a/A320neo-PW.xml b/A320neo-PW.xml
index 94da872a..f565b8f2 100644
--- a/A320neo-PW.xml
+++ b/A320neo-PW.xml
@@ -449,7 +449,6 @@ xsi:noNamespaceSchemaLocation="http://jsbsim.sourceforge.net/JSBSim.xsd">
-
diff --git a/Models/A320-100-CFM.xml b/Models/A320-100-CFM.xml
index 62bb0afd..4e6fb174 100644
--- a/Models/A320-100-CFM.xml
+++ b/Models/A320-100-CFM.xml
@@ -316,7 +316,7 @@
Pushback
- Models/Airport/Pushback/Goldhofert.xml
+ Models/Goldhofert-autopush.xml
5.55
0
diff --git a/Models/A320-200-CFM.xml b/Models/A320-200-CFM.xml
index 8a1abda1..3b2f71bd 100644
--- a/Models/A320-200-CFM.xml
+++ b/Models/A320-200-CFM.xml
@@ -348,7 +348,7 @@
Pushback
- Models/Airport/Pushback/Goldhofert.xml
+ Models/Goldhofert-autopush.xml
5.55
0
diff --git a/Models/A320-200-IAE.xml b/Models/A320-200-IAE.xml
index 02707e48..bc17b952 100644
--- a/Models/A320-200-IAE.xml
+++ b/Models/A320-200-IAE.xml
@@ -344,7 +344,7 @@
Pushback
- Models/Airport/Pushback/Goldhofert.xml
+ Models/Goldhofert-autopush.xml
5.55
0
diff --git a/Models/A320neo-CFM.xml b/Models/A320neo-CFM.xml
index 96963b37..8b47fce4 100644
--- a/Models/A320neo-CFM.xml
+++ b/Models/A320neo-CFM.xml
@@ -325,7 +325,7 @@
Pushback
- Models/Airport/Pushback/Goldhofert.xml
+ Models/Goldhofert-autopush.xml
5.55
0
diff --git a/Models/A320neo-PW.xml b/Models/A320neo-PW.xml
index 6e76706a..03f9897b 100644
--- a/Models/A320neo-PW.xml
+++ b/Models/A320neo-PW.xml
@@ -325,7 +325,7 @@
Pushback
- Models/Airport/Pushback/Goldhofert.xml
+ Models/Goldhofert-autopush.xml
5.55
0
diff --git a/Models/Goldhofert-autopush.xml b/Models/Goldhofert-autopush.xml
new file mode 100644
index 00000000..1f256db7
--- /dev/null
+++ b/Models/Goldhofert-autopush.xml
@@ -0,0 +1,130 @@
+
+
+
+ Models/Airport/Pushback/Goldhofert.ac
+
+
+
+
+ Models/Airport/Pushback/Goldhofert-warning-light.xml
+
+ -2.908
+ 0.260
+ 1.326
+ 0
+
+
+
+
+ Models/Airport/Pushback/Goldhofert-warning-light.xml
+
+ -1.781
+ 0.260
+ 1.326
+ 90
+
+
+
+
+ spin
+ wheelfl
+ wheelfr
+
+ gear/gear[0]/rollspeed-ms
+ -16.3
+
+ -2.8976
+ 0
+ 0.5809
+
+
+ 0
+ 1
+ 0
+
+
+
+
+ spin
+ wheelrl
+ wheelrr
+
+ gear/gear[0]/rollspeed-ms
+ -16.3
+
+ 2.0501
+ 0
+ 0.5735
+
+
+ 0
+ 1
+ 0
+
+
+
+
+ select
+
+
+ sim/model/pushback/connected
+
+
+
+
+ rotate
+
+ gear/gear/steering-norm
+
+ 70.0
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+ translate
+
+ gear/gear[0]/compression-ft
+ 0.3048
+
+ 0
+ 0
+ 1
+
+
+
+
+ rotate
+
+ orientation/pitch-deg
+
+ 2.0
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 1
+ 0
+
+
+
diff --git a/Nasal/autopush-README.txt b/Nasal/autopush-README.txt
new file mode 100644
index 00000000..698db78e
--- /dev/null
+++ b/Nasal/autopush-README.txt
@@ -0,0 +1,117 @@
+AUTOMATIC PUSHBACK FOR FLIGHTGEAR
+Version 0.1.1
+
+Copyright (c) 2018 Michael Danilov
+Some of the code (c) FlightGear
+Distribute under the terms of GPLv2.
+
+
+FILES
+
+Nasal/autopush-README.txt this file
+Models/Goldhofert-autopush.xml the animation for FGDATA's Goldhofer
+Nasal/autopush.nas basic pushback logic
+Nasal/autopush_driver.nas pushback driver
+gui/dialogs/autopush.xml GUI dialog
+
+
+INSTALLATION
+
+Only the minimal working example is covered here. Later you will have
+to implement correct steering in your FDM (driving the
+"/sim/model/pushback/yaw-deg") and pushback availability
+("/sim/model/pushback/available") -- because those are too
+aircraft-specific.
+
+1. Set up the pushback logic. Add the following tags in your set.xml.
+ Replace the "/fdm/jsbsim/gear/unit[0]/steering/pos-deg" with the
+ property for the front wheel steering degrees of your aircraft.
+
+ Under :
+
+
+
+ 1
+
+ 0.0
+ 100.0
+ 25.0
+ 25000.0
+ 0.0
+
+ -60.0
+ 0.0
+
+ 10.0
+ 10.0
+ 5.0
+ 0.05
+ 1.4
+ 7.0
+ 0.2
+ 2.5
+
+
+
+ Under :
+
+
+ Nasal/autopush.nas
+
+
+ Nasal/autopush_driver.nas
+
+
+2. Connect the FDM's external force.
+
+ a) JSBSim:
+
+ a.1. Add the following under of your
+ JSBSim XML. Change the coordinates under to equal
+ those of your front bogey.
+
+
+
+ 0.0
+ 0.0
+ 0.0
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+ a.2. Add the following under of
+ your set.xml.
+
+
+
+
+
+
+
+ b) YASim: TODO
+
+3. Copy the gui/dialogs/autopush.xml from the distribution to your
+ aircraft's gui/dialogs directory. Add it to the menu (see
+ FlightGear documentation for editing the menu).
+
+4. Copy the Models/Goldhofert-autopush.xml to your aircraft's Models/
+ directory. Edit all the places marked by "SETTING" to match your
+ setup. Include it in your Model XML, with offsets equal to the
+ front wheel's contact point.
+
+5. Add the following in your Model XML. Change the coordinates under
+ to equal those of your front bogey.
+
+
+ Pushback
+ Models/Airport/Pushback/Goldhofert-autopush.xml
+
+ 0.0
+ 0.0
+ 0.0
+
+
diff --git a/Nasal/autopush.nas b/Nasal/autopush.nas
new file mode 100644
index 00000000..f4feb0b2
--- /dev/null
+++ b/Nasal/autopush.nas
@@ -0,0 +1,76 @@
+# AUTOPUSH
+# Basic pushback logic class.
+#
+# Copyright (c) 2018 Michael Danilov
+# Distribute under the terms of GPLv2.
+
+
+var _K_p = nil;
+var _K_i = nil;
+var _K_d = nil;
+var _F_i = nil;
+var _int = nil;
+var _deltaV_old = nil;
+var _t_old = nil;
+
+var loop = func() {
+ if(!getprop("/sim/model/pushback/available")){
+ stop();
+ return;
+ }
+ var force = 0.0;
+ # Rollspeed is only adequate if the wheel is touching the ground.
+ if(getprop("/gear/gear[0]/wow")){
+ var deltaV =
+ getprop("/sim/model/pushback/target-speed-km_h") -
+ getprop("/gear/gear[0]/rollspeed-ms") * 3.6;
+ var dV = deltaV - _deltaV_old;
+ var t = getprop("/sim/time/elapsed-sec");
+ var dt = math.max(t - _t_old, 0.0001);
+ _int = math.min(math.max(_int + dV * dt, -_F_i), _F_i);
+ force = (_K_p * deltaV + _K_d * dV / dt + _K_i * _int) * KG2LB;
+ _deltaV_old = deltaV;
+ _t_old = t;
+ var yaw = getprop("/sim/model/pushback/yaw-deg") * D2R;
+ setprop("/sim/model/pushback/force-x", math.cos(yaw));
+ setprop("/sim/model/pushback/force-y", math.sin(yaw));
+ }
+ setprop("/sim/model/pushback/force-lbf", force);
+}
+
+var timer = maketimer(0.026, func{loop()});
+
+var start = func() {
+ _K_p = getprop("/sim/model/pushback/K_p");
+ _K_i = getprop("/sim/model/pushback/K_i");
+ _K_d = getprop("/sim/model/pushback/K_d");
+ _F_i = getprop("/sim/model/pushback/F_i");
+ _int = 0.0;
+ _deltaV_old = 0.0;
+ _t_old = getprop("/sim/time/elapsed-sec");
+ setprop("/sim/model/pushback/connected", 1);
+ if(!timer.isRunning){
+ screen.log.write("(pushback): Release brakes.");
+ }
+ timer.start();
+}
+
+var stop = func() {
+ if(timer.isRunning){
+ screen.log.write("(pushback): Pushback disconnected, bypass pin removed.");
+ }
+ timer.stop();
+ setprop("/sim/model/pushback/force-lbf", 0.0);
+ setprop("/sim/model/pushback/connected", 0);
+}
+
+var toggle = func(){
+ if(
+ getprop("/sim/model/pushback/enabled") *
+ getprop("/sim/model/pushback/available")
+ ){
+ start();
+ }else{
+ stop();
+ }
+}
diff --git a/Nasal/autopush_driver.nas b/Nasal/autopush_driver.nas
new file mode 100644
index 00000000..76126ade
--- /dev/null
+++ b/Nasal/autopush_driver.nas
@@ -0,0 +1,113 @@
+# AUTOPUSH
+# Pushback driver class.
+#
+# Command the pushback to tow/push the aircract facing set heading.
+#
+# Copyright (c) 2018 Michael Danilov
+# Distribute under the terms of GPLv2.
+
+
+# FIXME When facing, acccount for current steering position, to work without dry steering.
+
+
+var _K_V_push = nil;
+var _F_V_push = nil;
+var _S_min = nil;
+var _K_psi_push = nil;
+var _K_V_turn = nil;
+var _F_V_turn = nil;
+var _K_psi_turn = nil;
+var _V_min = nil;
+
+var _turning = nil;
+var _sign = nil;
+var _target = nil;
+var _psi_parking = nil;
+var _psi_turn = nil;
+
+var loop = func() {
+ if(!getprop("/sim/model/pushback/connected")){
+ stop();
+ return;
+ }
+ var V = 0.0;
+ var steering = 0.0;
+ var deltapsi = 0.0;
+ var psi = getprop("/orientation/heading-deg");
+ if(!_turning){
+ # Initially follow a straight line parallel to our initial heading.
+ var (A, S) = courseAndDistance(_target);
+ S *= NM2M;
+ # Stop when the bearing flips, guarante against infinite pushing.
+ if(
+ (abs(S) < _S_min) +
+ (abs(saw180(_psi_parking - A - math.min(_sign, 0.0) * 180.0) > 90.0))
+ ){
+ _turning = 1;
+ screen.log.write("(pushback): Turning to face heading " ~ int(_psi_turn) ~ ".");
+ }
+ deltapsi = saw180(_psi_parking - psi);
+ V = math.min(math.max(_K_V_push * _sign * (S / _S_min), -_F_V_push), _F_V_push);
+ steering = math.min(math.max(_K_psi_push * _sign * deltapsi, -1.0), 1.0);
+ }else{
+ # Turn (almost) in place.
+ deltapsi = saw180(_psi_turn - psi);
+ V = math.min(math.max(_K_V_turn * _sign * abs(deltapsi), -_F_V_turn), _F_V_turn);
+ # We are done when the heading error is small.
+ if(abs(V) < _V_min){
+ stop();
+ return;
+ }
+ steering = math.min(math.max(_K_psi_turn * _sign * deltapsi, -1.0), 1.0);
+ }
+ setprop("/sim/model/pushback/target-speed-km_h", V);
+ setprop("/sim/model/pushback/driver/steer-cmd-norm", steering);
+}
+
+var timer = maketimer(0.051, func{loop()});
+
+var start = func() {
+ _K_V_push = getprop("/sim/model/pushback/driver/K_V_push");
+ _F_V_push = getprop("/sim/model/pushback/driver/F_V_push");
+ _S_min = getprop("/sim/model/pushback/driver/S_min-m");
+ _K_psi_push = getprop("/sim/model/pushback/driver/K_psi_push");
+ _K_V_turn = getprop("/sim/model/pushback/driver/K_V_turn");
+ _F_V_turn = getprop("/sim/model/pushback/driver/F_V_turn");
+ _K_psi_turn = getprop("/sim/model/pushback/driver/K_psi_turn");
+ _V_min = getprop("/sim/model/pushback/driver/V_min");
+ if(!getprop("/sim/model/pushback/connected")){
+ return;
+ }
+ towdist = getprop("/sim/model/pushback/driver/tow-distance-m");
+ _target = geo.aircraft_position();
+ _psi_parking = getprop("/orientation/heading-deg");
+ _target.apply_course_distance(_psi_parking, towdist);
+ _psi_turn = getprop("/sim/model/pushback/driver/face-heading-deg");
+ _sign = 1.0 - 2.0 * (towdist < 0.0);
+ _turning = 0;
+ timer.start();
+ if(_sign < 0.0){
+ screen.log.write("(pushback): Pushing back.");
+ }else{
+ screen.log.write("(pushback): Towing.");
+ }
+}
+
+var stop = func() {
+ if(timer.isRunning){
+ screen.log.write("(pushback): Done.");
+ }
+ timer.stop();
+ setprop("/sim/model/pushback/target-speed-km_h", 0.0);
+}
+
+# Sawtooth: -180..+180 deg.
+var saw180 = func(p) {
+ while (p <= -180.0){
+ p += 360.0;
+ }
+ while (p > 180.0){
+ p -= 360.0;
+ }
+ return p;
+}
diff --git a/Systems/pushback.xml b/Systems/pushback.xml
deleted file mode 100644
index abb72c46..00000000
--- a/Systems/pushback.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
- /sim/model/pushback/target-speed-fps
-
-
-
-
-
- /sim/model/pushback/position-norm gt 0.95
- /gear/gear/wow == 1
- gear/unit[0]/wheel-speed-fps lt 50
-
-
-
-
- /sim/model/pushback/target-speed-fps
- -gear/unit[0]/wheel-speed-fps
-
-
-
- systems/pushback/speed-error
- /sim/model/pushback/kp
- /sim/model/pushback/ki
- /sim/model/pushback/kd
- systems/pushback/linked
-
-
-
-
-
-
- systems/pushback/linked == 0
-
-
-
-
-
-
diff --git a/gui/dialogs/autopush.xml b/gui/dialogs/autopush.xml
new file mode 100644
index 00000000..fa7371a4
--- /dev/null
+++ b/gui/dialogs/autopush.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ autopush
+ vbox
+
+
+ hbox
+
+
+
+
+ true
+
+
+
+
+
+
+
+ true
+ vbox
+ center
+ top
+
+
+ left
+
+ /sim/model/pushback/enabled
+ dialog-apply
+
+ nasal
+
+
+
+
+
+ hbox
+
+
+
+
+
0
+ 2
+ -15
+ 15
+ /sim/model/pushback/target-speed-km_h
+ true
+
+ dialog-apply
+
+
+
+ 16
+ /sim/model/pushback/target-speed-km_h
+ %3.0f
+ true
+
+
+
+
+
+
+
+
+
+ hbox
+
+
+
+
+ 0
+ 2
+ -100
+ 100
+ /sim/model/pushback/driver/tow-distance-m
+
+ dialog-apply
+
+
+
+ 16
+ /sim/model/pushback/driver/tow-distance-m
+ %4.0f
+ true
+
+
+
+
+
+
+
+ hbox
+
+
+
+
+ 0
+ 2
+ 0
+ 360
+ /sim/model/pushback/driver/face-heading-deg
+
+ dialog-apply
+
+
+
+ 16
+ /sim/model/pushback/driver/face-heading-deg
+ %3.0f
+ true
+
+
+
+
+ hbox
+
+
+
+
+
+
+
diff --git a/gui/dialogs/pushback.xml b/gui/dialogs/pushback.xml
new file mode 100644
index 00000000..8c83c721
--- /dev/null
+++ b/gui/dialogs/pushback.xml
@@ -0,0 +1,201 @@
+
+
+
+ pushback
+ vbox
+
+
+
+
+
+ var pushback_position = aircraft.door.new("sim/model/pushback", 10.0);
+ pushback_position.setpos(pushback_position.getpos());
+ props.globals.getNode("/sim/model/pushback/enabled", 1 ).setBoolValue(1);
+ props.globals.initNode("/sim/model/pushback/target-speed-fps", 0.0 );
+
+
+ pushback_position.setpos(0);
+ setprop("/sim/model/pushback/enabled", 0 );
+ setprop("/sim/model/pushback/target-speed-fps", 0 );
+ setprop("/sim/model/pushback/force", 0);
+
+
+
+
+ hbox
+
+
+
+
+ true
+
+
+
+
+
+
+
+ true
+ vbox
+ center
+ top
+
+
+ left
+
+
+
+
+ nasal
+
+
+
+
+
+
+
+
+ hbox
+
+
+
+
+ 0
+ 2
+ -25
+ 25
+ /sim/model/pushback/target-speed-fps
+ true
+
+ dialog-apply
+
+
+
+ 16
+ /sim/model/pushback/target-speed-fps
+ %3.0f
+ true
+
+
+
+
+
+
+
+
+
+ hbox
+
+
+
+
+ 0
+ 2
+ -100
+ 100
+ /nasal/pushback_driver/tow-distance-m
+
+ dialog-apply
+
+
+
+ 16
+ /nasal/pushback_driver/tow-distance-m
+ %4.0f
+ true
+
+
+
+
+
+
+
+ hbox
+
+
+
+
+ 0
+ 2
+ 0
+ 360
+ /nasal/pushback_driver/face-heading-deg
+
+ dialog-apply
+
+
+
+ 16
+ /nasal/pushback_driver/face-heading-deg
+ %3.0f
+ true
+
+
+
+
+ hbox
+
+
+
+
+
+
+