From 5f85d712aaa3ad0c30bcebd4eefeeecb26ec52a4 Mon Sep 17 00:00:00 2001
From: merspieler <merspieler@users.noreply.github.com>
Date: Sat, 31 Mar 2018 20:54:40 +0200
Subject: [PATCH 1/2] Added new implementation of the pushback

Signed-off-by: merspieler <merspieler@users.noreply.github.com>
---
 A320-100-CFM.xml               |   1 -
 A320-200-CFM.xml               |   1 -
 A320-200-IAE.xml               |   1 -
 A320-main.xml                  |  45 +++++++-
 A320neo-CFM.xml                |   1 -
 A320neo-PW.xml                 |   1 -
 Models/A320-100-CFM.xml        |   2 +-
 Models/A320-200-CFM.xml        |   2 +-
 Models/A320-200-IAE.xml        |   2 +-
 Models/A320neo-CFM.xml         |   2 +-
 Models/A320neo-PW.xml          |   2 +-
 Models/Goldhofert-autopush.xml | 130 +++++++++++++++++++++
 Nasal/autopush-README.txt      | 117 +++++++++++++++++++
 Nasal/autopush.nas             |  76 +++++++++++++
 Nasal/autopush_driver.nas      | 113 ++++++++++++++++++
 Systems/pushback.xml           |  45 --------
 gui/dialogs/autopush.xml       | 158 ++++++++++++++++++++++++++
 gui/dialogs/pushback.xml       | 201 +++++++++++++++++++++++++++++++++
 18 files changed, 839 insertions(+), 61 deletions(-)
 create mode 100644 Models/Goldhofert-autopush.xml
 create mode 100644 Nasal/autopush-README.txt
 create mode 100644 Nasal/autopush.nas
 create mode 100644 Nasal/autopush_driver.nas
 delete mode 100644 Systems/pushback.xml
 create mode 100644 gui/dialogs/autopush.xml
 create mode 100644 gui/dialogs/pushback.xml

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">
 		</tank>
 	</propulsion>
 
-	<system file="pushback" />
 	<system file="electrical" />
 	<system file="fuel" />
 	<system file="glass-effect1" />
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">
 		</tank>
 	</propulsion>
 
-	<system file="pushback" />
 	<system file="fuel" />
 	<system file="electrical" />
 	<system file="glass-effect1" />
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">
 		</tank>
 	</propulsion>
 
-	<system file="pushback" />
 	<system file="electrical" />
 	<system file="fuel" />
 	<system file="glass-effect1" />
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 @@
 
 		<model>
 			<pushback>
-				<kp type="double">5000</kp>
-				<ki type="double">200</ki>
-				<kd type="double">500</kd>
-				<position-norm type="double">0</position-norm>
+				<enabled type="int"/>
+				<available type="int">1</available>
+				<yaw-deg alias="/fdm/jsbsim/fcs/steer-deg"/>
+				<target-speed-km_h type="float">0.0</target-speed-km_h>
+				<K_p type="float">5000.0</K_p>
+				<K_i type="float">200.0</K_i>
+				<F_i type="float">25000.0</F_i>
+				<K_d type="float">500.0</K_d>
+				<driver>
+					<tow-distance-m type="float">-60.0</tow-distance-m>
+					<face-heading-deg type="float">0.0</face-heading-deg>
+					<steer-cmd-norm alias="/controls/flight/rudder"/>
+					<K_V_push type="float">5.0</K_V_push>
+					<F_V_push type="float">5.0</F_V_push>
+					<S_min-m type="float">5.0</S_min-m>
+					<K_psi_push type="float">0.05</K_psi_push>
+					<K_V_turn type="float">1.4</K_V_turn>
+					<F_V_turn type="float">7.0</F_V_turn>
+					<K_psi_turn type="float">0.2</K_psi_turn>
+					<V_min type="float">2.5</V_min>
+				</driver>
 			</pushback>
 			<icing>
 				<iceable>
@@ -231,7 +248,7 @@
 						<label>Pushback</label>
 						<binding>
 							<command>dialog-show</command>
-							<dialog-name>pushback</dialog-name>
+							<dialog-name>autopush</dialog-name>
 						</binding>
 					</item>
 					<item>
@@ -1455,6 +1472,22 @@
 		<icing>
 			<file>Aircraft/IDG-A32X/Nasal/icing.nas</file>
 		</icing>
+		<autopush>
+			<file>Nasal/autopush.nas</file>
+		</autopush>
+		<autopush_driver>
+			<file>Nasal/autopush_driver.nas</file>
+		</autopush_driver>
 	</nasal>
-
+	<fdm>
+		<jsbsim>
+			<external_reactions>
+		        	<pushback>
+		        		<magnitude alias="/sim/model/pushback/force-lbf"/>
+			        	<x alias="/sim/model/pushback/force-x"/>
+			        	<y alias="/sim/model/pushback/force-y"/>
+			        </pushback>
+			</external_reactions>
+		</jsbsim>
+	</fdm>
 </PropertyList>
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">
 		</tank>
 	</propulsion>
 
