diff --git a/Docs/Goldhofert.xml b/Docs/Goldhofert.xml
new file mode 100644
index 000000000..06d2c8e77
--- /dev/null
+++ b/Docs/Goldhofert.xml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]>
+
+
+
+
+
+
+ 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
+ &ROLLSPEED-PROP;
+ -16.3
+
+ -2.8976
+ 0
+ 0.5809
+
+
+ 0
+ 1
+ 0
+
+
+
+
+ spin
+ wheelrl
+ wheelrr
+ &ROLLSPEED-PROP;
+ -16.3
+
+ 2.0501
+ 0
+ 0.5735
+
+
+ 0
+ 1
+ 0
+
+
+
+
+ select
+
+ &CONNECTED-PROP;
+
+
+
+
+ rotate
+ &YAW-PROP;
+ &YAW-MULT;
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+ translate
+ &COMPRESSION-PROP;
+ 0.3048
+
+ 0
+ 0
+ 1
+
+
+
+
+ rotate
+ &PITCH-PROP;
+ &PITCH-FACTOR;
+ &PITCH-OFFSET;
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 1
+ 0
+
+
+
diff --git a/Docs/README.autopush b/Docs/README.autopush
new file mode 100644
index 000000000..d599572f9
--- /dev/null
+++ b/Docs/README.autopush
@@ -0,0 +1,255 @@
+AUTOMATIC PUSHBACK FOR FLIGHTGEAR
+http://gitlab.com/mdanil/flightgear-autopush
+
+Version 2.0.1
+
+Copyright (c) 2018 Autopush authors:
+ Michael Danilov
+ Joshua Davidson http://github.com/Octal450
+ Merspieler http://gitlab.com/merspieler
+Some of the code (c) FlightGear
+Distribute under the terms of GPLv2.
+
+
+This project aims to develop a generic pushback for JSBSim and YASim
+aircraft, with the following characteristics.
+
+1. Do the pushback procedure automatically.
+2. Scale to different aircraft with minimum changes to their logic.
+3. Use no computer resources in flight.
+
+
+INSTALLATION
+
+Minimum FlightGear version: 2018.0
+
+NOTE: these steps cover the minimal testing setup. To get the most
+realism out of Autopush, you will have to add logic aircraft-side as
+described in the last step.
+
+1. Copy the autopush.config.xml.template into your aircraft directory
+and the Goldhofert.xml into /Models/Autopush/
+
+2. Set up the pushback logic.
+
+Add the following line under of your set.xml
+
+
+
+Rename autopush-config.xml.template to autopush-config.xml and edit
+the file as described below.
+
+Alternatively, you can copy the contents of
+autopush-config.xml.template to under of your
+set.xml and make these modifications there.
+
+Change the aliases to point to the following data:
+
+steer-cmd-norm nose wheel steering command norm
+yaw nose wheel steering output norm or angle (see below)
+chocks chocks command bool (or leave it until step 7)
+available pushback availability bool (or leave it until step 7)
+
+Replace the MULTIPLIER in yaw-mult with:
+
+max steering angle if yaw is norm
+1.0 if yaw is degrees
+57.3 if yaw is radians
+
+Replace PITCH with the pushback's pitch relative to aircraft. If your
+aircraft's ground pitch varies greatly, you can make an alias to
+property with calculated relative pitch.
+
+Replace MIN_RADIUS with the minimum allowed turning radius in m of the
+center of the aircraft during pushback procedure.
+
+NOTE: on some aircraft the turning radius is different for taxi and
+pushback. Please check the aircraft's literature.
+
+Replace STOP_DIST with the stopping distance in m from pushback speed.
+
+Add under of your set.xml:
+
+
+ Nasal/Autopush/autopush.nas
+
+
+ Nasal/Autopush/driver.nas
+
+
+ Nasal/Autopush/dynarr.nas
+
+
+ Nasal/Autopush/route.nas
+
+
+Add under of your set.xml:
+
+ /sim/model/autopush/route/show
+ /sim/model/autopush/route/show-wingtip
+
+3. Connect the force to FDM.
+
+ 3.a. JSBSim:
+
+ 3.a.1. Add the following under of your JSBSim
+ XML.
+
+
+
+ FRONT-X
+ FRONT-Y
+ FRONT-Z
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+Replace FRONT-X, FRONT-Y, FRONT-Z with coordinates of your front
+bogey.
+
+ 3.a.2. Add the following under of
+ your set.xml.
+
+
+
+
+
+
+
+
+ 3.b. YASim. Add the following under of your YASim xml.
+
+
+
+
+
+
+
+
+
+
+
+Replace FRONT-X, FRONT-Y, FRONT-Z with coordinates of your front
+bogey.
+
+4. Add the pushback model to your Model XML:
+
+
+ Pushback
+ MODEL
+
+ FRONT-X
+ FRONT-Y
+ FRONT-Z
+
+
+
+Replace FRONT-X, FRONT-Y, FRONT-Z with coordinates of your front
+bogey. Replace MODEL with one of the following file names:
+
+Autopush/Goldhofert.xml for Goldhofer towbarless pushback
+
+Edit the "SETTINGS" part in the beginning of the Goldhofert.xml to match
+your setup.
+
+5. Add gui/dialogs/autopush.xml to your aircraft's menu (see
+ FlightGear documentation for editing the menu).
+
+6. If you have a bug tracker, please add a note, that autopush related
+ bugs should be reported here:
+
+https://gitlab.com/mdanil/flightgear-autopush/issues
+
+7. Launch the simulator and try Autopush. After making sure it works,
+ complete the support by implementing some logic in your aircraft
+ and connecting the rest of the interface.
+
+CAUTION.
+1. Make sure to HAVE A BACK-UP COPY of your work.
+2. If anything is not clear in the instruction below, do not guess --
+ contact the authors.
+
+ 7.1. When pushback is connected ("/sim/model/autopush/connected"),
+ nose wheel steering must actuate at different speed, depending
+ on its rollspeed. The exact dependency is different per aircraft
+ and used pushback visual, and for most aircraft the only way to
+ find it is trial and error until it "looks right":
+ - the input must be taken from pushback steering command norm
+ (set by autopush-config.xml, default:
+ "/controls/flight/rudder");
+ - the output must be written to pushback orientation in degrees,
+ radians or norm (set by autopush-config.xml, default:
+ "/gear/gear[0]/steering-norm").
+
+ 7.2. Set or alias (in autopush-config.xml) the availability property
+ "/sim/model/autopush/available". It must be false if e.g. the
+ aircraft is moving too fast, the front wheel is not touching
+ the ground, the aircraft is damaged or is outside of an airport
+ etc.
+
+ 7.3. Make Autopush remove wheel chocks by setting the alias in
+ autopush-config.xml to the chocks property. (default:
+ "/controls/gear/wheel-chocks").
+
+ 7.4. Optionally, Autopush may be made visible in multiplayer by
+ duplicating the properties used in its model.xml into some
+ unused MP properties. See the list in
+
+https://sourceforge.net/p/flightgear/flightgear/ci/next/tree/src/MultiPlayer/multiplaymgr.cxx
+
+ around line 215, and then using those properties in the
+ "SETTINGS" part of Autopush model.xml.
+
+ After making these changes check the Autopush model.xml's settings
+ and make sure they match the setup. Launch the simulator and try
+ Autopush.
+
+
+TUNING
+
+There should normally be no need to change the coefficients, because
+the inertia of different aircraft is already taken into account. But
+the gear setup may differ enough to require some tuning.
+
+If these settings are changed during simulation, they take effect
+after disconnecting and reconnecting the pushback.
+
+Pushback (/sim/model/autopush):
+
+Coefficient Unit Description
+
+K_p ((km/h)/s)/(km/h) Proportional coefficient of PID.
+ Defines the start/stop throttle
+ and immediate response to speed
+ difference deltaV = V_set - V.
+
+F_p (km/h)/s Proportional clipping.
+
+K_i ((km/h)/s)/((km/h)*s) Integral coefficient of PID.
+ Defines how fast the steady
+ throttle is ramped during the
+ push.
+
+F_i (km/h)/s Integral clipping.
+
+K_d -((km/h)/s)/((km/h)/s) Differential coefficient of PID.
+ Stabilizes the approaching of
+ steady throttle.
+
+F_d (km/h)/s Differential clipping.
+
+Pushback driver (/sim/model/autopush/driver):
+
+Coefficient Unit Description
+
+F_V km/h Max towing speed in auto mode.
+K_psi 1/deg Amount of steering per heading correction.
+F_psi 1/deg Max steering per heading correction.
+K_deltapsi -1/(deg/s) Amount of steering compensation per heading
+ derivative.
+F_psi 1/(deg/s) Max amount of steering compensation per
+ heading derivative.
diff --git a/Docs/autopush-config.xml.template b/Docs/autopush-config.xml.template
new file mode 100644
index 000000000..f34540c8b
--- /dev/null
+++ b/Docs/autopush-config.xml.template
@@ -0,0 +1,30 @@
+
+
+
+ MULTIPLIER
+ PITCH
+ MIN_RADIUS
+ STOP_DIST
+
+ 1
+
+ false
+ 0.0
+ 0.5
+ 0.15
+ 0.25
+ 0.1
+ 0.0
+ 0.0
+
+ 8.0
+ 0.03
+ 1.0
+ 0.03
+ 1.0
+
+
+
+
+ 0
+
diff --git a/Models/Autopush/cursor.ac b/Models/Autopush/cursor.ac
new file mode 100644
index 000000000..0b3206772
--- /dev/null
+++ b/Models/Autopush/cursor.ac
@@ -0,0 +1,589 @@
+AC3Db
+MATERIAL "autopush cursor" rgb 0.0000 0.0000 0.0000 amb 0.0000 0.0000 0.0000 emis 1.000 0.173 0.545 spec 0.0000 0.0000 0.0000 shi 50 trans 0.0000
+OBJECT world
+name "Blender_exporter_v2.26__cursor.ac"
+kids 1
+OBJECT poly
+name "Circle"
+data 11
+Circle.mesh
+crease 40.0
+numvert 128
+0 0.2 -0.75
+0 0.2 -1
+-0.09802 0.2 -0.99518
+-0.19509 0.2 -0.98079
+-0.29028 0.2 -0.95694
+-0.38268 0.2 -0.92388
+-0.4714 0.2 -0.88192
+-0.55557 0.2 -0.83147
+-0.63439 0.2 -0.77301
+-0.70711 0.2 -0.70711
+-0.77301 0.2 -0.63439
+-0.83147 0.2 -0.55557
+-0.88192 0.2 -0.4714
+-0.92388 0.2 -0.38268
+-0.95694 0.2 -0.29028
+-0.98079 0.2 -0.19509
+-0.99518 0.2 -0.09802
+-1 0.2 0
+-0.99518 0.2 0.09802
+-0.98079 0.2 0.19509
+-0.95694 0.2 0.29028
+-0.92388 0.2 0.38268
+-0.88192 0.2 0.4714
+-0.83147 0.2 0.55557
+-0.77301 0.2 0.63439
+-0.70711 0.2 0.70711
+-0.63439 0.2 0.77301
+-0.55557 0.2 0.83147
+-0.4714 0.2 0.88192
+-0.38268 0.2 0.92388
+-0.29028 0.2 0.95694
+-0.19509 0.2 0.98079
+-0.09802 0.2 0.99518
+0 0.2 1
+0.09802 0.2 0.99518
+0.19509 0.2 0.98079
+0.29029 0.2 0.95694
+0.38268 0.2 0.92388
+0.4714 0.2 0.88192
+0.55557 0.2 0.83147
+0.63439 0.2 0.77301
+0.70711 0.2 0.70711
+0.77301 0.2 0.63439
+0.83147 0.2 0.55557
+0.88192 0.2 0.4714
+0.92388 0.2 0.38268
+0.95694 0.2 0.29028
+0.98079 0.2 0.19509
+0.99518 0.2 0.09802
+1 0.2 0
+0.99518 0.2 -0.09802
+0.98079 0.2 -0.19509
+0.95694 0.2 -0.29028
+0.92388 0.2 -0.38268
+0.88192 0.2 -0.4714
+0.83147 0.2 -0.55557
+0.77301 0.2 -0.63439
+0.70711 0.2 -0.70711
+0.6344 0.2 -0.77301
+0.55557 0.2 -0.83147
+0.4714 0.2 -0.88192
+0.38269 0.2 -0.92388
+0.29029 0.2 -0.95694
+0.19509 0.2 -0.98078
+0.09802 0.2 -0.99518
+-0.07351 0.2 -0.74639
+-0.14632 0.2 -0.73559
+-0.21771 0.2 -0.71771
+-0.28701 0.2 -0.69291
+-0.35355 0.2 -0.66144
+-0.41668 0.2 -0.6236
+-0.47579 0.2 -0.57976
+-0.53033 0.2 -0.53033
+-0.57976 0.2 -0.47579
+-0.6236 0.2 -0.41668
+-0.66144 0.2 -0.35355
+-0.69291 0.2 -0.28701
+-0.71771 0.2 -0.21771
+-0.73559 0.2 -0.14632
+-0.74639 0.2 -0.07351
+-0.75 0.2 0
+-0.74639 0.2 0.07351
+-0.73559 0.2 0.14632
+-0.71771 0.2 0.21771
+-0.69291 0.2 0.28701
+-0.66144 0.2 0.35355
+-0.6236 0.2 0.41668
+-0.57976 0.2 0.4758
+-0.53033 0.2 0.53033
+-0.47579 0.2 0.57976
+-0.41668 0.2 0.6236
+-0.35355 0.2 0.66144
+-0.28701 0.2 0.69291
+-0.21771 0.2 0.71771
+-0.14632 0.2 0.73559
+-0.07351 0.2 0.74639
+0 0.2 0.75
+0.07351 0.2 0.74639
+0.14632 0.2 0.73559
+0.21771 0.2 0.71771
+0.28701 0.2 0.69291
+0.35355 0.2 0.66144
+0.41668 0.2 0.6236
+0.4758 0.2 0.57976
+0.53033 0.2 0.53033
+0.57976 0.2 0.47579
+0.6236 0.2 0.41668
+0.66144 0.2 0.35355
+0.69291 0.2 0.28701
+0.71771 0.2 0.21771
+0.73559 0.2 0.14632
+0.74639 0.2 0.07351
+0.75 0.2 0
+0.74639 0.2 -0.07351
+0.73559 0.2 -0.14632
+0.71771 0.2 -0.21771
+0.69291 0.2 -0.28701
+0.66144 0.2 -0.35355
+0.6236 0.2 -0.41668
+0.57976 0.2 -0.47579
+0.53033 0.2 -0.53033
+0.4758 0.2 -0.57976
+0.41668 0.2 -0.6236
+0.35355 0.2 -0.66144
+0.28701 0.2 -0.69291
+0.21772 0.2 -0.7177
+0.14632 0.2 -0.73559
+0.07352 0.2 -0.74639
+numsurf 64
+SURF 0X10
+mat 0
+refs 4
+25 0.080806 0.323223
+88 0.091854 0.367417
+87 0.088765 0.381051
+24 0.076687 0.341402
+SURF 0X10
+mat 0
+refs 4
+12 0.06988 0.617849
+75 0.08366 0.588387
+74 0.086025 0.604169
+11 0.073033 0.638893
+SURF 0X10
+mat 0
+refs 4
+51 0.186299 0.548772
+114 0.170974 0.536579
+113 0.171649 0.518378
+50 0.187199 0.524504
+SURF 0X10
+mat 0
+refs 4
+64 0.131126 0.748796
+127 0.129595 0.686597
+126 0.134145 0.683897
+63 0.137193 0.745196
+SURF 0X10
+mat 0
+refs 4
+11 0.073033 0.638893
+74 0.086025 0.604169
+73 0.088765 0.618949
+10 0.076687 0.658598
+SURF 0X10
+mat 0
+refs 4
+24 0.076687 0.341402
+87 0.088765 0.381051
+86 0.086025 0.39583
+23 0.073033 0.361107
+SURF 0X10
+mat 0
+refs 4
+37 0.148918 0.26903
+100 0.142938 0.326773
+99 0.138607 0.320574
+36 0.143143 0.260765
+SURF 0X10
+mat 0
+refs 4
+50 0.187199 0.524504
+113 0.171649 0.518378
+112 0.171875 0.5
+49 0.1875 0.5
+SURF 0X10
+mat 0
+refs 4
+63 0.137193 0.745196
+126 0.134145 0.683897
+125 0.138607 0.679426
+62 0.143143 0.739235
+SURF 0X10
+mat 0
+refs 4
+10 0.076687 0.658598
+73 0.088765 0.618949
+72 0.091854 0.632583
+9 0.080806 0.676777
+SURF 0X10
+mat 0
+refs 4
+23 0.073033 0.361107
+86 0.086025 0.39583
+85 0.08366 0.411613
+22 0.06988 0.382151
+SURF 0X10
+mat 0
+refs 4
+36 0.143143 0.260765
+99 0.138607 0.320574
+98 0.134145 0.316103
+35 0.137193 0.254804
+SURF 0X10
+mat 0
+refs 4
+49 0.1875 0.5
+112 0.171875 0.5
+111 0.171649 0.481622
+48 0.187199 0.475496
+SURF 0X10
+mat 0
+refs 4
+62 0.143143 0.739235
+125 0.138607 0.679426
+124 0.142938 0.673227
+61 0.148918 0.73097
+SURF 0X10
+mat 0
+refs 4
+9 0.080806 0.676777
+72 0.091854 0.632583
+71 0.095263 0.644939
+8 0.08535 0.693253
+SURF 0X10
+mat 0
+refs 4
+22 0.06988 0.382151
+85 0.08366 0.411613
+84 0.081693 0.428247
+21 0.067258 0.404329
+SURF 0X10
+mat 0
+refs 4
+35 0.137193 0.254804
+98 0.134145 0.316103
+97 0.129595 0.313403
+34 0.131126 0.251204
+SURF 0X10
+mat 0
+refs 4
+48 0.187199 0.475496
+111 0.171649 0.481622
+110 0.170974 0.463421
+47 0.186299 0.451227
+SURF 0X10
+mat 0
+refs 4
+61 0.148918 0.73097
+124 0.142938 0.673227
+123 0.147097 0.66536
+60 0.154463 0.72048
+SURF 0X10
+mat 0
+refs 4
+8 0.08535 0.693253
+71 0.095263 0.644939
+70 0.098958 0.6559
+7 0.090277 0.707867
+SURF 0X10
+mat 0
+refs 4
+21 0.067258 0.404329
+84 0.081693 0.428247
+83 0.080143 0.445572
+20 0.065191 0.427429
+SURF 0X10
+mat 0
+refs 4
+34 0.131126 0.251204
+97 0.129595 0.313403
+96 0.125 0.3125
+33 0.125 0.25
+SURF 0X10
+mat 0
+refs 4
+47 0.186299 0.451227
+110 0.170974 0.463421
+109 0.169857 0.445572
+46 0.184809 0.427429
+SURF 0X10
+mat 0
+refs 4
+60 0.154463 0.72048
+123 0.147097 0.66536
+122 0.151043 0.6559
+59 0.159723 0.707867
+SURF 0X10
+mat 0
+refs 4
+7 0.090277 0.707867
+70 0.098958 0.6559
+69 0.102903 0.66536
+6 0.095538 0.72048
+SURF 0X10
+mat 0
+refs 4
+20 0.065191 0.427429
+83 0.080143 0.445572
+82 0.079026 0.46342
+19 0.063701 0.451227
+SURF 0X10
+mat 0
+refs 4
+33 0.125 0.25
+96 0.125 0.3125
+95 0.120406 0.313403
+32 0.118874 0.251204
+SURF 0X10
+mat 0
+refs 4
+46 0.184809 0.427429
+109 0.169857 0.445572
+108 0.168307 0.428247
+45 0.182743 0.404329
+SURF 0X10
+mat 0
+refs 4
+59 0.159723 0.707867
+122 0.151043 0.6559
+121 0.154737 0.644939
+58 0.16465 0.693252
+SURF 0X10
+mat 0
+refs 4
+6 0.095538 0.72048
+69 0.102903 0.66536
+68 0.107062 0.673227
+5 0.101082 0.73097
+SURF 0X10
+mat 0
+refs 4
+19 0.063701 0.451227
+82 0.079026 0.46342
+81 0.078351 0.481622
+18 0.062801 0.475496
+SURF 0X10
+mat 0
+refs 4
+32 0.118874 0.251204
+95 0.120406 0.313403
+94 0.115855 0.316103
+31 0.112807 0.254804
+SURF 0X10
+mat 0
+refs 4
+45 0.182743 0.404329
+108 0.168307 0.428247
+107 0.16634 0.411613
+44 0.18012 0.382151
+SURF 0X10
+mat 0
+refs 4
+58 0.16465 0.693252
+121 0.154737 0.644939
+120 0.158146 0.632582
+57 0.169194 0.676776
+SURF 0X10
+mat 0
+refs 4
+5 0.101082 0.73097
+68 0.107062 0.673227
+67 0.111393 0.679426
+4 0.106857 0.739235
+SURF 0X10
+mat 0
+refs 4
+18 0.062801 0.475496
+81 0.078351 0.481622
+80 0.078125 0.5
+17 0.0625 0.5
+SURF 0X10
+mat 0
+refs 4
+31 0.112807 0.254804
+94 0.115855 0.316103
+93 0.111393 0.320574
+30 0.106857 0.260765
+SURF 0X10
+mat 0
+refs 4
+44 0.18012 0.382151
+107 0.16634 0.411613
+106 0.163975 0.395831
+43 0.176967 0.361108
+SURF 0X10
+mat 0
+refs 4
+57 0.169194 0.676776
+120 0.158146 0.632582
+119 0.161235 0.618949
+56 0.173313 0.658598
+SURF 0X10
+mat 0
+refs 4
+4 0.106857 0.739235
+67 0.111393 0.679426
+66 0.115855 0.683897
+3 0.112807 0.745196
+SURF 0X10
+mat 0
+refs 4
+17 0.0625 0.5
+80 0.078125 0.5
+79 0.078351 0.518378
+16 0.062801 0.524504
+SURF 0X10
+mat 0
+refs 4
+30 0.106857 0.260765
+93 0.111393 0.320574
+92 0.107062 0.326772
+29 0.101082 0.26903
+SURF 0X10
+mat 0
+refs 4
+43 0.176967 0.361108
+106 0.163975 0.395831
+105 0.161235 0.381051
+42 0.173313 0.341402
+SURF 0X10
+mat 0
+refs 4
+56 0.173313 0.658598
+119 0.161235 0.618949
+118 0.163975 0.604169
+55 0.176967 0.638892
+SURF 0X10
+mat 0
+refs 4
+3 0.112807 0.745196
+66 0.115855 0.683897
+65 0.120406 0.686597
+2 0.118874 0.748796
+SURF 0X10
+mat 0
+refs 4
+16 0.062801 0.524504
+79 0.078351 0.518378
+78 0.079026 0.536579
+15 0.063701 0.548773
+SURF 0X10
+mat 0
+refs 4
+29 0.101082 0.26903
+92 0.107062 0.326772
+91 0.102903 0.33464
+28 0.095538 0.27952
+SURF 0X10
+mat 0
+refs 4
+42 0.173313 0.341402
+105 0.161235 0.381051
+104 0.158146 0.367418
+41 0.169194 0.323223
+SURF 0X10
+mat 0
+refs 4
+55 0.176967 0.638892
+118 0.163975 0.604169
+117 0.16634 0.588387
+54 0.18012 0.617849
+SURF 0X10
+mat 0
+refs 4
+15 0.063701 0.548773
+78 0.079026 0.536579
+77 0.080143 0.554428
+14 0.065191 0.572571
+SURF 0X10
+mat 0
+refs 4
+28 0.095538 0.27952
+91 0.102903 0.33464
+90 0.098958 0.344099
+27 0.090277 0.292132
+SURF 0X10
+mat 0
+refs 4
+41 0.169194 0.323223
+104 0.158146 0.367418
+103 0.154737 0.355061
+40 0.16465 0.306747
+SURF 0X10
+mat 0
+refs 4
+54 0.18012 0.617849
+117 0.16634 0.588387
+116 0.168307 0.571753
+53 0.182743 0.595671
+SURF 0X10
+mat 0
+refs 4
+0 0.125 0.6875
+1 0.125 0.75
+2 0.118874 0.748796
+65 0.120406 0.686597
+SURF 0X10
+mat 0
+refs 4
+14 0.065191 0.572571
+77 0.080143 0.554428
+76 0.081693 0.571753
+13 0.067258 0.595671
+SURF 0X10
+mat 0
+refs 4
+27 0.090277 0.292132
+90 0.098958 0.344099
+89 0.095263 0.35506
+26 0.08535 0.306747
+SURF 0X10
+mat 0
+refs 4
+40 0.16465 0.306747
+103 0.154737 0.355061
+102 0.151042 0.344099
+39 0.159723 0.292133
+SURF 0X10
+mat 0
+refs 4
+53 0.182743 0.595671
+116 0.168307 0.571753
+115 0.169857 0.554428
+52 0.184809 0.572571
+SURF 0X10
+mat 0
+refs 4
+13 0.067258 0.595671
+76 0.081693 0.571753
+75 0.08366 0.588387
+12 0.06988 0.617849
+SURF 0X10
+mat 0
+refs 4
+26 0.08535 0.306747
+89 0.095263 0.35506
+88 0.091854 0.367417
+25 0.080806 0.323223
+SURF 0X10
+mat 0
+refs 4
+39 0.159723 0.292133
+102 0.151042 0.344099
+101 0.147097 0.33464
+38 0.154462 0.27952
+SURF 0X10
+mat 0
+refs 4
+52 0.184809 0.572571
+115 0.169857 0.554428
+114 0.170974 0.536579
+51 0.186299 0.548772
+SURF 0X10
+mat 0
+refs 4
+1 0.125 0.75
+0 0.125 0.6875
+127 0.129595 0.686597
+64 0.131126 0.748796
+SURF 0X10
+mat 0
+refs 4
+38 0.154462 0.27952
+101 0.147097 0.33464
+100 0.142938 0.326773
+37 0.148918 0.26903
+kids 0
diff --git a/Models/Autopush/cursor.xml b/Models/Autopush/cursor.xml
new file mode 100644
index 000000000..489f98152
--- /dev/null
+++ b/Models/Autopush/cursor.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ cursor.ac
+
+
+ false
+
+
+
+ scale
+ /sim/model/autopush/stopping-distance-m
+ 0.0
+ 1.0
+
+
+
+ material
+
+ /sim/model/autopush/route/invalid
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+
diff --git a/Models/Autopush/cursor_reverse.ac b/Models/Autopush/cursor_reverse.ac
new file mode 100644
index 000000000..938b7fc65
--- /dev/null
+++ b/Models/Autopush/cursor_reverse.ac
@@ -0,0 +1,133 @@
+AC3Db
+MATERIAL "autopush cursor" rgb 0.0000 0.0000 0.0000 amb 0.0000 0.0000 0.0000 emis 1.000 0.173 0.545 spec 0.0000 0.0000 0.0000 shi 50 trans 0.0000
+OBJECT world
+name "Blender_exporter_v2.26__cursor_reverse.ac"
+kids 1
+OBJECT poly
+name "Circle"
+data 11
+Circle.mesh
+crease 40.0
+numvert 36
+-0.86603 0.2 0.15
+-0.64952 0.2 0.15
+-0.64952 0.2 -0.375
+-0.86603 0.2 -0.15
+-0.64952 0.2 -0.15
+-0.86603 0.2 -0.5
+-0.56292 0.2 -0.675
+-0.45466 0.2 -0.4875
+-0 0.2 -0.75
+-0 0.2 -1
+-0.30311 0.2 -0.825
+-0.19486 0.2 -0.6375
+0.30311 0.2 -0.825
+0.19486 0.2 -0.6375
+0.64952 0.2 -0.375
+0.86603 0.2 -0.5
+0.56292 0.2 -0.675
+0.45466 0.2 -0.4875
+0.86603 0.2 -0.15
+0.64952 0.2 -0.15
+0.86603 0.2 0.5
+0.86603 0.2 0.15
+0.64952 0.2 0.15
+0.64952 0.2 0.375
+0.56292 0.2 0.675
+0.45466 0.2 0.4875
+0.30311 0.2 0.825
+0.19486 0.2 0.6375
+-0 0.2 0.75
+-0 0.2 1
+-0.30311 0.2 0.825
+-0.19486 0.2 0.6375
+-0.64952 0.2 0.375
+-0.86603 0.2 0.5
+-0.56292 0.2 0.675
+-0.45466 0.2 0.4875
+numsurf 12
+SURF 0X10
+mat 0
+refs 4
+2 0.078125 0.40625
+5 0.0625 0.375
+3 0.0625 0.5
+4 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+32 0.078125 0.40625
+1 0.078125 0.5
+0 0.0625 0.5
+33 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+8 0.078125 0.40625
+9 0.0625 0.375
+10 0.0625 0.5
+11 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+2 0.078125 0.40625
+7 0.078125 0.5
+6 0.0625 0.5
+5 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+14 0.078125 0.40625
+15 0.0625 0.375
+16 0.0625 0.5
+17 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+8 0.078125 0.40625
+13 0.078125 0.5
+12 0.0625 0.5
+9 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+23 0.078125 0.40625
+20 0.0625 0.375
+21 0.0625 0.5
+22 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+14 0.078125 0.40625
+19 0.078125 0.5
+18 0.0625 0.5
+15 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+28 0.078125 0.40625
+29 0.0625 0.375
+26 0.0625 0.5
+27 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+23 0.078125 0.40625
+25 0.078125 0.5
+24 0.0625 0.5
+20 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+32 0.078125 0.40625
+33 0.0625 0.375
+34 0.0625 0.5
+35 0.078125 0.5
+SURF 0X10
+mat 0
+refs 4
+28 0.078125 0.40625
+31 0.078125 0.5
+30 0.0625 0.5
+29 0.0625 0.375
+kids 0
diff --git a/Models/Autopush/cursor_reverse.xml b/Models/Autopush/cursor_reverse.xml
new file mode 100644
index 000000000..a09f24966
--- /dev/null
+++ b/Models/Autopush/cursor_reverse.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ cursor_reverse.ac
+
+
+ false
+
+
+
+ scale
+ /sim/model/autopush/stopping-distance-m
+ 0.0
+ 1.0
+
+
+
+ material
+
+ /sim/model/autopush/route/invalid
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+
diff --git a/Models/Autopush/cursor_sharp.ac b/Models/Autopush/cursor_sharp.ac
new file mode 100644
index 000000000..e89929598
--- /dev/null
+++ b/Models/Autopush/cursor_sharp.ac
@@ -0,0 +1,67 @@
+AC3Db
+MATERIAL "autopush cursor" rgb 0.0000 0.0000 0.0000 amb 0.0000 0.0000 0.0000 emis 1.000 0.173 0.545 spec 0.0000 0.0000 0.0000 shi 50 trans 0.0000
+OBJECT world
+name "Blender_exporter_v2.26__cursor_sharp.ac"
+kids 1
+OBJECT poly
+name "Circle"
+data 11
+Circle.mesh
+crease 40.0
+numvert 12
+0 0.2 -0.75
+0 0.2 -1
+0.64952 0.2 -0.375
+0.86603 0.2 -0.5
+0.64952 0.2 0.375
+0.86603 0.2 0.5
+0 0.2 0.75
+0 0.2 1
+-0.64952 0.2 0.375
+-0.86603 0.2 0.5
+-0.64952 0.2 -0.375
+-0.86603 0.2 -0.5
+numsurf 6
+SURF 0X10
+mat 0
+refs 4
+0 0.125 0.6875
+2 0.171875 0.59375
+3 0.1875 0.625
+1 0.125 0.75
+SURF 0X10
+mat 0
+refs 4
+2 0.171875 0.59375
+4 0.171875 0.40625
+5 0.1875 0.375
+3 0.1875 0.625
+SURF 0X10
+mat 0
+refs 4
+4 0.171875 0.40625
+6 0.125 0.3125
+7 0.125 0.25
+5 0.1875 0.375
+SURF 0X10
+mat 0
+refs 4
+6 0.125 0.3125
+8 0.078125 0.40625
+9 0.0625 0.375
+7 0.125 0.25
+SURF 0X10
+mat 0
+refs 4
+8 0.078125 0.40625
+10 0.078125 0.59375
+11 0.0625 0.625
+9 0.0625 0.375
+SURF 0X10
+mat 0
+refs 4
+10 0.078125 0.59375
+0 0.125 0.6875
+1 0.125 0.75
+11 0.0625 0.625
+kids 0
diff --git a/Models/Autopush/cursor_sharp.xml b/Models/Autopush/cursor_sharp.xml
new file mode 100644
index 000000000..df04653cc
--- /dev/null
+++ b/Models/Autopush/cursor_sharp.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ cursor_sharp.ac
+
+
+ false
+
+
+
+ scale
+ /sim/model/autopush/stopping-distance-m
+ 0.0
+ 1.0
+
+
+
+ material
+
+ /sim/model/autopush/route/invalid
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+
diff --git a/Models/Autopush/waypoint.ac b/Models/Autopush/waypoint.ac
new file mode 100644
index 000000000..e6c3ff93e
--- /dev/null
+++ b/Models/Autopush/waypoint.ac
@@ -0,0 +1,276 @@
+AC3Db
+MATERIAL "autopush cursor" rgb 0.0000 0.0000 0.0000 amb 0.0000 0.0000 0.0000 emis 1.000 0.173 0.545 spec 0.0000 0.0000 0.0000 shi 50 trans 0.0000
+OBJECT world
+name "Blender_exporter_v2.26__waypoint.ac"
+kids 3
+OBJECT poly
+name "Waypoint"
+data 11
+Circle.mesh
+crease 40.0
+numvert 33
+0.03902 0.2 -0.19616
+0.07654 0.2 -0.18478
+0.11111 0.2 -0.16629
+0.14142 0.2 -0.14142
+0.16629 0.2 -0.11111
+0.18478 0.2 -0.07654
+0.19616 0.2 -0.03902
+0.2 0.2 0
+0.19616 0.2 0.03902
+0.18478 0.2 0.07654
+0.16629 0.2 0.11111
+0.14142 0.2 0.14142
+0.11111 0.2 0.16629
+0.07654 0.2 0.18478
+0.03902 0.2 0.19616
+0 0.2 0.2
+-0.03902 0.2 0.19616
+-0.07654 0.2 0.18478
+-0.11111 0.2 0.16629
+-0.14142 0.2 0.14142
+-0.16629 0.2 0.11111
+-0.18478 0.2 0.07654
+-0.19616 0.2 0.03902
+-0.2 0.2 0
+-0.19616 0.2 -0.03902
+-0.18478 0.2 -0.07654
+-0.16629 0.2 -0.11111
+-0.14142 0.2 -0.14142
+-0.11111 0.2 -0.16629
+-0.07654 0.2 -0.18478
+-0.03902 0.2 -0.19616
+0 0.2 -0.2
+0 0.2 0
+numsurf 32
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+18 0.090277 0.292133
+17 0.101082 0.26903
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+31 0.125 0.75
+30 0.112807 0.745197
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+4 0.176967 0.638893
+3 0.169194 0.676777
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+17 0.101082 0.26903
+16 0.112807 0.254804
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+30 0.112807 0.745197
+29 0.101082 0.73097
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+3 0.169194 0.676777
+2 0.159723 0.707868
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+16 0.112807 0.254804
+15 0.125 0.25
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+29 0.101082 0.73097
+28 0.090277 0.707868
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+2 0.159723 0.707868
+1 0.148918 0.73097
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+15 0.125 0.25
+14 0.137193 0.254804
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+28 0.090277 0.707868
+27 0.080806 0.676777
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+1 0.148918 0.73097
+0 0.137193 0.745197
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+14 0.137193 0.254804
+13 0.148918 0.26903
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+27 0.080806 0.676777
+26 0.073033 0.638893
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+0 0.137193 0.745197
+31 0.125 0.75
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+13 0.148918 0.26903
+12 0.159723 0.292133
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+26 0.073033 0.638893
+25 0.067258 0.595671
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+12 0.159723 0.292133
+11 0.169194 0.323224
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+25 0.067258 0.595671
+24 0.063701 0.548773
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+11 0.169194 0.323224
+10 0.176967 0.361108
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+24 0.063701 0.548773
+23 0.0625 0.5
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+10 0.176967 0.361108
+9 0.182743 0.404329
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+23 0.0625 0.5
+22 0.063701 0.451228
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+9 0.182743 0.404329
+8 0.186299 0.451228
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+22 0.063701 0.451228
+21 0.067258 0.404329
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+8 0.186299 0.451228
+7 0.1875 0.5
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+21 0.067258 0.404329
+20 0.073033 0.361108
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+7 0.1875 0.5
+6 0.186299 0.548773
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+20 0.073033 0.361108
+19 0.080806 0.323223
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+6 0.186299 0.548773
+5 0.182742 0.595671
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+19 0.080806 0.323223
+18 0.090277 0.292133
+SURF 0X0
+mat 0
+refs 3
+32 0.125 0.5
+5 0.182742 0.595671
+4 0.176967 0.638893
+kids 0
+OBJECT poly
+name "WingtipL"
+data 15
+Circle.mesh.001
+crease 40.0
+numvert 4
+-0.1 0.2 -0.1
+-0.1 0.2 0.1
+0.1 0.2 -0.1
+0.1 0.2 0.1
+numsurf 1
+SURF 0X0
+mat 0
+refs 4
+0 0 0
+1 0 0
+3 0 0
+2 0 0
+kids 0
+OBJECT poly
+name "WingtipR"
+data 15
+Circle.mesh.007
+crease 40.0
+numvert 4
+-0.1 0.2 -0.1
+-0.1 0.2 0.1
+0.1 0.2 -0.1
+0.1 0.2 0.1
+numsurf 1
+SURF 0X0
+mat 0
+refs 4
+0 0 0
+1 0 0
+3 0 0
+2 0 0
+kids 0
diff --git a/Models/Autopush/waypoint.xml b/Models/Autopush/waypoint.xml
new file mode 100644
index 000000000..8708adc85
--- /dev/null
+++ b/Models/Autopush/waypoint.xml
@@ -0,0 +1,87 @@
+
+
+
+
+ waypoint.ac
+
+
+ false
+
+
+
+ select
+ WingtipL
+ WingtipR
+
+ /sim/model/autopush/route/show-wingtip
+
+
+
+
+ translate
+ WingtipL
+ /sim/model/autopush/route/wingspan-m
+ 0.5
+
+ 0
+ 1
+ 0
+
+ 0.0
+ 1.0
+
+
+
+ translate
+ WingtipR
+ /sim/model/autopush/route/wingspan-m
+ -0.5
+
+ 0
+ 1
+ 0
+
+ 0.0
+ 1.0
+
+
+
+ scale
+
+ Waypoint
+ WingtipL
+ WingtipR
+ /sim/model/autopush/stopping-distance-m
+ 0.0
+ 1.0
+
+
+
+ material
+
+ /sim/model/autopush/route/invalid
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+ 1.0
+ 0.0
+ 0.0
+
+
+
+
diff --git a/Nasal/Autopush/autopush.nas b/Nasal/Autopush/autopush.nas
new file mode 100644
index 000000000..02b7f13f1
--- /dev/null
+++ b/Nasal/Autopush/autopush.nas
@@ -0,0 +1,141 @@
+# AUTOPUSH
+# Basic pushback logic class.
+#
+# Copyright (c) 2018 Autopush authors:
+# Michael Danilov
+# Joshua Davidson http://github.com/Octal450
+# Merspieler http://gitlab.com/merspieler
+# Distribute under the terms of GPLv2.
+
+
+var _K_p = nil;
+var _F_p = nil;
+var _K_i = nil;
+var _F_i = nil;
+var _K_d = nil;
+var _F_d = nil;
+var _F = nil;
+var _int = nil;
+var _V = nil;
+var _T_f = nil;
+var _K_yaw = nil;
+var _yasim = 0;
+var _time = nil;
+# (ft / s^2) / ((km / h) / s)
+var _unitconv = M2FT / 3.6;
+var _debug = nil;
+
+var _loop = func() {
+ if (!getprop("/sim/model/autopush/available")) {
+ _stop();
+ return;
+ }
+ var force = 0.0;
+ var x = 0.0;
+ var y = 0.0;
+ var z = 0.0;
+ # Rollspeed is only adequate if the wheel is touching the ground.
+ if (getprop("/gear/gear[0]/wow")) {
+ var V = getprop("/gear/gear[0]/rollspeed-ms") * 3.6;
+ var deltaV = getprop("/sim/model/autopush/target-speed-km_h") - V;
+ var minus_dV = _V - V;
+ var time = getprop("/sim/time/elapsed-sec");
+ var prop = math.min(math.max(_K_p * deltaV, -_F_p), _F_p);
+ var dt = time - _time;
+ var deriv = 0.0;
+ # XXX Sanitising dt. Smaller chance of freakout on lag spike.
+ if(dt > 0.0) {
+ if(dt < 0.05) {
+ _int = math.min(math.max(_int + _K_i * deltaV * dt, -_F_i), _F_i);
+ }
+ if(dt > 0.002) {
+ deriv = math.min(math.max(_K_d * minus_dV / dt, -_F_d), _F_d);
+ }
+ }
+ var accel = prop + _int + deriv;
+ if (_debug > 2) {
+ print("pushback prop " ~ prop ~ ", _int " ~ _int ~ ", deriv " ~ deriv);
+ }
+ _V = V;
+ _time = time;
+ if (!_yasim) {
+ force = accel * getprop("/fdm/jsbsim/inertia/weight-lbs") * _unitconv;
+ } else {
+ force = accel * getprop("/fdm/yasim/gross-weight-lbs") * _unitconv;
+ }
+ var pitch = getprop("/sim/model/autopush/pitch-deg") * D2R;
+ z = math.sin(pitch);
+ var pz = math.cos(pitch);
+ var yaw = getprop("/sim/model/autopush/yaw") * _K_yaw;
+ x = math.cos(yaw) * pz;
+ y = math.sin(yaw) * pz;
+ setprop("/sim/model/autopush/force-x", x);
+ setprop("/sim/model/autopush/force-y", y);
+ # JSBSim force's z is down.
+ setprop("/sim/model/autopush/force-z", -z);
+ }
+ setprop("/sim/model/autopush/force-lbf", force);
+ if (_yasim) {
+ # The force is divided by YASim thrust="100000.0" setting.
+ setprop("/sim/model/autopush/force-x-yasim", x * force * 0.00001);
+ # YASim force's y is to the left.
+ setprop("/sim/model/autopush/force-y-yasim", -y * force * 0.00001);
+ setprop("/sim/model/autopush/force-z-yasim", z * force * 0.00001);
+ }
+}
+
+var _timer = maketimer(0.0167, func{_loop()});
+
+var _start = func() {
+ # Else overwritten by dialog.
+ settimer(func() {
+ setprop("/sim/model/autopush/target-speed-km_h", 0.0)
+ }, 0.1);
+ _K_p = getprop("/sim/model/autopush/K_p");
+ _F_p = getprop("/sim/model/autopush/F_p");
+ _K_i = getprop("/sim/model/autopush/K_i");
+ _F_i = getprop("/sim/model/autopush/F_i");
+ _K_d = getprop("/sim/model/autopush/K_d");
+ _F_d = getprop("/sim/model/autopush/F_d");
+ _F = getprop("/sim/model/autopush/F");
+ _T_f = getprop("/sim/model/autopush/T_f");
+ _K_yaw = getprop("/sim/model/autopush/yaw-mult") * D2R;
+ _yasim = (getprop("/sim/flight-model") == "yasim");
+ _debug = getprop("/sim/model/autopush/debug") or 0;
+ _int = 0.0;
+ _V = 0.0;
+ _time = getprop("/sim/time/elapsed-sec");
+ setprop("/sim/model/autopush/connected", 1);
+ if (!_timer.isRunning) {
+ if (getprop("/sim/model/autopush/chocks")) {
+ setprop("/sim/model/autopush/chocks", 0);
+ screen.log.write("(pushback): Pushback connected, chocks removed. Please release brakes.");
+ } else {
+ screen.log.write("(pushback): Pushback connected, please release brakes.");
+ }
+ }
+ _timer.start();
+}
+
+var _stop = func() {
+ if (_timer.isRunning) {
+ screen.log.write("(pushback): Pushback and bypass pin removed.");
+ }
+ _timer.stop();
+ setprop("/sim/model/autopush/force-lbf", 0.0);
+ if (_yasim) {
+ setprop("/sim/model/autopush/force-x-yasim", 0.0);
+ setprop("/sim/model/autopush/force-y-yasim", 0.0);
+ }
+ setprop("/sim/model/autopush/connected", 0);
+ setprop("/sim/model/autopush/enabled", 0);
+}
+
+setlistener("/sim/model/autopush/enabled", func(p) {
+ var enabled = p.getValue();
+ if ((enabled) and getprop("/sim/model/autopush/available")) {
+ _start();
+ } else {
+ _stop();
+ }
+}, 1, 0);
diff --git a/Nasal/Autopush/driver.nas b/Nasal/Autopush/driver.nas
new file mode 100644
index 000000000..a8c1cbb1d
--- /dev/null
+++ b/Nasal/Autopush/driver.nas
@@ -0,0 +1,155 @@
+# AUTOPUSH
+# Pushback driver class.
+#
+# Command the pushback to tow/push the aircraft.
+#
+# Copyright (c) 2018 Autopush authors:
+# Michael Danilov
+# Joshua Davidson http://github.com/Octal450
+# Merspieler http://gitlab.com/merspieler
+# Distribute under the terms of GPLv2.
+
+
+var _K_V = nil;
+var _F_V = nil;
+var _R_turn_min = nil;
+var _D_stop = nil;
+var _K_psi = nil;
+var _F_psi = nil;
+var _K_psidot = nil;
+var _F_psidot = nil;
+var _debug = nil;
+var _psi = nil;
+var _time = nil;
+
+var _route = nil;
+var _route_reverse = nil;
+var _push = nil;
+var _sign = nil;
+
+var _to_wp = 0;
+var _is_last_wp = 0;
+var _is_reverse_wp = 0;
+
+
+var _advance_wp = func(flip_sign = 0) {
+ _to_wp += 1;
+ _is_last_wp = (_to_wp == (size(_route) - 1));
+ _is_reverse_wp = (_route_reverse[_to_wp]);
+ if (flip_sign) {
+ _sign *= -1;
+ _push = !_push;
+ }
+ if (_debug == 1) {
+ print("autopush_driver to_wp " ~ _to_wp);
+ }
+}
+
+var _loop = func() {
+ if (!getprop("/sim/model/autopush/connected")) {
+ stop();
+ return;
+ }
+ var (A, D) = courseAndDistance(_route[_to_wp]);
+ D *= NM2M;
+ var (psi_leg, D_leg) = courseAndDistance(_route[_to_wp - 1], _route[_to_wp]);
+ var deltapsi = geo.normdeg180(A - psi_leg);
+ var psi = getprop("/orientation/heading-deg") + _push * 180.0;
+ var deltaA = math.min(math.max(_K_psi * geo.normdeg180(A - psi), -_F_psi), _F_psi);
+ var time = getprop("/sim/time/elapsed-sec");
+ var dt = time - _time;
+ var minus_psidot = (dt > 0.002) * math.min(math.max(_K_psidot * (_psi - psi) / dt, -_F_psidot), _F_psidot);
+ _psi = psi;
+ _time = time;
+ # TODO Either use _K_V and total remaining distance or turn radius to calculate speed.
+ # TODO Make slider input override speed.
+ var V = _F_V;
+ if (_is_reverse_wp or _is_last_wp) {
+ if ((D < _D_stop) or (abs(deltapsi) > 90.0)) {
+ if (_is_last_wp) {
+ _done();
+ return;
+ }
+ if (_is_reverse_wp) {
+ _advance_wp(1);
+ }
+ }
+ } else {
+ if ((D < _R_turn_min) or (abs(deltapsi) > 90.0)) {
+ _advance_wp();
+ }
+ }
+ var steering = math.min(math.max(_sign * (deltaA + minus_psidot), -1.0), 1.0);
+ if (_debug > 1) {
+ print("autopush_driver to_wp " ~ _to_wp ~ ", A " ~ geo.normdeg(A) ~ ", deltaA " ~ deltaA ~ ", minus_psidot " ~ minus_psidot);
+ }
+ setprop("/sim/model/autopush/target-speed-km_h", _sign * V);
+ setprop("/sim/model/autopush/steer-cmd-norm", steering);
+}
+
+var _timer = maketimer(0.051, func{_loop()});
+
+var _done = func() {
+ stop();
+ autopush_route.clear();
+ screen.log.write("(pushback): Pushback complete, please set parking brake.");
+}
+
+var start = func() {
+ if (_timer.isRunning) {
+ gui.popupTip("Already moving");
+ return;
+ }
+ if (!getprop("/sim/model/autopush/connected")) {
+ gui.popupTip("Pushback not connected");
+ return;
+ }
+ _route = autopush_route.route();
+ _route_reverse = autopush_route.route_reverse();
+ if ((_route == nil) or size(_route) < 2) {
+ gui.popupTip("Pushback route empty or invalid");
+ return;
+ } else {
+ autopush_route.done();
+ }
+ _K_V = getprop("/sim/model/autopush/driver/K_V");
+ _F_V = getprop("/sim/model/autopush/driver/F_V");
+ _R_turn_min = getprop("/sim/model/autopush/min-turn-radius-m");
+ _D_stop = getprop("/sim/model/autopush/stopping-distance-m");
+ _K_psi = getprop("/sim/model/autopush/driver/K_psi");
+ _F_psi = getprop("/sim/model/autopush/driver/F_psi");
+ _K_psidot = getprop("/sim/model/autopush/driver/K_psidot");
+ _F_psidot = getprop("/sim/model/autopush/driver/F_psidot");
+ _debug = getprop("/sim/model/autopush/debug") or 0;
+ if (!_to_wp) {
+ var (psi_park, D_park) = courseAndDistance(_route[0], _route[1]);
+ _push = (abs(geo.normdeg180(getprop("/orientation/heading-deg") - psi_park)) > 90.0);
+ _sign = 1.0 - 2.0 * _push;
+ _advance_wp();
+ _psi = 0.0;
+ }
+ _time = getprop("/sim/time/elapsed-sec");
+ _timer.start();
+ var endsign = _sign;
+ for (ii = _to_wp; ii < size(_route_reverse); ii += 1) {
+ if (_route_reverse[ii]) {
+ endsign = -endsign;
+ }
+ }
+ var (psi_twy, D_twy) = courseAndDistance(_route[size(_route) - 2], _route[size(_route) - 1]);
+ if (endsign < 0.0) {
+ screen.log.write("(pushback): Push back facing " ~ math.round(geo.normdeg(psi_twy + 180.0 - magvar()), 1.0) ~ ".");
+ } else {
+ screen.log.write("(pushback): Tow facing " ~ math.round(geo.normdeg(psi_twy - magvar()), 1.0) ~ ".");
+ }
+}
+
+var pause = func() {
+ _timer.stop();
+ setprop("/sim/model/autopush/target-speed-km_h", 0.0);
+}
+
+var stop = func() {
+ pause();
+ _to_wp = 0;
+}
diff --git a/Nasal/Autopush/dynarr.nas b/Nasal/Autopush/dynarr.nas
new file mode 100644
index 000000000..03518aa0f
--- /dev/null
+++ b/Nasal/Autopush/dynarr.nas
@@ -0,0 +1,61 @@
+# Class for dynamic arrays
+#
+# Copyright (c) 2018 dynamic arrays authors:
+# Michael Danilov
+# Merspieler http://gitlab.com/merspieler
+# Distribute under the terms of GPLv2.
+
+## Useage
+# to create a new object: var = dynarr.new();
+# to add elements: .add();
+# you can access the full stored array as: .arr
+# to get only the used area of the array: var = .get_spliced()
+
+var dynarr =
+{
+ new: func(size = 8)
+ {
+ var this = {parents:[dynarr]};
+ this.maxsize = size;
+ this.size = 0;
+ this.arr = setsize([], size);
+
+ return this;
+ },
+
+ # add a new element to the array
+ add: func(obj)
+ {
+ # case there's no space left
+ if (me.size + 1 >= me.maxsize)
+ {
+ # double array size
+ me.maxsize *= 2;
+ me.arr = setsize(me.arr, me.maxsize);
+ }
+
+ # add object and increase used counter
+ me.arr[me.size] = obj;
+ me.size += 1;
+ },
+
+ # delete an element from the array
+ del: func(id)
+ {
+ me.size -= 1;
+ for(ii = id; ii < me.size - 1; ii += 1){
+ me.arr[ii] = me.arr[ii + 1];
+ }
+ },
+
+ # returns only the filled part of the array or nil if array is empty
+ get_sliced: func()
+ {
+ if (me.size == 0)
+ {
+ return nil;
+ }
+
+ return me.arr[0:me.size - 1];
+ }
+};
diff --git a/Nasal/Autopush/route.nas b/Nasal/Autopush/route.nas
new file mode 100644
index 000000000..b67c60723
--- /dev/null
+++ b/Nasal/Autopush/route.nas
@@ -0,0 +1,433 @@
+# AUTOPUSH
+# Visual entry of pushback route.
+#
+# Copyright (c) 2018 Autopush authors:
+# Michael Danilov
+# Joshua Davidson http://github.com/Octal450
+# Merspieler http://gitlab.com/merspieler
+# Distribute under the terms of GPLv2.
+
+
+var _listener = nil;
+var _view_listener = nil;
+var _user_points = dynarr.dynarr.new(4);
+var _user_point_modes = dynarr.dynarr.new(4); # Modes: 0 = Bezier node, 1 = Bezier end/start node, 2 = Reverse
+var _route = [];
+var _route_hdg = [];
+var _route_reverse = [];
+var _top_view_index = nil;
+var _top_view_heading_offset_deg = 180.0;
+var _reset_view_index = nil;
+var _view_z_offset = nil;
+var _view_pitch_offset_deg = nil;
+var _view_heading_offset_deg = nil;
+var _user_point_models = [];
+var _waypoint_models = [];
+var _N = 0;
+var _show = 0;
+var _R_turn_min = 0;
+var _invalid = 0;
+
+# Make top-down view point north in old FG.
+var __fg_version = num(string.replace(getprop("/sim/version/flightgear"),".",""));
+if (__fg_version < 201920) {
+ _top_view_heading_offset_deg = 94.5;
+}
+
+
+var _add = func(pos) {
+ if (_N) {
+ var (A, S) = courseAndDistance(_user_points.arr[_N - 1], pos);
+ S *= NM2M;
+ if (S < _R_turn_min) {
+ gui.popupTip("Too close to the previous point,\ntry again");
+ return;
+ }else if (S > 10000.0) {
+ gui.popupTip("Too far from the previous point,\ntry again");
+ return;
+ }
+ }
+ _user_points.add(geo.Coord.new(pos));
+
+ if (_user_point_modes.maxsize == 1 and _user_point_modes.size == 1) {
+ _user_point_modes.arr[0] = 0;
+ } else {
+ _user_point_modes.add(0);
+ }
+ setsize(_user_point_models, _N + 1);
+ _user_point_models[_N] = geo.put_model("Models/Autopush/cursor.xml", pos, 0.0);
+ _N += 1;
+ if (_N == 1) {
+ gui.popupTip("Click waypoints, press \"Done\" to finish");
+ } else {
+ _calculate_route();
+ _place_waypoint_models();
+ }
+}
+
+var delete_last = func() {
+ if (_listener == nil) {
+ return;
+ }
+ if (_N > 1) {
+ _N -= 1;
+ _user_points.del(_N);
+ _user_point_modes.del(_N);
+ _user_point_models[_N].remove();
+ _user_point_models[_N] = nil;
+ setsize(_user_point_models, _N);
+ _calculate_route();
+ _place_waypoint_models();
+ }
+}
+
+var _stop = func(fail = 0) {
+ if (_listener != nil) {
+ removelistener(_listener);
+ _listener = nil;
+ if (!fail) {
+ settimer(func() {
+ _finalize_top_view();
+ gui.popupTip("Done");
+ }, 1.0);
+ } else {
+ _finalize_top_view();
+ }
+ }
+}
+
+var _place_user_point_models = func() {
+ _clear_user_point_models();
+ setsize(_user_point_models, _N);
+ var user_points = _user_points.get_sliced();
+ for (var ii = 0; ii < _N; ii += 1) {
+ var model = "Models/Autopush/cursor.xml";
+ if (_user_point_modes.arr[ii] == 1) {
+ model = "Models/Autopush/cursor_sharp.xml";
+ } else if (_user_point_modes.arr[ii] == 2) {
+ model = "Models/Autopush/cursor_reverse.xml";
+ }
+ _user_point_models[ii] = geo.put_model(model, user_points[ii], 0.0);
+ }
+}
+
+var _clear_user_point_models = func() {
+ for (var ii = 0; ii < size(_user_point_models); ii += 1) {
+ if (_user_point_models[ii] != nil) {
+ _user_point_models[ii].remove();
+ _user_point_models[ii] = nil;
+ }
+ }
+ setsize(_user_point_models, 0);
+}
+
+var _place_waypoint_models = func() {
+ _clear_waypoint_models();
+ setsize(_waypoint_models, size(_route));
+ for (var ii = 0; ii < size(_route); ii += 1) {
+ _waypoint_models[ii] = geo.put_model("Models/Autopush/waypoint.xml", _route[ii], _route_hdg[ii]);
+ }
+}
+
+var _clear_waypoint_models = func() {
+ for (var ii = 0; ii < size(_waypoint_models); ii += 1) {
+ if (_waypoint_models[ii] != nil) {
+ _waypoint_models[ii].remove();
+ _waypoint_models[ii] = nil;
+ }
+ }
+ setsize(_waypoint_models, 0);
+}
+
+var top_view = func() {
+ if (_view_listener != nil) {
+ return;
+ }
+ _top_view_index = view.indexof("Chase View Without Yaw");
+ _reset_view_index = getprop("/sim/current-view/view-number");
+ setprop("/sim/current-view/view-number", _top_view_index);
+ _view_pitch_offset_deg = getprop("/sim/current-view/pitch-offset-deg");
+ _view_heading_offset_deg = getprop("/sim/current-view/heading-offset-deg");
+ _view_z_offset = getprop("/sim/current-view/z-offset-m");
+ setprop("/sim/current-view/z-offset-m", -500.0);
+ setprop("/sim/current-view/heading-offset-deg", _top_view_heading_offset_deg);
+ setprop("/sim/current-view/pitch-offset-deg", 90.0);
+ _view_listener = setlistener("/sim/current-view/name", func {
+ _finalize_top_view();
+ }, 0, 0);
+}
+
+var _finalize_top_view = func() {
+ if (_view_listener == nil) {
+ return;
+ }
+ removelistener(_view_listener);
+ _view_listener = nil;
+ # Go back to the view to restore settings, in case user has switched away.
+ setprop("/sim/current-view/view-number", _top_view_index);
+ setprop("/sim/current-view/z-offset-m", _view_z_offset);
+ setprop("/sim/current-view/heading-offset-deg", _view_heading_offset_deg);
+ setprop("/sim/current-view/pitch-offset-deg", _view_pitch_offset_deg);
+ setprop("/sim/current-view/view-number", _reset_view_index);
+ if (!_show) {
+ _clear_user_point_models();
+ _clear_waypoint_models();
+ }
+}
+
+var _calculate_route = func() {
+ _route = [];
+ _route_reverse = [];
+ var user_points = _user_points.get_sliced();
+ var route = dynarr.dynarr.new();
+ # add the first point cause it will be fix at this pos
+ route.add(geo.Coord.new(user_points[0]));
+ n = size(user_points);
+ var base = 0;
+ # Detect points where push/pull direction is reversed.
+ for (var i = 0; i < n; i += 1) {
+ if (i and (i < n - 1)) {
+ if((_user_point_modes.arr[i] == 1) or (_user_point_modes.arr[i] == 2)) {
+ var newmode = 1;
+ var deltaA = abs(geo.normdeg180(user_points[i - 1].course_to(user_points[i]) - user_points[i].course_to(user_points[i + 1])));
+ if (deltaA > 91.0) {
+ newmode = 2;
+ }
+ if(newmode != _user_point_modes.arr[i]){
+ _set_userpoint_mode(i, newmode);
+ }
+ }
+ } else {
+ # Clear reverse for first and last points.
+ if(_user_point_modes.arr[i] == 2) {
+ if(_user_point_modes.arr[i] != 1){
+ _set_userpoint_mode(i, 1);
+ }
+ }
+ }
+ }
+ for (var i = 0; i < n; i += 1) {
+ if (_user_point_modes.arr[i] or (i == n - 1)) {
+ if (i - base > 0) {
+ var bezier = _calculate_bezier(user_points[base:i]);
+ if (bezier != nil) {
+ var m = size(bezier);
+ for (var j = 0; j < m; j += 1) {
+ route.add(geo.Coord.new(bezier[j]));
+ }
+ }
+ }
+ base = i;
+ route.add(geo.Coord.new(user_points[i]));
+ if (_user_point_modes.arr[i] == 2) {
+ var route_size = size(route.get_sliced());
+ setsize(_route_reverse, route_size);
+ _route_reverse[route_size - 1] = 1;
+ }
+ }
+ }
+ var PNumber = size(user_points);
+ _route = route.get_sliced();
+ setsize(_route_reverse, size(_route));
+ _check_turn_radius();
+ _calculate_hdg();
+}
+
+var _calculate_bezier = func(user_points) {
+ var route = dynarr.dynarr.new();
+
+ var PNumber = size(user_points);
+
+ if (PNumber > 1) {
+ var pointList = [];
+ setsize(pointList, PNumber);
+ for (var i = 0; i < PNumber; i += 1) {
+ pointList[i] = [];
+ setsize(pointList[i], PNumber);
+ }
+
+ pointList[0] = user_points;
+
+ var len = 0;
+ for (var i = 0; i < PNumber - 1; i += 1) {
+ len += user_points[i].distance_to(user_points[i + 1]);
+ }
+
+ if (len < _R_turn_min) {
+ route.add(geo.Coord.new(user_points[PNumber - 1]))
+ } else {
+ var step = _R_turn_min / len;
+
+ for (var i = step; i < 1 - step; i+= step) {
+ # start iterating from 1 cause we don't need to iterate over Pn
+ for (var j = 1; j < PNumber; j += 1) {
+ for (var k = 0; k < PNumber - j; k += 1) {
+ pointList[j][k] = geo.Coord.new(pointList[j - 1][k]);
+ var dist = pointList[j - 1][k].distance_to(pointList[j - 1][k + 1]);
+ var course = pointList[j - 1][k].course_to(pointList[j - 1][k + 1]);
+ pointList[j][k].apply_course_distance(course, dist * i);
+ }
+ }
+ pointList[PNumber - 1][0].set_alt(geo.elevation(pointList[PNumber - 1][0].lat(),pointList[PNumber - 1][0].lon()));
+ route.add(geo.Coord.new(pointList[PNumber - 1][0]));
+ }
+ }
+ }
+
+ return route.get_sliced();
+}
+
+var _calculate_hdg = func() {
+ _route_hdg = [];
+ var route_hdg = dynarr.dynarr.new();
+ var ilast = size(_route) - 1;
+ for (i = 0; i < ilast; i += 1) {
+ var hdg = _route[i].course_to(_route[i + 1]);
+ route_hdg.add(hdg);
+ }
+ # Last heading would be undefined, so just repeat the one before the last.
+ route_hdg.add(route_hdg.get_sliced()[ilast - 1]);
+ _route_hdg = route_hdg.get_sliced();
+}
+
+# Checks each waypoint's turn radius and marks the route invalid if
+# it is smaller than the aircraft's turn radius.
+var _check_turn_radius = func() {
+ # A waypoint's turn radius is the radius of a circle circumscribed around the waypoint, previous and next waypoints.
+ # Formula source: https://math.stackexchange.com/questions/947882/radius-of-circumscribed-circle-of-triangle-as-function-of-the-sides
+
+ var len = size(_route);
+ _invalid = 0;
+
+ # We can't calculate the radius for the first and last point
+ for (i = 1; i < len - 2; i += 1) {
+ # Disable check for push and pull points
+ if (_route_reverse[i] != 1) {
+ var a = _route[i].distance_to(_route[i + 1]);
+ var b = _route[i].distance_to(_route[i - 1]);
+ var c = _route[i - 1].distance_to(_route[i + 1]);
+
+
+ var margin = _R_turn_min / 5000;
+
+ # Stright line check with marging to prevent floating point error
+ if (a + b + margin >= c and a + b - margin <= c) {
+ var r = - 1;
+ } else {
+ var r = (a * b * c) / math.sqrt(
+ 2 * a * a * b * b
+ + 2 * a * a * c * c
+ + 2 * b * b * c * c
+ - a * a * a * a
+ - b * b * b * b
+ - c * c * c * c
+ );
+ }
+
+ if ((r < _R_turn_min) and (r != -1)) {
+ _invalid = 1;
+ }
+ }
+ }
+
+ setprop("/sim/model/autopush/route/invalid", _invalid);
+}
+
+setlistener("/sim/model/autopush/route/show", func(p) {
+ var show = p.getValue();
+ if (_listener == nil) {
+ if (show) {
+ _place_user_point_models();
+ _place_waypoint_models();
+ } else {
+ _clear_user_point_models();
+ _clear_waypoint_models();
+ }
+ }
+ _show = show;
+}, 1, 0);
+
+
+var enter = func() {
+ clear();
+ top_view();
+ _R_turn_min = getprop("/sim/model/autopush/min-turn-radius-m");
+ var wp = geo.aircraft_position();
+ var H = geo.elevation(wp.lat(), wp.lon());
+ if (H != nil) {
+ wp.set_alt(H);
+ }
+ _add(wp);
+ _listener = setlistener("/sim/signals/click", func {
+ _add(geo.click_position());
+ });
+ # This property can be overridden manually, if needed.
+ var wingspan = getprop("/sim/model/autopush/route/wingspan-m");
+ if ((wingspan == nil) or (wingspan == 0.0)) {
+ # JSBSim
+ wingspan = getprop("/fdm/jsbsim/metrics/bw-ft");
+ if (wingspan != nil) {
+ wingspan *= FT2M;
+ } else {
+ # YAsim
+ wingspan = getprop("/fdm/yasim/model/wings/wing/wing-span");
+ }
+ setprop("/sim/model/autopush/route/wingspan-m", wingspan);
+ }
+}
+
+var _set_userpoint_mode = func(id, mode) {
+ if (_user_point_modes.arr[id] != mode) {
+ _user_point_modes.arr[id] = mode;
+ }
+ if (_user_point_models[id] != nil) {
+ _user_point_models[id].remove();
+ var model = "Models/Autopush/cursor.xml";
+ if (_user_point_modes.arr[id] == 1) {
+ model = "Models/Autopush/cursor_sharp.xml";
+ } else if (_user_point_modes.arr[id] == 2) {
+ model = "Models/Autopush/cursor_reverse.xml";
+ }
+ _user_point_models[id] = geo.put_model(model, _user_points.get_sliced()[id], 0.0);
+ }
+}
+
+var toggle_sharp = func() {
+ if (_listener == nil) {
+ return;
+ }
+ id = _N - 1;
+ if (_user_point_modes.arr[id]) {
+ _set_userpoint_mode(id, 0);
+ } else {
+ _set_userpoint_mode(id, 1);
+ }
+}
+
+var done = func() {
+ _stop(0);
+}
+
+var clear = func() {
+ autopush_driver.stop();
+ _stop(1);
+ _clear_user_point_models();
+ _clear_waypoint_models();
+ _N = 0;
+ _user_points = dynarr.dynarr.new(4);
+ _user_point_modes = dynarr.dynarr.new(1);
+}
+
+var route = func() {
+ if (_invalid or (_N < 2)) {
+ return nil;
+ }
+ return _route;
+}
+
+var route_reverse = func() {
+ if (_invalid or (_N < 2)) {
+ return nil;
+ }
+ return _route_reverse;
+}
diff --git a/gui/dialogs/autopush.xml b/gui/dialogs/autopush.xml
new file mode 100644
index 000000000..7a619bd8d
--- /dev/null
+++ b/gui/dialogs/autopush.xml
@@ -0,0 +1,361 @@
+
+
+
+
+
+
+ autopush
+ vbox
+
+
+
+ hbox
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ true
+ vbox
+ center
+ top
+
+
+ left
+
+ /sim/model/autopush/enabled
+ true
+
+ dialog-apply
+
+
+
+
+ table
+
+
+ 0
+ 0
+
+ left
+
+
+
+
+
+ 0
+ 2
+ -1.0
+ 1.0
+ /sim/model/autopush/steer-cmd-norm
+ 0.1
+ true
+
+ dialog-apply
+
+
+
+
+
+
+
+
+ 1
+ 0
+
+ left
+
+
+
+
+
+ 1
+ 2
+ -25
+ 25
+ /sim/model/autopush/target-speed-km_h
+ 1.0
+ true
+
+ dialog-apply
+
+
+
+
+
+
+
+
+ 1
+ 5
+ 16
+ /sim/model/autopush/target-speed-km_h
+ %3.0f
+ true
+ right
+
+
+
+ 1
+ 6
+
+ left
+
+
+
+
+
+
+
+ table
+
+
+ 0
+ 0
+
+ left
+
+
+
+
+
+
+
+
+
+ 1
+ 0
+
+ left
+
+
+
+
+
+
+
+ 1
+ 3
+ left
+
+ /sim/model/autopush/route/show
+ true
+
+ dialog-apply
+
+
+
+
+ 2
+ 0
+
+ left
+
+
+
+
+
+
+
+ 2
+ 3
+ left
+
+ true
+ /sim/model/autopush/route/show-wingtip
+
+ dialog-apply
+
+
+
+
+
+
+
+