-	<system file="pushback" />
 	<system file="electrical" />
 	<system file="fuel" />
 	<system file="glass-effect1" />
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">
 		</tank>
 	</propulsion>
 
-	<system file="pushback" />
 	<system file="electrical" />
 	<system file="fuel" />
 	<system file="glass-effect1" />
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 @@
 	<!-- Other non-aircraft models -->
 	<model>
 		<name>Pushback</name>
-		<path>Models/Airport/Pushback/Goldhofert.xml</path>
+		<path>Models/Goldhofert-autopush.xml</path>
 		<offsets>
 			<x-m>5.55</x-m>
 			<y-m>0</y-m>
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 @@
 	<!-- Other non-aircraft models -->
 	<model>
 		<name>Pushback</name>
-		<path>Models/Airport/Pushback/Goldhofert.xml</path>
+		<path>Models/Goldhofert-autopush.xml</path>
 		<offsets>
 			<x-m>5.55</x-m>
 			<y-m>0</y-m>
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 @@
 	<!-- Other non-aircraft models -->
 	<model>
 		<name>Pushback</name>
-		<path>Models/Airport/Pushback/Goldhofert.xml</path>
+		<path>Models/Goldhofert-autopush.xml</path>
 		<offsets>
 			<x-m>5.55</x-m>
 			<y-m>0</y-m>
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 @@
 	<!-- Other non-aircraft models -->
 	<model>
 		<name>Pushback</name>
-		<path>Models/Airport/Pushback/Goldhofert.xml</path>
+		<path>Models/Goldhofert-autopush.xml</path>
 		<offsets>
 			<x-m>5.55</x-m>
 			<y-m>0</y-m>
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 @@
 	<!-- Other non-aircraft models -->
 	<model>
 		<name>Pushback</name>
-		<path>Models/Airport/Pushback/Goldhofert.xml</path>
+		<path>Models/Goldhofert-autopush.xml</path>
 		<offsets>
 			<x-m>5.55</x-m>
 			<y-m>0</y-m>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+AUTOPUSH
+
+Copyright (c) 2018 Michael Danilov <mike.d.ft402 -eh- gmail.com>
+Original code (c) FlightGear
+Distribute under the terms of GPLv2.
+
+-->
+<PropertyList>
+  <path>Models/Airport/Pushback/Goldhofert.ac</path>
+
+  <!-- Trucks models and artworks courtesy of XPGoodWay Team
+(http://www.xpgoodwayteam.org/site_xpushback/) -->
+
+  <model>
+    <path>Models/Airport/Pushback/Goldhofert-warning-light.xml</path>
+    <offsets>
+      <x-m>-2.908</x-m>
+      <y-m>0.260</y-m>
+      <z-m>1.326</z-m>
+      <heading-deg>0</heading-deg>
+    </offsets>
+  </model>
+
+  <model>
+    <path>Models/Airport/Pushback/Goldhofert-warning-light.xml</path>
+    <offsets>
+      <x-m>-1.781</x-m>
+      <y-m>0.260</y-m>
+      <z-m>1.326</z-m>
+      <heading-deg>90</heading-deg>
+    </offsets>
+  </model>
+
+ <animation>
+   <type>spin</type>
+   <object-name>wheelfl</object-name>
+   <object-name>wheelfr</object-name>
+   <!-- SETTING The aircraft's front wheel rollspeed in m/s. -->
+   <property>gear/gear[0]/rollspeed-ms</property>
+   <factor>-16.3</factor>
+   <center>
+     <x-m>-2.8976</x-m>
+     <y-m>0</y-m>
+     <z-m>0.5809</z-m>
+   </center>
+   <axis>
+     <x>0</x>
+     <y>1</y>
+     <z>0</z>
+   </axis>
+ </animation>
+
+ <animation>
+   <type>spin</type>
+   <object-name>wheelrl</object-name>
+   <object-name>wheelrr</object-name>
+   <!-- SETTING The aircraft's front wheel rollspeed in m/s. -->
+   <property>gear/gear[0]/rollspeed-ms</property>
+   <factor>-16.3</factor>
+   <center>
+     <x-m>2.0501</x-m>
+     <y-m>0</y-m>
+     <z-m>0.5735</z-m>
+   </center>
+   <axis>
+     <x>0</x>
+     <y>1</y>
+     <z>0</z>
+   </axis>
+ </animation>
+
+ <animation>
+   <type>select</type>
+   <condition>
+     <!-- SETTING Whether connected. -->
+     <property>sim/model/pushback/connected</property>
+   </condition>
+ </animation>
+
+ <animation>
+   <type>rotate</type>
+   <!-- SETTING Normalized steering. -->
+   <property>gear/gear/steering-norm</property>
+   <!-- SETTING Max steering. -->
+   <factor>70.0</factor>
+   <center>
+     <x-m>0</x-m>
+     <y-m>0</y-m>
+     <z-m>0</z-m>
+   </center>
+   <axis>
+     <x>0</x>
+     <y>0</y>
+     <z>-1</z>
+   </axis>
+ </animation>
+
+ <animation>
+   <type>translate</type>
+   <!-- SETTING Gear compression. -->
+   <property>gear/gear[0]/compression-ft</property>
+   <factor>0.3048</factor>
+   <axis>
+     <x>0</x>
+     <y>0</y>
+     <z>1</z>
+   </axis>
+ </animation>
+
+ <animation>
+   <type>rotate</type>
+   <!-- SETTING Aircraft pitch. -->
+   <property>orientation/pitch-deg</property>
+   <!-- SETTING Average ground pitch. -->
+   <offset-deg>2.0</offset-deg>
+   <center>
+     <x-m>0</x-m>
+     <y-m>0</y-m>
+     <z-m>0</z-m>
+   </center>
+   <axis>
+     <x>0</x>
+     <y>1</y>
+     <z>0</z>
+   </axis>
+ </animation>
+</PropertyList>
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 <mike.d.ft402 -eh- gmail.com>
+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 <sim><model>:
+
+ <pushback>
+  <enabled type="int"/>
+  <available type="int">1</available>
+  <yaw-deg alias="/fdm/jsbsim/gear/unit[0]/steering/pos-deg"/>
+  <target-speed-km_h type="float">0.0</target-speed-km_h>
+  <K_p type="float">100.0</K_p>
+  <K_i type="float">25.0</K_i>
+  <F_i type="float">25000.0</F_i>
+  <K_d type="float">0.0</K_d>
+  <driver>
+   <tow-distance-m type="float">-60.0</tow-distance-m>
+   <face-heading-deg type="float">0.0</face-heading-deg>
+   <steer-cmd-norm alias="/controls/flight/rudder"/>
+   <K_V_push type="float">10.0</K_V_push>
+   <F_V_push type="float">10.0</F_V_push>
+   <S_min-m type="float">5.0</S_min-m>
+   <K_psi_push type="float">0.05</K_psi_push>
+   <K_V_turn type="float">1.4</K_V_turn>
+   <F_V_turn type="float">7.0</F_V_turn>
+   <K_psi_turn type="float">0.2</K_psi_turn>
+   <V_min type="float">2.5</V_min>
+  </driver>
+ </pushback>
+
+   Under <nasal>:
+
+ <autopush>
+  <file>Nasal/autopush.nas</file>
+ </autopush>
+ <autopush_driver>
+  <file>Nasal/autopush_driver.nas</file>
+ </autopush_driver>
+
+2. Connect the FDM's external force.
+
+ a) JSBSim:
+
+  a.1. Add the following under <external_reactions> of your
+       JSBSim XML. Change the coordinates under <location> to equal
+       those of your front bogey.
+
+ <force name="tractor" frame="BODY">
+  <location unit="M">
+   <x>0.0</x>
+   <y>0.0</y>
+   <z>0.0</z>
+  </location>
+  <direction>
+   <x>1.0</x>
+   <y>0.0</y>
+   <z>0.0</z>
+  </direction>
+ </force>
+
+  a.2. Add the following under <fdm><jsbsim><external_reactions> of
+       your set.xml.
+
+   <tractor>
+    <magnitude alias="/sim/model/pushback/force-lbf"/>
+    <x alias="/sim/model/pushback/force-x"/>
+    <y alias="/sim/model/pushback/force-y"/>
+   </tractor>
+
+ 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
+   <offsets> to equal those of your front bogey.
+
+ <model>
+  <name>Pushback</name>
+  <path>Models/Airport/Pushback/Goldhofert-autopush.xml</path>
+  <offsets>
+   <x-m>0.0</x-m>
+   <y-m>0.0</y-m>
+   <z-m>0.0</z-m>
+  </offsets>
+ </model>
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 <mike.d.ft402 -eh- gmail.com>
+# 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 <mike.d.ft402 -eh- gmail.com>
+# 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 @@
-<?xml version='1.0' encoding='UTF-8' ?>
-
-<!--
-##############################################
-# Copyright (c) Joshua Davidson (it0uchpods) #
-##############################################
--->
-
-<system name="pushback">
- <property>/sim/model/pushback/target-speed-fps</property>
- <channel name="Pushback">
-
-   <switch name="systems/pushback/linked">
-     <default value="-1"/>
-     <test value="0">
-       /sim/model/pushback/position-norm gt 0.95
-       /gear/gear/wow == 1
-       gear/unit[0]/wheel-speed-fps lt 50
-     </test>
-   </switch>
-
-   <summer name="systems/pushback/speed-error">
-     <input>/sim/model/pushback/target-speed-fps</input>
-     <input>-gear/unit[0]/wheel-speed-fps</input>
-   </summer>
-
-   <pid name="systems/pushback/force">
-     <input>systems/pushback/speed-error</input>
-     <kp>/sim/model/pushback/kp</kp>
-     <ki>/sim/model/pushback/ki</ki>
-     <kd>/sim/model/pushback/kd</kd>
-     <trigger>systems/pushback/linked</trigger>
-     <output>/sim/model/pushback/force</output>
-   </pid>
-
-   <switch name="systems/pushback/force-output">
-     <default value="0"/>
-     <test value="systems/pushback/force">
-       systems/pushback/linked == 0
-     </test>
-     <output>external_reactions/pushback/magnitude</output>
-   </switch>
-
- </channel>
-</system>
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 @@
+<?xml version="1.0"?>
+<!--
+
+AUTOPUSH
+Pushback dialog.
+
+Copyright (c) 2018 Michael Danilov <mike.d.ft402 -eh- gmail.com>
+Original code (c) FlightGear
+Distribute under the terms of GPLv2.
+
+-->
+<PropertyList>
+
+ <name>autopush</name>
+ <layout>vbox</layout>
+
+ <group>
+  <layout>hbox</layout>
+  <text>
+   <label>Pushback</label>
+  </text>
+  <empty>
+   <stretch>true</stretch>
+  </empty>
+  <button>
+   <legend/>
+   <key>Esc</key>
+   <pref-width>16</pref-width>
+   <pref-height>16</pref-height>
+   <border>2</border>
+   <binding>
+    <command>dialog-close</command>
+   </binding>
+  </button>
+ </group>
+
+ <hrule/>
+
+ <group>
+  <stretch>true</stretch>
+  <layout>vbox</layout>
+  <halign>center</halign>
+  <valign>top</valign>
+
+  <checkbox>
+   <halign>left</halign>
+   <label>Connect</label>
+   <property>/sim/model/pushback/enabled</property>
+   <binding><command>dialog-apply</command></binding>
+   <binding>
+    <command>nasal</command>
+    <script>autopush.toggle();</script>
+   </binding>
+  </checkbox>
+
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Speed:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>-15</min>
+    <max>15</max>
+    <property>/sim/model/pushback/target-speed-km_h</property>
+    <live>true</live>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/sim/model/pushback/target-speed-km_h</property>
+    <format>%3.0f</format>
+    <live>true</live>
+   </text>
+   <text>
+    <label> km/h</label>
+   </text>
+  </group>
+
+  <hrule/>
+
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Distance:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>-100</min>
+    <max>100</max>
+    <property>/sim/model/pushback/driver/tow-distance-m</property>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/sim/model/pushback/driver/tow-distance-m</property>
+    <format>%4.0f</format>
+    <live>true</live>
+   </text>
+   <text>
+    <label> m</label>
+   </text>
+  </group>
+
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Facing:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>0</min>
+    <max>360</max>
+    <property>/sim/model/pushback/driver/face-heading-deg</property>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/sim/model/pushback/driver/face-heading-deg</property>
+    <format>%3.0f</format>
+    <live>true</live>
+   </text>
+  </group>
+
+  <group>
+   <layout>hbox</layout>
+   <button>
+    <legend>Tow / push back</legend>
+    <binding>
+     <command>nasal</command>
+     <script>
+      autopush_driver.start();
+     </script>
+    </binding>
+   </button>
+   <button>
+    <legend>Stop</legend>
+    <binding>
+     <command>nasal</command>
+     <script>
+      autopush_driver.stop();
+     </script>
+    </binding>
+   </button>
+  </group>
+
+  </group>
+
+</PropertyList>
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 @@
+<?xml version="1.0"?>
+<PropertyList>
+
+ <name>pushback</name>
+ <layout>vbox</layout>
+
+
+<!-- Uncomment for default FG pushback.
+-->
+ <nasal>
+  <open>
+   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  );
+  </open>
+  <close>
+   pushback_position.setpos(0);
+   setprop("/sim/model/pushback/enabled", 0 );
+   setprop("/sim/model/pushback/target-speed-fps", 0 );
+   setprop("/sim/model/pushback/force", 0);
+  </close>
+ </nasal>
+
+ <group>
+  <layout>hbox</layout>
+  <text>
+   <label>Pushback</label>
+  </text>
+  <empty>
+   <stretch>true</stretch>
+  </empty>
+  <button>
+   <legend/>
+   <key>Esc</key>
+   <pref-width>16</pref-width>
+   <pref-height>16</pref-height>
+   <border>2</border>
+   <binding>
+    <command>dialog-close</command>
+   </binding>
+  </button>
+ </group>
+
+ <hrule/>
+
+ <group>
+  <stretch>true</stretch>
+  <layout>vbox</layout>
+  <halign>center</halign>
+  <valign>top</valign>
+
+  <checkbox>
+   <halign>left</halign>
+   <label>Connect</label>
+<!-- Uncomment for Tu-144 pushback.
+   <property>/sim/model/pushback/enabled</property>
+   <binding><command>dialog-apply</command></binding>
+-->
+<!-- Uncomment for default FG pushback.
+-->
+   <binding>
+    <command>nasal</command>
+    <script>pushback_position.toggle();</script>
+   </binding>
+  </checkbox>
+
+<!-- Uncomment for Tu-144 pushback.
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Speed:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>-15</min>
+    <max>15</max>
+    <property>/sim/model/pushback/target-speed-km_h</property>
+    <live>true</live>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/sim/model/pushback/target-speed-km_h</property>
+    <format>%3.0f</format>
+    <live>true</live>
+   </text>
+   <text>
+    <label> km/h</label>
+   </text>
+  </group>
+-->
+
+<!-- Uncomment for default FG pushback.
+-->
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Speed:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>-25</min>
+    <max>25</max>
+    <property>/sim/model/pushback/target-speed-fps</property>
+    <live>true</live>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/sim/model/pushback/target-speed-fps</property>
+    <format>%3.0f</format>
+    <live>true</live>
+   </text>
+   <text>
+    <label> fps</label>
+   </text>
+  </group>
+
+  <hrule/>
+
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Distance:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>-100</min>
+    <max>100</max>
+    <property>/nasal/pushback_driver/tow-distance-m</property>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/nasal/pushback_driver/tow-distance-m</property>
+    <format>%4.0f</format>
+    <live>true</live>
+   </text>
+   <text>
+    <label> m</label>
+   </text>
+  </group>
+
+  <group>
+   <layout>hbox</layout>
+   <text>
+    <label>Facing:</label>
+   </text>
+   <slider>
+    <row>0</row>
+    <col>2</col>
+    <min>0</min>
+    <max>360</max>
+    <property>/nasal/pushback_driver/face-heading-deg</property>
+    <binding>
+     <command>dialog-apply</command>
+    </binding>
+   </slider>
+   <text>
+    <pref-width>16</pref-width>
+    <property>/nasal/pushback_driver/face-heading-deg</property>
+    <format>%3.0f</format>
+    <live>true</live>
+   </text>
+  </group>
+
+  <group>
+   <layout>hbox</layout>
+   <button>
+    <legend>Tow / push back</legend>
+    <binding>
+     <command>nasal</command>
+     <script>
+      pushback_driver.start();
+     </script>
+    </binding>
+   </button>
+   <button>
+    <legend>Stop</legend>
+    <binding>
+     <command>nasal</command>
+     <script>
+      pushback_driver.stop();
+     </script>
+    </binding>
+   </button>
+  </group>
+
+  </group>
+
+</PropertyList>

From d21d700b0d5ad2c80eca8343257e73569843a284 Mon Sep 17 00:00:00 2001
From: Joshua Davidson <joshuadavidson2000@gmail.com>
Date: Wed, 4 Apr 2018 15:15:15 -0400
Subject: [PATCH 2/2] Control: Tweak new pushback

---
 A320-main.xml                            |  10 +-
 Nasal/autopush.nas                       |  94 +++---
 Nasal/autopush_driver.nas                | 142 +++++----
 {Nasal => Resources}/autopush-README.txt |   0
 Systems/autopush.xml                     | 375 +++++++++++++++++++++++
 gui/dialogs/autopush.xml                 | 158 ----------
 gui/dialogs/pushback.xml                 | 201 ------------
 7 files changed, 497 insertions(+), 483 deletions(-)
 rename {Nasal => Resources}/autopush-README.txt (100%)
 create mode 100644 Systems/autopush.xml
 delete mode 100644 gui/dialogs/autopush.xml
 delete mode 100644 gui/dialogs/pushback.xml

diff --git a/A320-main.xml b/A320-main.xml
index 1dc3821a..0420bae3 100644
--- a/A320-main.xml
+++ b/A320-main.xml
@@ -46,8 +46,8 @@
 					<K_psi_push type="float">0.05</K_psi_push>
 					<K_V_turn type="float">1.4</K_V_turn>
 					<F_V_turn type="float">7.0</F_V_turn>
-					<K_psi_turn type="float">0.2</K_psi_turn>
-					<V_min type="float">2.5</V_min>
+					<K_psi_turn type="float">0.1</K_psi_turn>
+					<V_min type="float">1.5</V_min>
 				</driver>
 			</pushback>
 			<icing>
@@ -247,8 +247,10 @@
 					<item>
 						<label>Pushback</label>
 						<binding>
-							<command>dialog-show</command>
-							<dialog-name>autopush</dialog-name>
+							<command>nasal</command>
+							<script>
+							autopush.push_dlg.open();
+							</script>
 						</binding>
 					</item>
 					<item>
diff --git a/Nasal/autopush.nas b/Nasal/autopush.nas
index f4feb0b2..b5ea5059 100644
--- a/Nasal/autopush.nas
+++ b/Nasal/autopush.nas
@@ -4,6 +4,7 @@
 # Copyright (c) 2018 Michael Danilov <mike.d.ft402 -eh- gmail.com>
 # Distribute under the terms of GPLv2.
 
+var push_dlg = gui.Dialog.new("sim/gui/dialogs/autopush/push/dialog", "Aircraft/IDG-A32X/Systems/autopush.xml");
 
 var _K_p = nil;
 var _K_i = nil;
@@ -14,63 +15,60 @@ 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);
+	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();
+	_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);
+	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();
- }
+	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
index 76126ade..5845df65 100644
--- a/Nasal/autopush_driver.nas
+++ b/Nasal/autopush_driver.nas
@@ -26,88 +26,86 @@ 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);
+	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-magnetic-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.");
- }
+	_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);
+	if (timer.isRunning) {
+		screen.log.write("(pushback): Done.");
+		setprop("/controls/flight/rudder", 0);
+	}
+	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;
+	while (p <= -180.0) {
+		p += 360.0;
+	}
+	while (p > 180.0) {
+		p -= 360.0;
+	}
+	return p;
 }
diff --git a/Nasal/autopush-README.txt b/Resources/autopush-README.txt
similarity index 100%
rename from Nasal/autopush-README.txt
rename to Resources/autopush-README.txt
diff --git a/Systems/autopush.xml b/Systems/autopush.xml
new file mode 100644
index 00000000..e76df763
--- /dev/null
+++ b/Systems/autopush.xml
@@ -0,0 +1,375 @@
+<?xml version="1.0"?>
+
+<!--
+
+AUTOPUSH
+Pushback dialog.
+
+Copyright (c) 2018 Michael Danilov <mike.d.ft402 -eh- gmail.com>
+Original code (c) FlightGear
+Distribute under the terms of GPLv2.
+
+-->
+
+<PropertyList>
+
+	<name>autopush</name>
+	<layout>vbox</layout>
+
+	<group>
+		<layout>hbox</layout>
+		
+		<text>
+			<label>Pushback</label>
+		</text>
+		
+		<empty>
+			<stretch>true</stretch>
+		</empty>
+		
+        <button>
+            <halign>right</halign>
+            <pref-width>20</pref-width>
+            <pref-height>20</pref-height>
+            <legend>X</legend>
+            <key>Esc</key>
+            <binding>
+                <command>dialog-close</command>
+            </binding>
+        </button>
+	</group>
+
+	<hrule/>
+
+	<group>
+		<stretch>true</stretch>
+		<layout>vbox</layout>
+		<halign>center</halign>
+		<valign>top</valign>
+		
+		<checkbox>
+			<halign>left</halign>
+			<label>Connect</label>
+			<property>/sim/model/pushback/enabled</property>
+			<binding>
+				<command>dialog-apply</command>
+			</binding>
+			<binding>
+				<command>nasal</command>
+				<script>autopush.toggle();</script>
+			</binding>
+		</checkbox>
+		
+		<hrule/>
+
+		<group>
+			<layout>table</layout>
+			
+			<text>
+				<row>0</row>
+				<col>0</col>
+				<label>Speed:   </label>
+			</text>
+			
+			<button>
+				<row>0</row>
+				<col>1</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&lt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/target-speed-km_h</property>
+					<min>-15</min>
+					<max>15</max>
+					<step>-1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<slider>
+				<row>0</row>
+				<col>2</col>
+				<min>-15</min>
+				<max>15</max>
+				<property>/sim/model/pushback/target-speed-km_h</property>
+				<live>true</live>
+				<binding>
+					<command>dialog-apply</command>
+				</binding>
+			</slider>
+			
+			<button>
+				<row>0</row>
+				<col>3</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&gt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/target-speed-km_h</property>
+					<min>-15</min>
+					<max>15</max>
+					<step>1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<button>
+				<row>0</row>
+				<col>4</col>
+				<pref-width>50</pref-width>
+				<pref-height>25</pref-height>
+				<legend>Stop</legend>
+				<binding>
+					<command>property-assign</command>
+					<property>/sim/model/pushback/target-speed-km_h</property>
+					<value>0</value>
+				</binding>
+			</button>
+			
+			<text>
+				<row>0</row>
+				<col>5</col>
+				<pref-width>16</pref-width>
+				<property>/sim/model/pushback/target-speed-km_h</property>
+				<format>%3.0f</format>
+				<live>true</live>
+			</text>
+			
+			<text>
+				<row>0</row>
+				<col>6</col>
+				<label>km/h</label>
+			</text>
+		</group>
+
+		<hrule/>
+
+		<group>
+			<layout>table</layout>
+			
+			<text>
+				<row>0</row>
+				<col>0</col>
+				<halign>left</halign>
+				<label>Distance:</label>
+			</text>
+			
+			<button>
+				<row>0</row>
+				<col>1</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&lt;&lt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/tow-distance-m</property>
+					<min>-100</min>
+					<max>100</max>
+					<step>-10</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<button>
+				<row>0</row>
+				<col>2</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&lt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/tow-distance-m</property>
+					<min>-100</min>
+					<max>100</max>
+					<step>-1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<slider>
+				<row>0</row>
+				<col>3</col>
+				<min>-100</min>
+				<max>100</max>
+				<property>/sim/model/pushback/driver/tow-distance-m</property>
+				<live>true</live>
+				<binding>
+					<command>dialog-apply</command>
+				</binding>
+			</slider>
+			
+			<button>
+				<row>0</row>
+				<col>4</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&gt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/tow-distance-m</property>
+					<min>-100</min>
+					<max>100</max>
+					<step>1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<button>
+				<row>0</row>
+				<col>5</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&gt;&gt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/tow-distance-m</property>
+					<min>-100</min>
+					<max>100</max>
+					<step>10</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<text>
+				<row>0</row>
+				<col>6</col>
+				<pref-width>16</pref-width>
+				<property>/sim/model/pushback/driver/tow-distance-m</property>
+				<format>%4.0f</format>
+				<live>true</live>
+			</text>
+			
+			<text>
+				<row>0</row>
+				<col>7</col>
+				<label>m    </label>
+			</text>
+		</group>
+
+		<group>
+			<layout>table</layout>
+			
+			<text>
+				<row>0</row>
+				<col>0</col>
+				<halign>left</halign>
+				<label>Facing:  </label>
+			</text>
+			
+			<button>
+				<row>0</row>
+				<col>1</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&lt;&lt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/face-heading-deg</property>
+					<min>0</min>
+					<max>360</max>
+					<step>-10</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<button>
+				<row>0</row>
+				<col>2</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&lt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/face-heading-deg</property>
+					<min>0</min>
+					<max>360</max>
+					<step>-1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<slider>
+				<row>0</row>
+				<col>3</col>
+				<min>0</min>
+				<max>360</max>
+				<property>/sim/model/pushback/driver/face-heading-deg</property>
+				<live>true</live>
+				<binding>
+					<command>dialog-apply</command>
+				</binding>
+			</slider>
+			
+			<button>
+				<row>0</row>
+				<col>4</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&gt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/face-heading-deg</property>
+					<min>0</min>
+					<max>360</max>
+					<step>1</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<button>
+				<row>0</row>
+				<col>5</col>
+				<pref-width>25</pref-width>
+				<pref-height>25</pref-height>
+				<legend>&gt;&gt;</legend>
+				<binding>
+					<command>property-adjust</command>
+					<property>/sim/model/pushback/driver/face-heading-deg</property>
+					<min>0</min>
+					<max>360</max>
+					<step>10</step>
+					<wrap>false</wrap>
+				</binding>
+			</button>
+			
+			<text>
+				<row>0</row>
+				<col>6</col>
+				<pref-width>16</pref-width>
+				<property>/sim/model/pushback/driver/face-heading-deg</property>
+				<format>%3.0f</format>
+				<live>true</live>
+			</text>
+			
+			<text>
+				<row>0</row>
+				<col>7</col>
+				<label>     </label>
+			</text>
+		</group>
+
+		<group>
+			<layout>hbox</layout>
+			
+			<button>
+				<legend>Start</legend>
+				<binding>
+					<command>nasal</command>
+					<script>autopush_driver.start();</script>
+				</binding>
+			</button>
+			
+			<button>
+				<legend>Stop</legend>
+				<binding>
+					<command>nasal</command>
+					<script>autopush_driver.stop();</script>
+				</binding>
+			</button>
+		</group>
+
+	</group>
+
+</PropertyList>
diff --git a/gui/dialogs/autopush.xml b/gui/dialogs/autopush.xml
deleted file mode 100644
index fa7371a4..00000000
--- a/gui/dialogs/autopush.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0"?>
-<!--
-
-AUTOPUSH
-Pushback dialog.
-
-Copyright (c) 2018 Michael Danilov <mike.d.ft402 -eh- gmail.com>
-Original code (c) FlightGear
-Distribute under the terms of GPLv2.
-
--->
-<PropertyList>
-
- <name>autopush</name>
- <layout>vbox</layout>
-
- <group>
-  <layout>hbox</layout>
-  <text>
-   <label>Pushback</label>
-  </text>
-  <empty>
-   <stretch>true</stretch>
-  </empty>
-  <button>
-   <legend/>
-   <key>Esc</key>
-   <pref-width>16</pref-width>
-   <pref-height>16</pref-height>
-   <border>2</border>
-   <binding>
-    <command>dialog-close</command>
-   </binding>
-  </button>
- </group>
-
- <hrule/>
-
- <group>
-  <stretch>true</stretch>
-  <layout>vbox</layout>
-  <halign>center</halign>
-  <valign>top</valign>
-
-  <checkbox>
-   <halign>left</halign>
-   <label>Connect</label>
-   <property>/sim/model/pushback/enabled</property>
-   <binding><command>dialog-apply</command></binding>
-   <binding>
-    <command>nasal</command>
-    <script>autopush.toggle();</script>
-   </binding>
-  </checkbox>
-
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Speed:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>-15</min>
-    <max>15</max>
-    <property>/sim/model/pushback/target-speed-km_h</property>
-    <live>true</live>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/sim/model/pushback/target-speed-km_h</property>
-    <format>%3.0f</format>
-    <live>true</live>
-   </text>
-   <text>
-    <label> km/h</label>
-   </text>
-  </group>
-
-  <hrule/>
-
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Distance:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>-100</min>
-    <max>100</max>
-    <property>/sim/model/pushback/driver/tow-distance-m</property>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/sim/model/pushback/driver/tow-distance-m</property>
-    <format>%4.0f</format>
-    <live>true</live>
-   </text>
-   <text>
-    <label> m</label>
-   </text>
-  </group>
-
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Facing:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>0</min>
-    <max>360</max>
-    <property>/sim/model/pushback/driver/face-heading-deg</property>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/sim/model/pushback/driver/face-heading-deg</property>
-    <format>%3.0f</format>
-    <live>true</live>
-   </text>
-  </group>
-
-  <group>
-   <layout>hbox</layout>
-   <button>
-    <legend>Tow / push back</legend>
-    <binding>
-     <command>nasal</command>
-     <script>
-      autopush_driver.start();
-     </script>
-    </binding>
-   </button>
-   <button>
-    <legend>Stop</legend>
-    <binding>
-     <command>nasal</command>
-     <script>
-      autopush_driver.stop();
-     </script>
-    </binding>
-   </button>
-  </group>
-
-  </group>
-
-</PropertyList>
diff --git a/gui/dialogs/pushback.xml b/gui/dialogs/pushback.xml
deleted file mode 100644
index 8c83c721..00000000
--- a/gui/dialogs/pushback.xml
+++ /dev/null
@@ -1,201 +0,0 @@
-<?xml version="1.0"?>
-<PropertyList>
-
- <name>pushback</name>
- <layout>vbox</layout>
-
-
-<!-- Uncomment for default FG pushback.
--->
- <nasal>
-  <open>
-   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  );
-  </open>
-  <close>
-   pushback_position.setpos(0);
-   setprop("/sim/model/pushback/enabled", 0 );
-   setprop("/sim/model/pushback/target-speed-fps", 0 );
-   setprop("/sim/model/pushback/force", 0);
-  </close>
- </nasal>
-
- <group>
-  <layout>hbox</layout>
-  <text>
-   <label>Pushback</label>
-  </text>
-  <empty>
-   <stretch>true</stretch>
-  </empty>
-  <button>
-   <legend/>
-   <key>Esc</key>
-   <pref-width>16</pref-width>
-   <pref-height>16</pref-height>
-   <border>2</border>
-   <binding>
-    <command>dialog-close</command>
-   </binding>
-  </button>
- </group>
-
- <hrule/>
-
- <group>
-  <stretch>true</stretch>
-  <layout>vbox</layout>
-  <halign>center</halign>
-  <valign>top</valign>
-
-  <checkbox>
-   <halign>left</halign>
-   <label>Connect</label>
-<!-- Uncomment for Tu-144 pushback.
-   <property>/sim/model/pushback/enabled</property>
-   <binding><command>dialog-apply</command></binding>
--->
-<!-- Uncomment for default FG pushback.
--->
-   <binding>
-    <command>nasal</command>
-    <script>pushback_position.toggle();</script>
-   </binding>
-  </checkbox>
-
-<!-- Uncomment for Tu-144 pushback.
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Speed:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>-15</min>
-    <max>15</max>
-    <property>/sim/model/pushback/target-speed-km_h</property>
-    <live>true</live>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/sim/model/pushback/target-speed-km_h</property>
-    <format>%3.0f</format>
-    <live>true</live>
-   </text>
-   <text>
-    <label> km/h</label>
-   </text>
-  </group>
--->
-
-<!-- Uncomment for default FG pushback.
--->
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Speed:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>-25</min>
-    <max>25</max>
-    <property>/sim/model/pushback/target-speed-fps</property>
-    <live>true</live>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/sim/model/pushback/target-speed-fps</property>
-    <format>%3.0f</format>
-    <live>true</live>
-   </text>
-   <text>
-    <label> fps</label>
-   </text>
-  </group>
-
-  <hrule/>
-
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Distance:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>-100</min>
-    <max>100</max>
-    <property>/nasal/pushback_driver/tow-distance-m</property>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/nasal/pushback_driver/tow-distance-m</property>
-    <format>%4.0f</format>
-    <live>true</live>
-   </text>
-   <text>
-    <label> m</label>
-   </text>
-  </group>
-
-  <group>
-   <layout>hbox</layout>
-   <text>
-    <label>Facing:</label>
-   </text>
-   <slider>
-    <row>0</row>
-    <col>2</col>
-    <min>0</min>
-    <max>360</max>
-    <property>/nasal/pushback_driver/face-heading-deg</property>
-    <binding>
-     <command>dialog-apply</command>
-    </binding>
-   </slider>
-   <text>
-    <pref-width>16</pref-width>
-    <property>/nasal/pushback_driver/face-heading-deg</property>
-    <format>%3.0f</format>
-    <live>true</live>
-   </text>
-  </group>
-
-  <group>
-   <layout>hbox</layout>
-   <button>
-    <legend>Tow / push back</legend>
-    <binding>
-     <command>nasal</command>
-     <script>
-      pushback_driver.start();
-     </script>
-    </binding>
-   </button>
-   <button>
-    <legend>Stop</legend>
-    <binding>
-     <command>nasal</command>
-     <script>
-      pushback_driver.stop();
-     </script>
-    </binding>
-   </button>
-  </group>
-
-  </group>
-
-</PropertyList>