Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9b20818cd | ||
|
|
f529f2ce7c | ||
|
|
1d5bd7c7f5 | ||
|
|
c60097c8cd | ||
|
|
f3d50cc815 | ||
|
|
cbc1888425 | ||
|
|
ea72151d48 | ||
|
|
bcd7d3cca8 | ||
|
|
6cd64a486a | ||
|
|
93a0d6f46d | ||
|
|
f4356c079f | ||
|
|
9ed54d98b6 | ||
|
|
b12e139b2c | ||
|
|
b1403b3066 | ||
|
|
3376a6f35d | ||
|
|
1d22597d3f | ||
|
|
467336590b | ||
|
|
5ce9a0a299 | ||
|
|
dff16702d4 | ||
|
|
97e02a332a | ||
|
|
9acdec8ae6 | ||
|
|
5ad232b83e | ||
|
|
e39815b44c | ||
|
|
09a52f18ab | ||
|
|
e06948d99d | ||
|
|
51fe8f095e | ||
|
|
8a0eaab753 | ||
|
|
3c49ba9d47 | ||
|
|
da1527c945 | ||
|
|
459e3a965a | ||
|
|
b1f1ef4a28 | ||
|
|
3c306ca039 | ||
|
|
92fd83615d | ||
|
|
29103533fb | ||
|
|
5aa5f731a6 | ||
|
|
1982df9f16 | ||
|
|
bd40f6830e | ||
|
|
1f8ea67e44 | ||
|
|
0c3046da80 | ||
|
|
267b231d8c |
40
CHANGELOG.md
@@ -1,5 +1,45 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v.1.8.1
|
||||
|
||||
- Improve the approach to landing
|
||||
|
||||
## v.1.8.0
|
||||
|
||||
- The tow plane, after completing its route, returns to the runway from which it took off. For a bush take-off it does not land.
|
||||
|
||||
## v.1.7.0
|
||||
|
||||
- Add possibility to set max altitude in route dialog
|
||||
|
||||
## v.1.6.0
|
||||
|
||||
- Add sound support from internal and external view for C-74 aircraft (thanks to wlbragg)
|
||||
|
||||
## v.1.5.0
|
||||
|
||||
- Add Towrope Configuration dialog
|
||||
|
||||
## v.1.4.1
|
||||
|
||||
- Fix crash by missing parameter
|
||||
|
||||
## v.1.4.0
|
||||
|
||||
- Add Douglas C-47 as a tow plane (thanks to wlbragg)
|
||||
- Add possibility to take off in the bush
|
||||
- Add a separate engine sound for the DR400
|
||||
- Add French translation of meta data (thanks to Florent)
|
||||
- Add possibility to save/load route waypoints with description
|
||||
|
||||
## v.1.3.0
|
||||
|
||||
- Add engine sound for AI aircrafts
|
||||
|
||||
## v.1.2.0
|
||||
|
||||
- Add possibility to change the flight plan of AI tow plane.
|
||||
|
||||
## v.1.1.0
|
||||
|
||||
- Fix AI aircraft going underground during take-off due to uneven runway elevations.
|
||||
|
||||
266
FGData/AI/Aircraft/Aerotow/C-47/Models/c-47-ai.xml
Normal file
@@ -0,0 +1,266 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<description>C-47</description>
|
||||
<path>c-47.ac</path>
|
||||
|
||||
<offsets>
|
||||
<pitch-deg>11.8</pitch-deg>
|
||||
<z-m>2.65</z-m>
|
||||
</offsets>
|
||||
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/C-47/Sound/sound.xml</path>
|
||||
</sound>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>roueG</object-name>
|
||||
<object-name>axeG1</object-name>
|
||||
<object-name>axeG2</object-name>
|
||||
<object-name>axeG3</object-name>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>17.5</dep></entry>
|
||||
<entry><ind>90</ind><dep>35</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.24966</x-m>
|
||||
<y-m>-2.814 </y-m>
|
||||
<z-m>-1.24846</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>translate</type>
|
||||
<object-name>roueG</object-name>
|
||||
<object-name>axeG1</object-name>
|
||||
<object-name>axeG2</object-name>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind> 70 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind>80</ind><dep>.35</dep></entry>
|
||||
<entry><ind>90</ind><dep>.7</dep></entry>
|
||||
</interpolation>
|
||||
<axis>
|
||||
<x> 0 </x>
|
||||
<y> 0 </y>
|
||||
<z> 1 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>-5</dep></entry>
|
||||
<entry><ind>90</ind><dep>-10</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.23614</x-m>
|
||||
<y-m>-2.814</y-m>
|
||||
<z-m>-3.00605</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>roueD</object-name>
|
||||
<object-name>axeD1</object-name>
|
||||
<object-name>axeD2</object-name>
|
||||
<object-name>axeD3</object-name>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>17.5</dep></entry>
|
||||
<entry><ind>90</ind><dep>35</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.24966</x-m>
|
||||
<y-m>-2.814 </y-m>
|
||||
<z-m>-1.40846</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>translate</type>
|
||||
<object-name>roueD</object-name>
|
||||
<object-name>axeD1</object-name>
|
||||
<object-name>axeD2</object-name>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind> 70 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind>80</ind><dep>.35</dep></entry>
|
||||
<entry><ind>90</ind><dep>.7</dep></entry>
|
||||
</interpolation>
|
||||
<axis>
|
||||
<x> 0 </x>
|
||||
<y> 0 </y>
|
||||
<z> 1 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>-5</dep></entry>
|
||||
<entry><ind>90</ind><dep>-10</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.23614</x-m>
|
||||
<y-m> 2.814</y-m>
|
||||
<z-m>-3.00605</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<name>leftprop</name>
|
||||
<object-name>heliceL</object-name>
|
||||
<object-name>propblurL</object-name>
|
||||
<object-name>propdiscL</object-name>
|
||||
</animation>
|
||||
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propblurL</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>100</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propdiskL</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>50</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>heliceL</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>60</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>heliceL</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>10</factor>
|
||||
<center>
|
||||
<x-m>-7.44425</x-m>
|
||||
<y-m>-2.814</y-m>
|
||||
<z-m>-1.189</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x> -1.000 </x>
|
||||
<y> 0.000 </y>
|
||||
<z> 0.000 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<name>rightprop</name>
|
||||
<object-name>heliceR</object-name>
|
||||
<object-name>propblurR</object-name>
|
||||
<object-name>propdiscR</object-name>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propblurR</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>100</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propdiskR</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>50</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>heliceR</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>60</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>heliceR</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor> 10 </factor>
|
||||
<center>
|
||||
<x-m>-7.44425</x-m>
|
||||
<y-m> 2.814</y-m>
|
||||
<z-m>-1.189</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x> 1.000 </x>
|
||||
<y> 0.000 </y>
|
||||
<z> 0.000 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
423789
FGData/AI/Aircraft/Aerotow/C-47/Models/c-47.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Models/prop.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
FGData/AI/Aircraft/Aerotow/C-47/Models/texture.png
Normal file
|
After Width: | Height: | Size: 728 KiB |
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_ext_2.wav
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_int_2.wav
Normal file
53
FGData/AI/Aircraft/Aerotow/C-47/Sound/sound.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engineINT</name>
|
||||
<mode>looped</mode>
|
||||
<path>Croisiere_int_2.wav</path>
|
||||
<condition>
|
||||
<and>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
<property>/sim/current-view/internal</property>
|
||||
</and>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.80</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
<engine>
|
||||
<name>engineEXT</name>
|
||||
<mode>looped</mode>
|
||||
<path>Croisiere_ext_2.wav</path>
|
||||
<condition>
|
||||
<and>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
<not>
|
||||
<property>/sim/current-view/internal</property>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.80</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
7
FGData/AI/Aircraft/Aerotow/Cub/Models/Cub-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/Cub/Models/Cub-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
66879
FGData/AI/Aircraft/Aerotow/Cub/Models/Cub.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/placards.png
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/propeller.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/propeller_slow.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/texture.png
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
7
FGData/AI/Aircraft/Aerotow/DR400/Models/dr400-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/DR400/Models/dr400-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/DR400/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
44494
FGData/AI/Aircraft/Aerotow/DR400/Models/dr400.ac
Normal file
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/GreenLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.948961138725 0.442461937666
|
||||
1 0.563538074493 0.442461848259
|
||||
2 0.563538074493 0.0570387989283
|
||||
3 0.948961138725 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.948961257935 0.0570388287306
|
||||
6 0.948961257935 0.442461937666
|
||||
5 0.563538074493 0.442461937666
|
||||
4 0.563538074493 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.948961257935 0.442461937666
|
||||
10 0.563538193703 0.442461937666
|
||||
9 0.563538074493 0.0570388287306
|
||||
8 0.948961257935 0.0570387542248
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/GreenLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>GreenLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/RedLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.448961138725 0.442461937666
|
||||
1 0.0635380744934 0.442461848259
|
||||
2 0.0635380744934 0.0570387989283
|
||||
3 0.448961138725 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.448961257935 0.0570388287306
|
||||
6 0.448961257935 0.442461937666
|
||||
5 0.0635380744934 0.442461937666
|
||||
4 0.0635380744934 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.448961257935 0.442461937666
|
||||
10 0.0635381937027 0.442461937666
|
||||
9 0.0635380744934 0.0570388287306
|
||||
8 0.448961257935 0.0570387542248
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/RedLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>RedLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>WhiteLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>toggle-white.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Cube</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Cube</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/WhiteLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.943492412567 0.942461967468
|
||||
1 0.558069348335 0.942461848259
|
||||
2 0.558069348335 0.557038784027
|
||||
3 0.943492412567 0.557038784027
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.943492531776 0.557038843632
|
||||
6 0.943492531776 0.942461967468
|
||||
5 0.558069348335 0.942461967468
|
||||
4 0.558069348335 0.557038784027
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.943492531776 0.942461967468
|
||||
10 0.558069467545 0.942461967468
|
||||
9 0.558069348335 0.557038843632
|
||||
8 0.943492531776 0.557038784027
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/WhiteLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>WhiteLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/beacon.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>RedLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
3268
FGData/AI/Aircraft/Aerotow/DR400/Models/light/light-cone.ac
Normal file
12
FGData/AI/Aircraft/Aerotow/DR400/Models/light/light-cone.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>light-cone.ac</path>
|
||||
|
||||
<effect>
|
||||
<inherits-from>Effects/light-cone</inherits-from>
|
||||
<object-name>Cone</object-name>
|
||||
</effect>
|
||||
|
||||
</PropertyList>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/light/lights.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,95 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "Material" rgb 0.92013 0.92013 0.92013 amb 0.5 0.5 0.5 emis 0 0 0 spec 1 1 1 shi 32 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 4
|
||||
Cube
|
||||
crease 30
|
||||
numvert 12
|
||||
0.009182 0.007635 -0.00748
|
||||
0.001377 -0.000598 -0.00748
|
||||
-0.005892 0.002033 -0.00748
|
||||
-0.001976 0.012793 -0.00748
|
||||
0.009182 0.007635 0.00748
|
||||
0.001377 -0.000598 0.00748
|
||||
-0.005892 0.002033 0.00748
|
||||
-0.001976 0.012793 0.00748
|
||||
-0.009655 -0.008307 -0.00748
|
||||
-0.009655 -0.008307 0.00748
|
||||
0.002203 -0.01154 -0.00748
|
||||
0.002203 -0.01154 0.00748
|
||||
numsurf 10
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
4 0 0
|
||||
7 0 0
|
||||
6 0 0
|
||||
5 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
4 0 0
|
||||
5 0 0
|
||||
1 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
6 0 0
|
||||
7 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
6 0 0
|
||||
2 0 0
|
||||
8 0 0
|
||||
9 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
1 0 0
|
||||
5 0 0
|
||||
11 0 0
|
||||
10 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
5 0 0
|
||||
6 0 0
|
||||
9 0 0
|
||||
11 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
1 0 0
|
||||
10 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
10 0 0
|
||||
11 0 0
|
||||
9 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
3 0 0
|
||||
7 0 0
|
||||
4 0 0
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/prop.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/texture.png
Normal file
|
After Width: | Height: | Size: 361 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/texture.xcf.gz
Normal file
BIN
FGData/AI/Aircraft/Aerotow/DR400/Sound/DR400-engine.wav
Normal file
25
FGData/AI/Aircraft/Aerotow/DR400/Sound/sound.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engine</name>
|
||||
<mode>looped</mode>
|
||||
<path>DR400-engine.wav</path>
|
||||
<condition>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.95</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
25
FGData/AI/Aircraft/Aerotow/Sound/sound.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engine</name>
|
||||
<mode>looped</mode>
|
||||
<path>wasp.wav</path>
|
||||
<condition>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.80</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
BIN
FGData/AI/Aircraft/Aerotow/Sound/wasp.wav
Normal file
53
FGData/AI/Aircraft/Aerotow/c182/Models/BeaconLight.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>WhiteLight1.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red>1.0</red>
|
||||
<green>0.0</green>
|
||||
<blue>0.0</blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Light</object-name>
|
||||
<condition>
|
||||
<and>
|
||||
<greater-than>
|
||||
<property>/sim/time/sun-angle-rad</property>
|
||||
<value>1.57</value>
|
||||
</greater-than>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>0</value>
|
||||
</greater-than>
|
||||
</and>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.25</dep></entry>
|
||||
<entry><ind>20</ind><dep>0.50</dep></entry>
|
||||
<entry><ind>1500</ind><dep>5</dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/Halos.rgb
Normal file
35
FGData/AI/Aircraft/Aerotow/c182/Models/StrobeLight.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>WhiteLight1.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
35
FGData/AI/Aircraft/Aerotow/c182/Models/ToggleLight.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>toggle-white.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Cube</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Cube</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
44
FGData/AI/Aircraft/Aerotow/c182/Models/WhiteLight1.ac
Normal file
@@ -0,0 +1,44 @@
|
||||
AC3Db
|
||||
MATERIAL "ac3dmat1" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0 0 0 shi 10 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
texture "Halos.rgb"
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 2.23517e-08
|
||||
-0.5 0.5 -2.23517e-08
|
||||
-0.5 -0.5 -2.23517e-08
|
||||
0.5 -0.5 2.23517e-08
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 -4.96048e-10 -0.5
|
||||
-0.5 4.42074e-08 -0.5
|
||||
-0.5 4.96048e-10 0.5
|
||||
0.5 -4.42074e-08 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
0 0 0.5
|
||||
1 0.5 0.5
|
||||
2 0.5 0
|
||||
3 0 0
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
7 0 0.5
|
||||
6 0.5 0.5
|
||||
5 0.5 0
|
||||
4 0 0
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
11 0 0.5
|
||||
10 0.5 0.5
|
||||
9 0.5 0
|
||||
8 0 0
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/c182-01.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/c182-02.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
7
FGData/AI/Aircraft/Aerotow/c182/Models/c182-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/c182/Models/c182-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
217
FGData/AI/Aircraft/Aerotow/c182/Models/c182-dpm.xml
Normal file
@@ -0,0 +1,217 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>c182.ac</path>
|
||||
|
||||
|
||||
<!-- Object movements -->
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Propeller</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<value>500</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<value>400</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>blend</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>0.0004</factor>
|
||||
<max>1.0</max>
|
||||
<min>0.0</min>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>NoseWheel</object-name>
|
||||
<property>surface-positions/rudder-pos-norm</property>
|
||||
<factor>-20</factor>
|
||||
<center>
|
||||
<x-m>-0.8</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>-0.8</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<z>1</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>Propeller</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>-1</factor>
|
||||
<center>
|
||||
<x-m>0</x-m>
|
||||
<y-m>0</y-m>
|
||||
<z-m>-0.214544</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>1.0</x>
|
||||
<y>0.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>-1</factor>
|
||||
<center>
|
||||
<x-m>0</x-m>
|
||||
<y-m>0</y-m>
|
||||
<z-m>-0.214544</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>1.0</x>
|
||||
<y>0.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftAileron</object-name>
|
||||
<property>surface-positions/left-aileron-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>15</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0.0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>-20</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>0.94</x-m>
|
||||
<y-m>-3.83</y-m>
|
||||
<z-m>0.40</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>-0.15</x>
|
||||
<y>-1.00</y>
|
||||
<z>0.03</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>RightAileron</object-name>
|
||||
<property>surface-positions/left-aileron-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>15</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0.0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>-20</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>0.94</x-m>
|
||||
<y-m>3.83</y-m>
|
||||
<z-m>0.40</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>-0.15</x>
|
||||
<y>1.00</y>
|
||||
<z>0.03</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftFlap</object-name>
|
||||
<object-name>RightFlap</object-name>
|
||||
<property>surface-positions/flap-pos-norm</property>
|
||||
<factor>30</factor>
|
||||
<center>
|
||||
<x-m>1.1</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>0.28</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.0</x>
|
||||
<y>1.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftElevator</object-name>
|
||||
<object-name>RightElevator</object-name>
|
||||
<property>surface-positions/elevator-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>-28</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>23</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>5.1</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>-0.45</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.0</x>
|
||||
<y>1.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>Rudder</object-name>
|
||||
<property>surface-positions/rudder-pos-norm</property>
|
||||
<factor>17.5</factor>
|
||||
<center>
|
||||
<x-m>5.45</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>0.0</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.72</x>
|
||||
<y>0.0</y>
|
||||
<z>1.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
8778
FGData/AI/Aircraft/Aerotow/c182/Models/c182.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/chrome2.rgb
Normal file
64
FGData/AI/Aircraft/Aerotow/c182/Models/lights.ac
Normal file
@@ -0,0 +1,64 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 8
|
||||
Cube.002
|
||||
texture "lights.rgb"
|
||||
texrep 1 1
|
||||
crease 30
|
||||
numvert 8
|
||||
5.2e-005 -0.024954 -0.09
|
||||
5.2e-005 -0.024954 0.173732
|
||||
-0.027131 -0.024954 0.173679
|
||||
-0.027131 -0.024954 -0.09
|
||||
5.2e-005 0.047045 -0.09
|
||||
5.2e-005 0.047045 0.173732
|
||||
-0.027131 0.047045 0.173679
|
||||
-0.027131 0.047045 -0.09
|
||||
numsurf 6
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
0 0.0462104454637 0.277649432421
|
||||
1 0.0462104454637 0.0592489615083
|
||||
2 0.264610916376 0.0592489615083
|
||||
3 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
4 0.0462104454637 0.277649432421
|
||||
7 0.0462104454637 0.0592489615083
|
||||
6 0.264610916376 0.0592489615083
|
||||
5 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
0 0.994239091873 0.706083416939
|
||||
4 0.994238674641 0.996782541275
|
||||
5 0.00671458244324 0.996782541275
|
||||
1 0.00671491026878 0.706083416939
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
1 0.0462104454637 0.277649432421
|
||||
5 0.0462104454637 0.0592489615083
|
||||
6 0.264610916376 0.0592489615083
|
||||
2 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
2 0.0 1.0
|
||||
6 0.0 0.0
|
||||
7 1.0 0.0
|
||||
3 1.0 1.0
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
4 0.0462104454637 0.277649432421
|
||||
0 0.0462104454637 0.0592489615083
|
||||
3 0.264610916376 0.0592489615083
|
||||
7 0.264610916376 0.277649432421
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/lights.rgb
Normal file
95
FGData/AI/Aircraft/Aerotow/c182/Models/toggle-white.ac
Normal file
@@ -0,0 +1,95 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "Material" rgb 0.92013 0.92013 0.92013 amb 0.5 0.5 0.5 emis 0 0 0 spec 1 1 1 shi 32 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 4
|
||||
Cube
|
||||
crease 30
|
||||
numvert 12
|
||||
0.009182 0.007635 -0.00748
|
||||
0.001377 -0.000598 -0.00748
|
||||
-0.005892 0.002033 -0.00748
|
||||
-0.001976 0.012793 -0.00748
|
||||
0.009182 0.007635 0.00748
|
||||
0.001377 -0.000598 0.00748
|
||||
-0.005892 0.002033 0.00748
|
||||
-0.001976 0.012793 0.00748
|
||||
-0.009655 -0.008307 -0.00748
|
||||
-0.009655 -0.008307 0.00748
|
||||
0.002203 -0.01154 -0.00748
|
||||
0.002203 -0.01154 0.00748
|
||||
numsurf 10
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
4 0 0
|
||||
7 0 0
|
||||
6 0 0
|
||||
5 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
4 0 0
|
||||
5 0 0
|
||||
1 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
6 0 0
|
||||
7 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
6 0 0
|
||||
2 0 0
|
||||
8 0 0
|
||||
9 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
1 0 0
|
||||
5 0 0
|
||||
11 0 0
|
||||
10 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
5 0 0
|
||||
6 0 0
|
||||
9 0 0
|
||||
11 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
1 0 0
|
||||
10 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
10 0 0
|
||||
11 0 0
|
||||
9 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
3 0 0
|
||||
7 0 0
|
||||
4 0 0
|
||||
kids 0
|
||||
84
README.md
@@ -24,23 +24,83 @@ Start FlightGear at any airport with your aircraft as a glider, such as ASK 21.
|
||||
|
||||
From the top menu, select `Aerotow Everywhere` -> `Call for Piper J3 Cub aircraft`, `Robin DR400` or `Cessna 182` (yes, you can choose from many aircrafts). The AI aircraft will appear to your right and align to the centreline of the runway in front of you. At this time you should hook up to the aircraft, most often by pressing the `Ctrl-o` key (check help of your glider). The AI aircraft will begin to accelerate and take off.
|
||||
|
||||
## How does the AI tow aircraft fly?
|
||||
You can also take off in the bush. Then the tow plane will position itself in front of you (glider's course), so what heading you have is important. If you move away from the runway threshold further than 100 m, then the take-off of the tow plane will be as in the bush, i.e. according to the glider heading and not the runway.
|
||||
|
||||
The tow plane always takes off in front of your runway and flies along the runway for 5 km, then turns back and flies downwind for 6 km, then turns back again and flies another 6 km. During this flight it is constantly gaining altitude. Then when it has completed the entire set route it simply turns right 90 degrees and flies at a constant altitude.
|
||||
## How does the AI tow aircraft fly by default?
|
||||
|
||||
You can disconnect from the aircraft at any time, most often by pressing the `o` key.
|
||||
The tow plane always takes off in front of your runway and flies along the runway for 5 km, then turns back and flies downwind for 6 km, then turns back again and flies another 6 km. During this flight it is constantly gaining altitude. Then, after having flown the entire given route, it lands at the airport from which it took off or when start in the bush turns in an unknown direction and flies at a constant altitude. If the aircraft lands at the airport, the scenario starts again, i.e. the aircraft respawn to take-off again.
|
||||
|
||||
You can disconnect from the aircraft at any time, most often by pressing the `o` key (check help of your glider).
|
||||
|
||||
```
|
||||
Default route
|
||||
^ - airport with heading direction to north
|
||||
1 - 1st waypoint
|
||||
2 - 2nd waypoint, etc.
|
||||
|
||||
2 . . 1 5
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
. ^ .
|
||||
. .
|
||||
. .
|
||||
3 . . . . 4
|
||||
```
|
||||
|
||||
## Menu of add-on
|
||||
|
||||
This add-on add a new item to main menu named `Aerotow Everywhere` with following items:
|
||||
|
||||
1. `Call for Piper J3 Cub aircraft` - load AI tow sceneraio with Piper J3 Cub. Possible altitude to reach ~3,600 ft.
|
||||
2. `Call for Robin DR400 aircraft` - load AI tow sceneraio with Robin DR400. This aircraft has better performance and can take you to over 4,500 ft.
|
||||
3. `Call for Cessna 182 aircraft` - load AI tow sceneraio with Cessna 182. This aircraft has little bit better performance than Robin.
|
||||
4. `Disable tow aircraft` - unload AI tow sceneraio.
|
||||
5. `Add thermal` - display the dialog for configuring and adding thermal.
|
||||
6. `Help` - display help dialog.
|
||||
7. `About` - display about dialog with add-on information.
|
||||
1. `Aerotow Route` - display the dialog for change aerotow route.
|
||||
2. `Towrope Configuration` - display the dialog for change towrope parameters.
|
||||
3. `Call for Piper J3 Cub aircraft` - load AI tow sceneraio with Piper J3 Cub.
|
||||
4. `Call for Robin DR400 aircraft` - load AI tow sceneraio with Robin DR400. This aircraft has better performance and can take you higher then Piper Cub.
|
||||
5. `Call for Cessna 182 aircraft` - load AI tow sceneraio with Cessna 182. This aircraft has little bit better performance than Robin.
|
||||
6. `Call for Douglas C-47 aircraft` - load AI tow sceneraio with Douglas C-47.
|
||||
7. `Disable tow aircraft` - unload AI tow sceneraio.
|
||||
8. `Add thermal` - display the dialog for configuring and adding thermal.
|
||||
9. `Help` - display help dialog.
|
||||
10. `About` - display about dialog with add-on information.
|
||||
|
||||
## Aerotow Route
|
||||
|
||||
You can change the AI aircraft's default route, for this go to menu `Aerotow Everywhere` -> `Aerotow Route`.
|
||||
|
||||
### Aerotow aircraft
|
||||
|
||||
On the top of the "Aerotow Route" dialog you have selector to change aircraft type. It's only for calculate performance and display how the altitude will change.
|
||||
|
||||
### Max alt (AGL)
|
||||
|
||||
Here you can set limit of AGL altitude. 0 means without limits - the plane will always increase its altitude during its flight. AGL is always in terms of the place you are starting from.
|
||||
|
||||
### Route
|
||||
|
||||
Next you have initial heading, distance and altitude change. "Initial heading" depend of the runway where you are located. The aircraft will always take-off along the runway, a distance of 100 m, where it will gain 20 ft in altitude above the terrain. These parameters you cannot change unless you change the airport/runway.
|
||||
|
||||
Next you can see a table with fields where you can change values. Each row represents a single waypoint of flight plan of AI aircraft. For each waypoint you have following columns:
|
||||
|
||||
1. `Heading change (deg)` - information on how the heading of the aircraft should change in relation to the previous one. A value of `0` means no change, so continue with the same heading. And e.g. `-90` means a left turn of 90 degrees, `60` means a right turn of 60 degrees, etc.
|
||||
2. `Distance (m)` - distance in meters from the previous waypoint to the present one.
|
||||
3. `Alt change (AGL ft)` - information on how much the altitude on this leg of the route will increase (in feet above ground.) This information depend of selected airplane. AGL is always in terms of the place you are starting from (not the waypoint place).
|
||||
|
||||
If you enter a value less than or equal to `0` for `Distance (m)` field this means that here the route is end and the next rows will not be included to the flight plan.
|
||||
|
||||
You have a maximum of 10 waypoints to use, hope that's enough.
|
||||
|
||||
On the below of the dialog you can see a total amount of distance (in meters) and total altitude in feet above ground level.
|
||||
|
||||
### Buttons
|
||||
|
||||
1. `OK` - close the route dialog.
|
||||
2. `Default` - set default waypoints (your changes will be lost.)
|
||||
|
||||
## Adding thermals
|
||||
|
||||
@@ -53,11 +113,11 @@ An additional feature of this add-on is the possibility of placing the thermals
|
||||
|
||||
Click `Add thermal` button for add the thermal.
|
||||
|
||||
Many thanks to the forum user "wlbragg" for proposing and presenting a solution to this feature.
|
||||
Many thanks to the FG forum user "wlbragg" for proposing and presenting a solution to this feature.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. This add-on doesn't check if there are any obstacles in the AI aircraft's path, e.g. terrain, buildings, power lines, etc. Keep this in mind when choosing an airport.
|
||||
1. This add-on doesn't check if there are any obstacles in the AI aircraft's path, e.g. terrain, buildings, power lines, etc. Keep this in mind when choosing an airport or planning your route.
|
||||
2. Minimum FlightGear version: 2020.4.0 (dev/nightly). Because only nightly version is able to search for flight plans in additional FGData folders added by `--data` command line option.
|
||||
|
||||
## Troubleshotting
|
||||
|
||||
@@ -37,15 +37,36 @@
|
||||
<height-msl type="int">9000</height-msl>
|
||||
<height-msl-m type="int">2742</height-msl-m>
|
||||
</add-thermal>
|
||||
<route>
|
||||
<ai-model>Piper J3 Cub</ai-model>
|
||||
<init-wpt>
|
||||
<heading-change type="double">0</heading-change>
|
||||
<distance-m type="double">0</distance-m>
|
||||
<alt-change-agl-ft type="double">0</alt-change-agl-ft>
|
||||
</init-wpt>
|
||||
<wpts>
|
||||
<description type="string">Default route around the start location</description>
|
||||
<max-alt-agl type="double"></max-alt-agl>
|
||||
</wpts>
|
||||
<total>
|
||||
<distance>0</distance>
|
||||
<alt>0</alt>
|
||||
</total>
|
||||
</route>
|
||||
<sound>
|
||||
<enable type="bool">false</enable>
|
||||
</sound>
|
||||
<help-text type="string">=== How to start? ===
|
||||
|
||||
From the top menu, select "Aerotow Everywhere" -> "Call for Piper J3 Cub aircraft", "Robin DR400" or "Cessna 182" (yes, you can choose from many aircrafts).
|
||||
The tow aircraft will appear to your right and align to the centreline of the runway in front of you. At this time you should hook up to the aircraft, most often by `Ctrl-o` key (check help of your glider). The aircraft will begin to accelerate and take off.
|
||||
|
||||
The tow plane always takes off in front of your runway and flies along the runway for 5 km, then turns back and flies downwind for 6 km, then turns back again and flies another 6 km. During this flight it is constantly gaining altitude. Then when it has completed the entire set route it simply turns right 90 degrees and flies at a constant altitude.
|
||||
By default, the tow plane always takes off in front of your runway and flies along the runway for 5 km, then turns back and flies downwind for 6 km, then turns back again and flies another 6 km. During this flight it is constantly gaining altitude. Then when it has completed the entire set route it simply turns right 90 degrees and flies at a constant altitude.
|
||||
|
||||
You can disconnect from the aircraft at any time, most often by pressing the `o` key.
|
||||
|
||||
Before start you can change route of AI aircraft by menu "Aerotow Everywhere" -> "Aerotow Route".
|
||||
|
||||
=== Troubleshotting ===
|
||||
|
||||
1. When I select "Aerotow Everywhere" -> "Call for Piper J3 Cub aircraft", "Robin DR400" or "Cessna 182" from menu, I see "Let's fly!" message but nothing happened. The tow plane does not appear.
|
||||
|
||||
@@ -23,12 +23,51 @@ var unload = func(addon) {
|
||||
# e.g. myCanvas.del();
|
||||
|
||||
aerotow.uninit();
|
||||
thermal.uninit();
|
||||
}
|
||||
|
||||
var main = func(addon) {
|
||||
print("Aerotow Everywhere add-on initialized from path ", addon.basePath);
|
||||
|
||||
loadExtraNasalFiles(addon);
|
||||
|
||||
createDirectories(addon);
|
||||
|
||||
aerotow.init(addon);
|
||||
}
|
||||
|
||||
#
|
||||
# Load extra Nasal files in main add-on directory
|
||||
#
|
||||
# addon - Addob object
|
||||
#
|
||||
var loadExtraNasalFiles = func (addon) {
|
||||
var modules = [
|
||||
"nasal/timer",
|
||||
"nasal/aircraft",
|
||||
"nasal/message",
|
||||
"nasal/dialogs/route",
|
||||
"nasal/dialogs/thermal",
|
||||
"nasal/flight-plan",
|
||||
"nasal/scenario",
|
||||
"nasal/io/waypoint",
|
||||
"nasal/io/flight-plan-writer",
|
||||
"nasal/aerotow",
|
||||
"aerotow",
|
||||
];
|
||||
|
||||
foreach (var scriptName; modules) {
|
||||
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
|
||||
|
||||
if (io.load_nasal(fileName, "aerotow")) {
|
||||
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Create all needed directories.
|
||||
#
|
||||
var createDirectories = func (addon) {
|
||||
# Create $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow directory
|
||||
addon.createStorageDir();
|
||||
|
||||
@@ -38,21 +77,7 @@ var main = func(addon) {
|
||||
var path = os.path.new(addon.storagePath ~ "/AI/FlightPlans/dummy-file.txt");
|
||||
path.create_dir();
|
||||
|
||||
loadExtraNasalFiles(addon);
|
||||
|
||||
aerotow.init();
|
||||
thermal.init();
|
||||
}
|
||||
|
||||
#
|
||||
# Load extra Nasal files in main add-on directory
|
||||
#
|
||||
var loadExtraNasalFiles = func (addon) {
|
||||
foreach (var scriptName; ["aerotow", "messages", "thermal"]) {
|
||||
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
|
||||
|
||||
if (io.load_nasal(fileName, scriptName)) {
|
||||
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
|
||||
}
|
||||
}
|
||||
# Create /route-saves directory in $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/
|
||||
path = os.path.new(addon.storagePath ~ "/" ~ aerotow.RouteDialog.ROUTE_SAVES_DIR ~ "/dummy-file.txt");
|
||||
path.create_dir();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,20 @@
|
||||
<label>Aerotow Everywhere</label>
|
||||
<enabled type="bool">true</enabled>
|
||||
|
||||
<item>
|
||||
<label>Aerotow Route</label>
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>route-aerotow</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Towrope Configuration</label>
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>towrope-config</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Call for Piper J3 Cub aircraft</label>
|
||||
<binding>
|
||||
@@ -53,11 +67,29 @@
|
||||
<value type="string">c182</value>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Call for Douglas C-47 aircraft</label>
|
||||
<binding>
|
||||
<command>property-assign</command>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/ai-model</property>
|
||||
<value type="string">C47</value>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Disable tow aircraft</label>
|
||||
<binding>
|
||||
<command>property-assign</command>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
<value type="bool">false</value>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[aerotow.stopAerotow();]]></script>
|
||||
<script><![CDATA[
|
||||
# Run stopAerotow() with a delay to ensure that the engine sound turns off
|
||||
aerotow.Timer.new().singleShot(1, aerotow.g_Aerotow, func () {
|
||||
aerotow.g_Aerotow.stopAerotow();
|
||||
});
|
||||
]]></script>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<addon>
|
||||
<identifier type="string">org.flightgear.addons.Aerotow</identifier>
|
||||
<name type="string">Aerotow Everywhere</name>
|
||||
<version type="string">1.1.0</version>
|
||||
<version type="string">1.8.1</version>
|
||||
|
||||
<authors>
|
||||
<author>
|
||||
@@ -49,6 +49,16 @@
|
||||
</long-description>
|
||||
|
||||
<localized>
|
||||
<fr>
|
||||
<short-description type="string">
|
||||
Remorquage d’un planeur depuis n’importe quel aérodrome.
|
||||
</short-description>
|
||||
|
||||
<long-description type="string">
|
||||
Ce greffon permet d’activer le scénario IA « avion remorqueur » pour démarrer avec un planeur depuis n’importe quel aérodrome.
|
||||
</long-description>
|
||||
</fr>
|
||||
|
||||
<pl>
|
||||
<short-description type="string">
|
||||
Samoloty holownicze dla szybowców na każdym lotnisku.
|
||||
|
||||
526
aerotow.nas
@@ -10,529 +10,31 @@
|
||||
#
|
||||
|
||||
#
|
||||
# Constants
|
||||
# Global aliases for boolean types to distinguish the use of "int" from "bool".
|
||||
# NOTE: unfortunately, it doesn't work as an assignment of a default value for a function parameter!
|
||||
#
|
||||
var addon = addons.getAddon("org.flightgear.addons.Aerotow");
|
||||
var FILENAME_SCENARIO = "aerotown-addon.xml";
|
||||
var FILENAME_FLIGHTPLAN = "aerotown-addon-flightplan.xml";
|
||||
var PATH_SCENARIO = addon.storagePath ~ "/" ~ FILENAME_SCENARIO;
|
||||
var PATH_FLIGHTPLAN = addon.storagePath ~ "/AI/FlightPlans/" ~ FILENAME_FLIGHTPLAN;
|
||||
var SCENARIO_ID = "aerotow_addon";
|
||||
var SCENARIO_NAME = "Aerotow Add-on";
|
||||
var SCENARIO_DESC = "This scenario starts the towing plane at the airport where the pilot with the glider is located. Use Ctrl-o to hook the plane.";
|
||||
var true = 1;
|
||||
var false = 0;
|
||||
|
||||
#
|
||||
# Global variables
|
||||
# Global object of Aerotow
|
||||
#
|
||||
var g_wptCount = 0;
|
||||
var g_isScenarioLoaded = 0;
|
||||
var g_fpFileHandler = nil; # Handler for wrire flight plan to file
|
||||
var g_coord = nil; # Coordinates for flight plan
|
||||
var g_heading = nil; # AI plane heading
|
||||
var g_altitude = nil; # AI plane altitude
|
||||
var g_listeners = [];
|
||||
var g_Aerotow = nil;
|
||||
|
||||
#
|
||||
# Initialize thermal module
|
||||
# Initialize Aerotow
|
||||
#
|
||||
var init = func () {
|
||||
# Listener for ai-model property triggered when the user select a tow aircraft from add-on menu
|
||||
append(g_listeners, setlistener(addon.node.getPath() ~ "/addon-devel/ai-model", func () {
|
||||
startAerotow();
|
||||
}));
|
||||
# addon - Addon object
|
||||
#
|
||||
var init = func (addon) {
|
||||
g_Aerotow = Aerotow.new(addon);
|
||||
}
|
||||
|
||||
#
|
||||
# Uninitialize thermal module
|
||||
# Uninitialize Aerotow
|
||||
#
|
||||
var uninit = func () {
|
||||
foreach (var listener; g_listeners) {
|
||||
removelistener(listener);
|
||||
if (g_Aerotow) {
|
||||
g_Aerotow.del();
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Main function to prepare AI scenario and run it.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var startAerotow = func () {
|
||||
var args = props.Node.new({ "name": SCENARIO_ID });
|
||||
if (g_isScenarioLoaded) {
|
||||
fgcommand("unload-scenario", args);
|
||||
}
|
||||
|
||||
generateScenarioXml();
|
||||
|
||||
if (!generateFlightPlanXml()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fgcommand("load-scenario", args)) {
|
||||
g_isScenarioLoaded = 1;
|
||||
messages.displayOk("Let's fly!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Tow failed!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Function for unload our AI scenario.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var stopAerotow = func () {
|
||||
if (g_isScenarioLoaded) {
|
||||
var args = props.Node.new({ "name": SCENARIO_ID });
|
||||
if (fgcommand("unload-scenario", args)) {
|
||||
g_isScenarioLoaded = 0;
|
||||
|
||||
messages.displayOk("Aerotown disabled");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Aerotown disable failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
messages.displayOk("Aerotown already disabled");
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Generate the XML file with the AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/aerotown-addon.xml.
|
||||
#
|
||||
var generateScenarioXml = func () {
|
||||
var scenarioXml = {
|
||||
"PropertyList": {
|
||||
"scenario": {
|
||||
"name": SCENARIO_NAME,
|
||||
"description": SCENARIO_DESC,
|
||||
"entry": {
|
||||
"callsign": "FG-TOW",
|
||||
"type": "aircraft",
|
||||
"class": "aerotow-dragger",
|
||||
"model": "Aircraft/Cub/Models/Cub-ai.xml", # default Cub
|
||||
"flightplan": FILENAME_FLIGHTPLAN,
|
||||
"repeat": 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var aiModel = getSelectedAircraft();
|
||||
if (aiModel == "DR400") {
|
||||
scenarioXml.PropertyList.scenario.entry.model = "Aircraft/DR400/Models/dr400-ai.xml";
|
||||
}
|
||||
else if (aiModel == "c182") {
|
||||
scenarioXml.PropertyList.scenario.entry.model = "Aircraft/c182/Models/c182-ai.xml";
|
||||
}
|
||||
|
||||
var node = props.Node.new(scenarioXml);
|
||||
io.writexml(PATH_SCENARIO, node);
|
||||
|
||||
addScenarioToPropertyList();
|
||||
}
|
||||
|
||||
#
|
||||
# Add our new scenario to the "/sim/ai/scenarios" property list
|
||||
# so that FlightGear will be able to load it by "load-scenario" command.
|
||||
#
|
||||
var addScenarioToPropertyList = func () {
|
||||
if (!isScenarioAdded()) {
|
||||
var scenarioData = {
|
||||
"name": SCENARIO_NAME,
|
||||
"id": SCENARIO_ID,
|
||||
"description": SCENARIO_DESC,
|
||||
"path": PATH_SCENARIO,
|
||||
};
|
||||
|
||||
props.globals.getNode("/sim/ai/scenarios").addChild("scenario").setValues(scenarioData);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Return 1 if scenario is already added to "/sim/ai/scenarios" property list, otherwise return 0.
|
||||
#
|
||||
var isScenarioAdded = func () {
|
||||
foreach (var scenario; props.globals.getNode("/sim/ai/scenarios").getChildren("scenario")) {
|
||||
var id = scenario.getChild("id");
|
||||
if (id != nil and id.getValue() == SCENARIO_ID) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Return name of selected aircraft. Possible values: "Cub", "DR400", "c182".
|
||||
#
|
||||
var getSelectedAircraft = func () {
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/ai-model") or "Cub";
|
||||
}
|
||||
|
||||
#
|
||||
# Generate the XML file with the flight plane for our plane for AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/aerotown-addon-flightplan.xml.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var generateFlightPlanXml = func () {
|
||||
g_wptCount = 0;
|
||||
|
||||
var icao = getprop("/sim/airport/closest-airport-id");
|
||||
if (icao == nil) {
|
||||
messages.displayError("Airport code cannot be obtained.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var runwayName = getprop("/sim/atc/runway");
|
||||
if (runwayName == nil) {
|
||||
messages.displayError("Runway name cannot be obtained.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var airport = airportinfo(icao);
|
||||
|
||||
if (!contains(airport.runways, runwayName)) {
|
||||
messages.displayError("The " ~ icao ~" airport does not have runway " ~ runwayName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var runway = airport.runways[runwayName];
|
||||
|
||||
var minRwyLength = getMinRunwayLength();
|
||||
if (runway.length < minRwyLength) {
|
||||
messages.displayError(
|
||||
"This runway is too short. Please choose a longer one than " ~ minRwyLength ~ " m "
|
||||
~ "(" ~ math.round(minRwyLength * globals.M2FT) ~ " ft)."
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_fpFileHandler = io.open(PATH_FLIGHTPLAN, "w");
|
||||
io.write(
|
||||
g_fpFileHandler,
|
||||
"<?xml version=\"1.0\"?>\n\n" ~
|
||||
"<!-- This file is generated automatically by the Aerotow Everywhere add-on -->\n\n" ~
|
||||
"<PropertyList>\n" ~
|
||||
" <flightplan>\n"
|
||||
);
|
||||
|
||||
var perf = getAircraftPerformance();
|
||||
|
||||
initAircraftVariable(airport, runway, 1);
|
||||
|
||||
# Start at 2 o'clock from the glider...
|
||||
# Inital ktas must be >= 1.0
|
||||
addWptGround({"hdgChange": 60, "dist": 25}, {"altChange": 0, "ktas": 5});
|
||||
|
||||
# Reset coord and heading
|
||||
initAircraftVariable(airport, runway, 0);
|
||||
|
||||
var gliderOffsetM = getGliderOffsetFromRunwayThreshold(runway);
|
||||
|
||||
# ... and line up with the runway
|
||||
addWptGround({"hdgChange": 0, "dist": 30 + gliderOffsetM}, {"altChange": 0, "ktas": 2.5});
|
||||
|
||||
# Rolling
|
||||
addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": perf.speed / 6});
|
||||
addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": perf.speed / 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 4});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 3.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 3});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 2.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 2});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.75});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.25});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed});
|
||||
|
||||
# Takeof
|
||||
addWptAir({"hdgChange": 0, "dist": 100 * perf.rolling}, {"elevationPlus": 3, "ktas": perf.speed * 1.05});
|
||||
addWptAir({"hdgChange": 0, "dist": 100}, {"altChange": perf.vs / 10, "ktas": perf.speed * 1.025});
|
||||
|
||||
# Circle around airport
|
||||
addWptAir({"hdgChange": 0, "dist": 500}, {"altChange": perf.vs / 1.5, "ktas": perf.speed});
|
||||
addWptAir({"hdgChange": 0, "dist": 500}, {"altChange": perf.vs / 1.7, "ktas": perf.speed});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.025});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.05});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.075});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.1});
|
||||
addWptAir({"hdgChange": -90, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.125}); # crosswind leg
|
||||
addWptAir({"hdgChange": -90, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.15}); # downwind leg
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.175});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.2});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.225});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.25});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.275});
|
||||
addWptAir({"hdgChange": -90, "dist": 500}, {"altChange": perf.vs / 2, "ktas": perf.speed * 1.25}); # base leg
|
||||
addWptAir({"hdgChange": 0, "dist": 500}, {"altChange": perf.vs / 2, "ktas": perf.speed * 1.275});
|
||||
addWptAir({"hdgChange": 0, "dist": 500}, {"altChange": perf.vs / 2, "ktas": perf.speed * 1.3});
|
||||
addWptAir({"hdgChange": -90, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.3}); # final leg
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.325});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.35});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.375});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.4});
|
||||
addWptAir({"hdgChange": 0, "dist": 1000}, {"altChange": perf.vs, "ktas": perf.speed * 1.425});
|
||||
|
||||
addWptEnd();
|
||||
|
||||
io.write(
|
||||
g_fpFileHandler,
|
||||
" </flightplan>\n" ~
|
||||
"</PropertyList>\n\n"
|
||||
);
|
||||
io.close(g_fpFileHandler);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Return hash with "vs", "speed", "rolling".
|
||||
#
|
||||
var getAircraftPerformance = func () {
|
||||
# Cub
|
||||
# Cruise Speed 61 kt
|
||||
# Max Speed 106 kt
|
||||
# Approach speed 44-52 kt
|
||||
# Stall speed 33 kt
|
||||
|
||||
# Robin DR 400
|
||||
# Cruise Speed 134 kt
|
||||
# Max speeed 166 kt
|
||||
# Stall speed 51 kt
|
||||
# Rate of climb: 825 ft/min
|
||||
|
||||
# Cessna 182
|
||||
# Cruise Speed 145 kt
|
||||
# Max speeed 175 kt
|
||||
# Stall speed 50 kt
|
||||
# Best climb: 924 ft/min
|
||||
|
||||
var aiModel = getSelectedAircraft();
|
||||
if (aiModel == "DR400") {
|
||||
return {
|
||||
"vs": 285, # ft per 1000 m
|
||||
"speed": 70,
|
||||
"rolling": 2,
|
||||
};
|
||||
}
|
||||
|
||||
if (aiModel == "c182") {
|
||||
return {
|
||||
"vs": 295, # ft per 1000 m
|
||||
"speed": 75,
|
||||
"rolling": 2.2,
|
||||
};
|
||||
}
|
||||
|
||||
# Cub
|
||||
return {
|
||||
"vs": 200, # ft per 1000 m
|
||||
"speed": 55,
|
||||
"rolling": 1,
|
||||
};
|
||||
}
|
||||
|
||||
#
|
||||
# Initialize AI aircraft variable
|
||||
#
|
||||
# airport - Object from airportinfo().
|
||||
# runway - Object of runway from which the glider start.
|
||||
# isGliderPos - Pass 1 for set AI aircraft's coordinates as glider position, 0 set coordinates as runway threshold.
|
||||
#
|
||||
var initAircraftVariable = func (airport, runway, isGliderPos = 1) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
# Set coordinates as glider position or runway threshold
|
||||
g_coord = isGliderPos
|
||||
? gliderCoord
|
||||
: geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
# Set airplane heading as runway heading
|
||||
g_heading = runway.heading;
|
||||
|
||||
# Set AI airplane altitude as glider altitude (assumed it's on the ground).
|
||||
# It is more accurate than airport.elevation.
|
||||
g_altitude = gliderCoord.alt() * globals.M2FT;
|
||||
}
|
||||
|
||||
#
|
||||
# Get distance from glider to runway threshold e.g. in case that the user taxi from the runway threshold
|
||||
#
|
||||
# runway - Object of runway from which the glider start
|
||||
# Return the distance in metres, of the glider's displacement from the runway threshold.
|
||||
#
|
||||
var getGliderOffsetFromRunwayThreshold = func (runway) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
return rwyThreshold.distance_to(gliderCoord);
|
||||
}
|
||||
|
||||
#
|
||||
# Get the minimum runway length required, in meters
|
||||
#
|
||||
var getMinRunwayLength = func () {
|
||||
var aiModel = getSelectedAircraft();
|
||||
if (aiModel == "DR400") {
|
||||
return 470;
|
||||
}
|
||||
|
||||
if (aiModel == "c182") {
|
||||
return 508;
|
||||
}
|
||||
|
||||
return 280;
|
||||
}
|
||||
|
||||
#
|
||||
# Add new waypoint on ground
|
||||
#
|
||||
# coordOffset - Hash for calculate next coordinates (lat, lon), with following fields:
|
||||
# {
|
||||
# hdgChange - How the aircraft's heading supposed to change? 0 - keep the same heading.
|
||||
# dis - Distance in meters to calculate next waypoint coordinates.
|
||||
# }
|
||||
# performance - Hash with following fields:
|
||||
# {
|
||||
# altChange - How the aircraft's altitude is supposed to change? 0 - keep the same altitude.
|
||||
# ktas - True air speed of AI plane at the waypoint.
|
||||
# }
|
||||
#
|
||||
var addWptGround = func (coordOffset, performance) {
|
||||
wrireWpt(nil, coordOffset, performance, "ground");
|
||||
}
|
||||
|
||||
#
|
||||
# Add new waypoint in air
|
||||
#
|
||||
var addWptAir = func (coordOffset, performance) {
|
||||
wrireWpt(nil, coordOffset, performance, "air");
|
||||
}
|
||||
|
||||
#
|
||||
# Add "WAIT" waypoint
|
||||
#
|
||||
# sec - Number of seconds for wait
|
||||
#
|
||||
var addWptWait = func (sec) {
|
||||
wrireWpt("WAIT", {}, {}, nil, sec);
|
||||
}
|
||||
|
||||
#
|
||||
# Add "END" waypoint
|
||||
#
|
||||
var addWptEnd = func () {
|
||||
wrireWpt("END", {}, {});
|
||||
}
|
||||
|
||||
#
|
||||
# Write waypoint to flight plan file
|
||||
#
|
||||
# name - The name of waypoint
|
||||
# coordOffset.hdgChange - How the aircraft's heading supposed to change?
|
||||
# coordOffset.dist - Distance in meters to calculate next waypoint coordinates
|
||||
# performance.altChange - How the aircraft's altitude is supposed to change?
|
||||
# performance.elevationPlus - Set aircraft altitude as current terrain elevation + given value in feets.
|
||||
# It's best to use for the first point in the air to avoid the plane collapsing into
|
||||
# the ground in a bumpy airport
|
||||
# performance.ktas - True air speed of AI plane at the waypoint
|
||||
# groundAir - Allowed value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
var wrireWpt = func (
|
||||
name,
|
||||
coordOffset,
|
||||
performance,
|
||||
groundAir = nil,
|
||||
sec = nil
|
||||
) {
|
||||
var coord = nil;
|
||||
if (contains(coordOffset, "hdgChange") and contains(coordOffset, "dist")) {
|
||||
g_heading = g_heading + coordOffset.hdgChange;
|
||||
if (g_heading < 0) {
|
||||
g_heading = 360 + g_heading;
|
||||
}
|
||||
|
||||
if (g_heading > 360) {
|
||||
g_heading = g_heading - 360;
|
||||
}
|
||||
|
||||
g_coord.apply_course_distance(g_heading, coordOffset.dist);
|
||||
coord = g_coord;
|
||||
}
|
||||
|
||||
var alt = nil;
|
||||
if (coord != nil and contains(performance, "elevationPlus")) {
|
||||
var elevation = geo.elevation(coord.lat(), coord.lon());
|
||||
if (elevation == nil) {
|
||||
g_altitude = g_altitude + performance.elevationPlus;
|
||||
}
|
||||
else {
|
||||
g_altitude = elevation * globals.M2FT + performance.elevationPlus;
|
||||
}
|
||||
alt = g_altitude;
|
||||
}
|
||||
else if (contains(performance, "altChange")) {
|
||||
g_altitude = g_altitude + performance.altChange;
|
||||
alt = g_altitude;
|
||||
}
|
||||
|
||||
var ktas = contains(performance, "ktas") ? performance.ktas : nil;
|
||||
|
||||
name = name == nil ? g_wptCount : name;
|
||||
var data = getWptString(name, coord, alt, ktas, groundAir, sec);
|
||||
|
||||
io.write(g_fpFileHandler, data);
|
||||
|
||||
g_wptCount = g_wptCount + 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Get single waypoint data as a string.
|
||||
#
|
||||
# name - Name of waypoint. Special names are: "WAIT", "END".
|
||||
# coord - The Coord object
|
||||
# alt - Altitude AMSL of AI plane
|
||||
# ktas - True air speed of AI plane
|
||||
# groundAir - Allowe value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
var getWptString = func (name, coord = nil, alt = nil, ktas = nil, groundAir = nil, sec = nil) {
|
||||
var str = " <wpt>\n"
|
||||
~ " <name>" ~ name ~ "</name>\n";
|
||||
|
||||
if (coord != nil) {
|
||||
str = str ~ " <lat>" ~ coord.lat() ~ "</lat>\n";
|
||||
str = str ~ " <lon>" ~ coord.lon() ~ "</lon>\n";
|
||||
str = str ~ " <!-- " ~ coord.lat() ~ "," ~ coord.lon() ~ " -->\n";
|
||||
}
|
||||
|
||||
if (alt != nil) {
|
||||
# str = str ~ " <alt>" ~ alt ~ "</alt>\n";
|
||||
str = str ~ " <crossat>" ~ alt ~ "</crossat>\n";
|
||||
}
|
||||
|
||||
if (ktas != nil) {
|
||||
str = str ~ " <ktas>" ~ ktas ~ "</ktas>\n";
|
||||
}
|
||||
|
||||
if (groundAir != nil) {
|
||||
var onGround = groundAir == "ground" ? "true" : "false";
|
||||
str = str ~ " <on-ground>" ~ onGround ~ "</on-ground>\n";
|
||||
}
|
||||
|
||||
if (sec != nil) {
|
||||
str = str ~ " <time-sec>" ~ sec ~ "</time-sec>\n";
|
||||
}
|
||||
|
||||
return str ~ " </wpt>\n";
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<label>Aerotow Everywhere version 1.1.0 - 16th August 2022</label>
|
||||
<label>Aerotow Everywhere version 1.8.1 - 17th September 2022</label>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</empty>
|
||||
|
||||
<text>
|
||||
<label>Add thermal</label>
|
||||
<label>Add Thermal</label>
|
||||
</text>
|
||||
|
||||
<empty>
|
||||
@@ -189,7 +189,9 @@
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[thermal.add();]]></script>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.thermal.add();
|
||||
]]></script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
|
||||
588
gui/dialogs/route-aerotow.xml
Normal file
@@ -0,0 +1,588 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
-->
|
||||
|
||||
<!-- This file requires FlightGear version 2018.2 or newer -->
|
||||
|
||||
<PropertyList>
|
||||
<name>route-aerotow</name>
|
||||
<resizable>false</resizable>
|
||||
<layout>vbox</layout>
|
||||
<default-padding>3</default-padding>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<empty>
|
||||
<stretch>1</stretch>
|
||||
</empty>
|
||||
|
||||
<text>
|
||||
<label>Aerotow Route</label>
|
||||
</text>
|
||||
|
||||
<empty>
|
||||
<stretch>1</stretch>
|
||||
</empty>
|
||||
|
||||
<button>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<legend></legend>
|
||||
<keynum>27</keynum>
|
||||
<border>2</border>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> Here you can change the default flight path of the tow plane. </label>
|
||||
</text>
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> You cannot change the initial point, the AI plane always </label>
|
||||
</text>
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> takes-off in front of the runway you are on. </label>
|
||||
</text>
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> Set 0 for `Distance` for completing the flight plan. </label>
|
||||
</text>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Aerotow:</label>
|
||||
</text>
|
||||
|
||||
<combo>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<pref-width>120</pref-width>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/ai-model</property>
|
||||
<value>Piper J3 Cub</value>
|
||||
<value>Robin DR400</value>
|
||||
<value>Cessna 182</value>
|
||||
<value>Douglas C-47</value>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</combo>
|
||||
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<halign>right</halign>
|
||||
<label>Max alt (AGL):</label>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>0</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/max-alt-agl</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<label>Initial Heading (deg)</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<label>Distance (m)</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label>Alt change (AGL)</label>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format>%.2f</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/init-wpt/heading-change</property>
|
||||
</text>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format>%.f m</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/init-wpt/distance-m</property>
|
||||
</text>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/init-wpt/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<label>Heading change (deg)</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<label>Distance (m)</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label>Alt change (AGL)</label>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>3</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>4</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>5</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>5</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>5</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>6</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>6</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>6</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>7</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>7</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>7</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>8</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>8</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>8</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>9</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>9</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>9</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>10</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>10</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>10</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>11</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>11</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>11</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>12</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<input>
|
||||
<row>12</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>12</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>13</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Total:</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>13</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format>%.f m</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/total/distance</property>
|
||||
</text>
|
||||
<text>
|
||||
<row>13</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/total/alt</property>
|
||||
</text>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label>Description:</label>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<halign>fill</halign>
|
||||
<!-- <halign>left</halign> -->
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/description</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
|
||||
<layout>hbox</layout>
|
||||
|
||||
<button>
|
||||
<legend>OK</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Default</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.flightPlan.initial();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Save...</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.routeDialog.save();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Load...</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.routeDialog.load();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
</group>
|
||||
|
||||
</PropertyList>
|
||||
342
gui/dialogs/towrope-configuration.xml
Normal file
@@ -0,0 +1,342 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<name>towrope-config</name>
|
||||
<layout>vbox</layout>
|
||||
<default-padding>5</default-padding>
|
||||
<modal>false</modal>
|
||||
<width>680</width>
|
||||
|
||||
<!-- title bar -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
<text>
|
||||
<label>Towrope Configuration</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/>
|
||||
|
||||
<!-- aerotow parameters -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<!-- tow length -->
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Length</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f m </format>
|
||||
<property>/sim/hitches/aerotow/tow/length</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- break force -->
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Weak Link Break Force</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f N </format>
|
||||
<property>/sim/hitches/aerotow/tow/brake-force</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow characteristic -->
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Elastic Constant</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f N</format>
|
||||
<property>/sim/hitches/aerotow/tow/elastic-constant</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow thickness -->
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Diameter</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f mm</format>
|
||||
<property>/sim/hitches/aerotow/rope/rope-diameter-mm</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow weight -->
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Weight per Meter</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.3f kg/m</format>
|
||||
<property>/sim/hitches/aerotow/tow/weight-per-m-kg-m</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
</group>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
|
||||
<vrule/>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<!-- tow length -->
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>20 m</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>tow_length</name>
|
||||
<property>/sim/hitches/aerotow/tow/length</property>
|
||||
<min>20</min>
|
||||
<max>200</max>
|
||||
<step>5</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>200 m</label>
|
||||
</text>
|
||||
|
||||
<!-- break force -->
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>100 N</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>1</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>break_force</name>
|
||||
<property>/sim/hitches/aerotow/tow/brake-force</property>
|
||||
<min>100</min>
|
||||
<max>100000</max>
|
||||
<step>100</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>100,000 N</label>
|
||||
</text>
|
||||
|
||||
<!-- tow characteristic -->
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 N</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>2</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>elastic_constant</name>
|
||||
<property>/sim/hitches/aerotow/tow/elastic-constant</property>
|
||||
<min>0</min>
|
||||
<max>1500000</max>
|
||||
<step>200</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>1,500,000 N</label>
|
||||
</text>
|
||||
|
||||
<!-- tow thickness -->
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 mm</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>3</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>rope_diameter</name>
|
||||
<property>/sim/hitches/aerotow/rope/rope-diameter-mm</property>
|
||||
<min>0</min>
|
||||
<max>50</max>
|
||||
<step>1</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>50 mm</label>
|
||||
</text>
|
||||
|
||||
<!-- tow weight -->
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 kg/m</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>4</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>weight_per_m</name>
|
||||
<property>/sim/hitches/aerotow/tow/weight-per-m-kg-m</property>
|
||||
<min>0</min>
|
||||
<max>1</max>
|
||||
<step>0.001</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>1 kg/m</label>
|
||||
</text>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<!-- bottom line -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
<button>
|
||||
<legend>Close</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
<button>
|
||||
<legend>Default</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
setprop("/sim/hitches/aerotow/tow/length", 60.0); # hitch.nas 60.0
|
||||
setprop("/sim/hitches/aerotow/tow/brake-force", 100000.0); # hitch.nas 12345.0
|
||||
setprop("/sim/hitches/aerotow/tow/elastic-constant", 10000.0); # hitch.nas 9111.0
|
||||
setprop("/sim/hitches/aerotow/rope/rope-diameter-mm", 20); # hitch.nas 20
|
||||
setprop("/sim/hitches/aerotow/tow/weight-per-m-kg-m", 0.35); # hitch.nas 0.35
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
</group>
|
||||
</PropertyList>
|
||||
54
messages.nas
@@ -1,54 +0,0 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Display given message as OK.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
var displayOk = func (message) {
|
||||
display(message, "ok");
|
||||
}
|
||||
|
||||
#
|
||||
# Display given message as an error.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
var displayError = func (message) {
|
||||
display(message, "error");
|
||||
}
|
||||
|
||||
#
|
||||
# Display given message.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
# type - The type of message. It can take values as "ok" or "error".
|
||||
#
|
||||
var display = func (message, type) {
|
||||
# Print to console
|
||||
print("Aerotow Everywhere add-on: " ~ message);
|
||||
|
||||
# Read the message by speech synthesizer
|
||||
props.globals.getNode("/sim/sound/voices/ai-plane").setValue(message);
|
||||
|
||||
# Display message on the screen
|
||||
var durationInSec = int(size(message) / 12) + 3;
|
||||
var window = screen.window.new(nil, -40, 10, durationInSec);
|
||||
window.bg = [0.0, 0.0, 0.0, 0.40];
|
||||
|
||||
window.fg = type == "error"
|
||||
? [1.0, 0.0, 0.0, 1]
|
||||
: [0.0, 1.0, 0.0, 1];
|
||||
|
||||
window.align = "center";
|
||||
window.write(message);
|
||||
}
|
||||
106
nasal/aerotow.nas
Normal file
@@ -0,0 +1,106 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Aerotow for enable and disable scenario with aerotow aircraft.
|
||||
#
|
||||
var Aerotow = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon) {
|
||||
var obj = { parents: [Aerotow] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
obj.listeners = [];
|
||||
|
||||
obj.message = Message.new();
|
||||
obj.thermal = Thermal.new(addon, obj.message);
|
||||
obj.scenario = Scenario.new(addon, obj.message);
|
||||
|
||||
# Listener for ai-model property triggered when the user select a tow aircraft from add-on menu
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/ai-model", func () {
|
||||
obj.restartAerotow();
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Uninitialize aerotow module
|
||||
#
|
||||
del: func () {
|
||||
me.thermal.del();
|
||||
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Function for restart AI scenario with delay when the sound has to stop.
|
||||
#
|
||||
restartAerotow: func () {
|
||||
me.message.success("Aerotow on the way");
|
||||
|
||||
# Stop playing engine sound
|
||||
setprop(me.addonNodePath ~ "/addon-devel/sound/enable", false);
|
||||
|
||||
# Wait a second for the engine sound to turn off
|
||||
Timer.new().singleShot(1, me, func () {
|
||||
me.unloadScenario();
|
||||
});
|
||||
},
|
||||
|
||||
#
|
||||
# Unload scenario and start a new one
|
||||
#
|
||||
unloadScenario: func () {
|
||||
if (!me.scenario.unload()) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Start aerotow with delay to avoid duplicate engine sound playing
|
||||
Timer.new().singleShot(1, me, func () {
|
||||
me.startAerotow();
|
||||
});
|
||||
},
|
||||
|
||||
#
|
||||
# Main function to prepare AI scenario and run it.
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
startAerotow: func () {
|
||||
if (!me.scenario.unload()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!me.scenario.generateXml()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return me.scenario.load();
|
||||
},
|
||||
|
||||
#
|
||||
# Function for unload our AI scenario.
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
stopAerotow: func () {
|
||||
var withMessages = true;
|
||||
return me.scenario.unload(withMessages);
|
||||
},
|
||||
};
|
||||
214
nasal/aircraft.nas
Normal file
@@ -0,0 +1,214 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Parent class of Aircraft
|
||||
#
|
||||
var Aircraft = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
DISTANCE_DETERMINANT: 1000, # meters
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# vs - vertical speed in ft per DISTANCE_DETERMINANT m
|
||||
# seed - take-off speed
|
||||
# speedLimit - max speed
|
||||
# rolling - factor for rolling
|
||||
# minRwyLength - minimum runway length required, in meters
|
||||
# minFinalLegDist - minimum distance for final leg in meters (for landing)
|
||||
# name - full name of aircraft used in route dialog
|
||||
# nameMenuCall - short name of aircraft for call a plane from menu
|
||||
# modelPath - Path to the aircraft model
|
||||
#
|
||||
new: func (vs, speed, speedLimit, rolling, minRwyLength, minFinalLegDist, name, nameMenuCall, modelPath) {
|
||||
var obj = { parents: [Aircraft] };
|
||||
|
||||
obj.vs = vs;
|
||||
obj.speed = speed;
|
||||
obj.speedLimit = speedLimit;
|
||||
obj.rolling = rolling;
|
||||
obj.minRwyLength = minRwyLength;
|
||||
obj.minFinalLegDist = minFinalLegDist;
|
||||
obj.name = name;
|
||||
obj.nameMenuCall = nameMenuCall;
|
||||
obj.modelPath = modelPath;
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Check that given name match to aircraft name
|
||||
#
|
||||
# name - Name of aircraft to check.
|
||||
#
|
||||
# Return true when match, otherwise false.
|
||||
#
|
||||
isModelName: func (name) {
|
||||
return name == me.name or name == me.nameMenuCall;
|
||||
},
|
||||
|
||||
#
|
||||
# Return how much the altitide increases for a given vertical speed and distance
|
||||
#
|
||||
# distance - distance in meters
|
||||
#
|
||||
getAltChange: func (distance) {
|
||||
return me.vs * (distance / Aircraft.DISTANCE_DETERMINANT);
|
||||
},
|
||||
|
||||
#
|
||||
# Return selected Aircraft object
|
||||
#
|
||||
# addon - Addon object
|
||||
# isRouteMode - Use true to get the plane for the "Aerotow Route" dialog,
|
||||
# use false (default) for call the airplane for towing.
|
||||
#
|
||||
getSelected: func (addon, isRouteMode = 0) {
|
||||
var name = Aircraft.getSelectedAircraftName(addon, isRouteMode);
|
||||
foreach (var aircraft; g_Aircrafts) {
|
||||
if (aircraft.isModelName(name)) {
|
||||
return aircraft;
|
||||
}
|
||||
}
|
||||
|
||||
# Fist as default
|
||||
return g_Aircrafts[0];
|
||||
},
|
||||
|
||||
#
|
||||
# Return name of selected aircraft. Possible values depend of isRouteMode: "Cub", "DR400", "c182".
|
||||
#
|
||||
#
|
||||
# addon - Addon object
|
||||
# isRouteMode - Use true to get the plane for the "Aerotow Route" dialog,
|
||||
# use false (default) for call the airplane for towing.
|
||||
#
|
||||
getSelectedAircraftName: func (addon, isRouteMode) {
|
||||
if (isRouteMode) {
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/route/ai-model") or g_Aircrafts[0].name;
|
||||
}
|
||||
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/ai-model") or g_Aircrafts[0].nameMenuCall;
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Cub
|
||||
# Cruise Speed 61 kt
|
||||
# Max Speed 106 kt
|
||||
# Approach speed 44-52 kt
|
||||
# Stall speed 33 kt
|
||||
#
|
||||
var AircraftCub = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 200,
|
||||
speed: 55,
|
||||
speedLimit: 60,
|
||||
rolling: 1,
|
||||
minRwyLength: 280,
|
||||
minFinalLegDist: 5000,
|
||||
name: "Piper J3 Cub",
|
||||
nameMenuCall: "Cub",
|
||||
modelPath: "Aircraft/Aerotow/Cub/Models/Cub-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Robin DR 400
|
||||
# Cruise Speed 134 kt
|
||||
# Max speeed 166 kt
|
||||
# Stall speed 51 kt
|
||||
# Rate of climb: 825 ft/min
|
||||
#
|
||||
var AircraftRobin = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 285,
|
||||
speed: 70,
|
||||
speedLimit: 75,
|
||||
rolling: 2,
|
||||
minRwyLength: 470,
|
||||
minFinalLegDist: 5400,
|
||||
name: "Robin DR400",
|
||||
nameMenuCall: "DR400",
|
||||
modelPath: "Aircraft/Aerotow/DR400/Models/dr400-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Cessna 182
|
||||
# Cruise Speed 145 kt
|
||||
# Max speeed 175 kt
|
||||
# Stall speed 50 kt
|
||||
# Best climb: 924 ft/min
|
||||
#
|
||||
var AircraftC182 = {
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 295,
|
||||
speed: 75,
|
||||
speedLimit: 80,
|
||||
rolling: 2.2,
|
||||
minRwyLength: 508,
|
||||
minFinalLegDist: 5500,
|
||||
name: "Cessna 182",
|
||||
nameMenuCall: "c182",
|
||||
modelPath: "Aircraft/Aerotow/c182/Models/c182-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Douglas C-47
|
||||
# Cruise Speed 152 kt
|
||||
# Max speeed 199 kt
|
||||
# Stall speed 57 kt
|
||||
# Best climb: 1052 ft/min
|
||||
#
|
||||
var AircraftC47 = {
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 310,
|
||||
speed: 85,
|
||||
speedLimit: 90,
|
||||
rolling: 2.2,
|
||||
minRwyLength: 508,
|
||||
minFinalLegDist: 6000,
|
||||
name: "Douglas C-47",
|
||||
nameMenuCall: "C47",
|
||||
modelPath: "Aircraft/Aerotow/C-47/Models/c-47-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
# Create Aircraft objects
|
||||
var g_Aircrafts = [
|
||||
AircraftCub.new(),
|
||||
AircraftRobin.new(),
|
||||
AircraftC182.new(),
|
||||
AircraftC47.new(),
|
||||
];
|
||||
149
nasal/dialogs/route.nas
Normal file
@@ -0,0 +1,149 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class for hande Route Dialog
|
||||
#
|
||||
var RouteDialog = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
ROUTE_SAVES_DIR: "route-saves",
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [RouteDialog] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.savePath = addon.storagePath ~ "/" ~ RouteDialog.ROUTE_SAVES_DIR;
|
||||
obj.maxRouteWaypoints = 10;
|
||||
obj.listeners = [];
|
||||
|
||||
# Set listener for aerotow combo box value in route dialog for recalculate altitude change
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/route/ai-model", func () {
|
||||
obj.calculateAltChangeAndTotals();
|
||||
}));
|
||||
|
||||
# Set listener for Max altitude AGL value in route dialog for recalculate altitude change
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/route/wpts/max-alt-agl", func () {
|
||||
obj.calculateAltChangeAndTotals();
|
||||
}));
|
||||
|
||||
# Set listeners for distance fields for calculate altitude change
|
||||
for (var i = 0; i < obj.maxRouteWaypoints; i += 1) {
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/distance-m", func () {
|
||||
obj.calculateAltChangeAndTotals();
|
||||
}));
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Calculate total distance and altitude and put in to property tree
|
||||
#
|
||||
calculateAltChangeAndTotals: func () {
|
||||
var totalDistance = 0.0;
|
||||
var totalAlt = 0.0;
|
||||
var isEnd = false;
|
||||
var isAltLimit = false;
|
||||
|
||||
var isRouteMode = true;
|
||||
var aircraft = Aircraft.getSelected(me.addon, isRouteMode);
|
||||
|
||||
# 0 means without altitude limits
|
||||
var maxAltAgl = getprop(me.addonNodePath ~ "/addon-devel/route/wpts/max-alt-agl") or 0;
|
||||
|
||||
for (var i = 0; i < me.maxRouteWaypoints; i += 1) {
|
||||
var distance = getprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/distance-m") or 0;
|
||||
|
||||
# If we have reached the altitude limit, the altitude no longer changes (0)
|
||||
var altChange = isAltLimit ? 0 : aircraft.getAltChange(distance);
|
||||
if (maxAltAgl > 0 and altChange > 0 and totalAlt + altChange > maxAltAgl) {
|
||||
# We will exceed the altitude limit, so set the altChange to the altitude limit
|
||||
# and set isAltLimit flag that the limit is reached.
|
||||
altChange = maxAltAgl - totalAlt;
|
||||
isAltLimit = true;
|
||||
}
|
||||
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/alt-change-agl-ft", altChange);
|
||||
|
||||
if (!isEnd) {
|
||||
if (distance > 0.0) {
|
||||
totalDistance += distance;
|
||||
totalAlt += altChange;
|
||||
}
|
||||
else {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/total/distance", totalDistance);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/total/alt", totalAlt);
|
||||
},
|
||||
|
||||
#
|
||||
# Save route with description to the XML file.
|
||||
#
|
||||
save: func () {
|
||||
me.openFileSelector(
|
||||
func (node) {
|
||||
var nodeSave = props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts");
|
||||
if (io.write_properties(node.getValue(), nodeSave)) {
|
||||
me.message.success("The route has been saved");
|
||||
}
|
||||
},
|
||||
"Save route",
|
||||
"Save"
|
||||
);
|
||||
},
|
||||
|
||||
#
|
||||
# Load route with description from the XML file.
|
||||
#
|
||||
load: func () {
|
||||
me.openFileSelector(
|
||||
func (node) {
|
||||
var nodeLoad = props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts");
|
||||
if (io.read_properties(node.getValue(), nodeLoad)) {
|
||||
me.message.success("The route has been loaded");
|
||||
}
|
||||
},
|
||||
"Load route",
|
||||
"Load"
|
||||
);
|
||||
},
|
||||
|
||||
#
|
||||
# Open file selector dialog for save/load XML file with route.
|
||||
#
|
||||
openFileSelector: func (callback, title, button) {
|
||||
var fileSelector = gui.FileSelector.new(callback, title, button, ["*.xml"], me.savePath, "route.xml");
|
||||
fileSelector.open();
|
||||
},
|
||||
};
|
||||
96
nasal/dialogs/thermal.nas
Normal file
@@ -0,0 +1,96 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Thermal for handle add new thermal.
|
||||
#
|
||||
var Thermal = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [Thermal] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
obj.message = message;
|
||||
obj.listeners = [];
|
||||
|
||||
# Listener for calculate distance from meters to nautical miles.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/distance-m", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/distance-nm", node.getValue() * globals.M2NM);
|
||||
}));
|
||||
|
||||
# Listener for calculate strength from ft/s to m/s.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/strength-fps", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/strength-mps", node.getValue() * globals.FPS2KT * globals.KT2MPS);
|
||||
}));
|
||||
|
||||
# Listener for calculate diameter from ft to m.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/diameter-ft", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/diameter-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
|
||||
# Listener for calculate height from ft to m.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/height-msl", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/height-msl-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Add thermal 300 m before glider position.
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
add: func () {
|
||||
var heading = getprop("/orientation/heading-deg") or 0;
|
||||
var distance = getprop(me.addonNodePath ~ "/addon-devel/add-thermal/distance-m") or 300;
|
||||
|
||||
var position = geo.aircraft_position();
|
||||
position.apply_course_distance(heading, distance);
|
||||
|
||||
# Get random layer from 1 to 4
|
||||
var layer = int(rand() * 4) + 1;
|
||||
|
||||
var args = props.Node.new({
|
||||
"type": "thermal",
|
||||
"model": "Models/Weather/altocumulus_layer" ~ layer ~ ".xml",
|
||||
"latitude": position.lat(),
|
||||
"longitude": position.lon(),
|
||||
"strength-fps": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/strength-fps") or 16.0,
|
||||
"diameter-ft": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/diameter-ft") or 4000,
|
||||
"height-msl": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/height-msl") or 9000,
|
||||
"search-order": "DATA_ONLY"
|
||||
});
|
||||
|
||||
if (fgcommand("add-aiobject", args)) {
|
||||
me.message.success("The thermal has been added");
|
||||
return true;
|
||||
}
|
||||
|
||||
me.message.error("Adding thermal failed");
|
||||
return false;
|
||||
},
|
||||
};
|
||||
563
nasal/flight-plan.nas
Normal file
@@ -0,0 +1,563 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class FlightPlan for crate and save as XML file the flight plan for AI scenario.
|
||||
#
|
||||
var FlightPlan = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
FILENAME_FLIGHTPLAN: "aerotown-addon-flightplan.xml",
|
||||
MAX_RUNWAY_DISTANCE: 100, # meters
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
# routeDialog - RouteDialog object
|
||||
#
|
||||
new: func (addon, message, routeDialog) {
|
||||
var obj = { parents: [FlightPlan] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
obj.routeDialog = routeDialog;
|
||||
obj.flightPlanWriter = FlightPlanWriter.new(addon);
|
||||
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.coord = nil; # Coordinates for flight plan
|
||||
obj.heading = nil; # AI plane heading
|
||||
obj.altitude = nil; # AI plane altitude
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Get inital location of glider.
|
||||
#
|
||||
# Return object with "lat", "lon" and "heading".
|
||||
#
|
||||
getLocation: func () {
|
||||
var icao = getprop("/sim/airport/closest-airport-id");
|
||||
if (icao == nil or icao == "") {
|
||||
me.message.error("Airport code cannot be obtained.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
# Find nearest runway threshold
|
||||
var airport = airportinfo(icao);
|
||||
if (airport == nil) {
|
||||
me.message.error("An airport with the code " ~ icao ~ " cannot be found.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
var rwyResult = me.findRunway(airport, gliderCoord);
|
||||
|
||||
if (rwyResult.distance > FlightPlan.MAX_RUNWAY_DISTANCE) {
|
||||
# The runway is too far away, we assume a bush start
|
||||
return {
|
||||
"type" : "bush",
|
||||
"lat" : gliderCoord.lat(),
|
||||
"lon" : gliderCoord.lon(),
|
||||
"heading" : getprop("/orientation/heading-deg"),
|
||||
"elevation": me.getEleveationInFt(gliderCoord),
|
||||
};
|
||||
}
|
||||
|
||||
# We have a runway
|
||||
|
||||
var minRwyLength = Aircraft.getSelected(me.addon).minRwyLength;
|
||||
if (rwyResult.runway.length < minRwyLength) {
|
||||
me.message.error(
|
||||
"This runway is too short. Please choose a longer one than " ~ minRwyLength ~ " m "
|
||||
~ "(" ~ math.round(minRwyLength * globals.M2FT) ~ " ft)."
|
||||
);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return {
|
||||
"type" : "runway",
|
||||
"lat" : rwyResult.runway.lat,
|
||||
"lon" : rwyResult.runway.lon,
|
||||
"heading" : rwyResult.runway.heading,
|
||||
"elevation": me.getEleveationInFt(
|
||||
geo.Coord.new().set_latlon(rwyResult.runway.lat, rwyResult.runway.lon)
|
||||
),
|
||||
"length" : rwyResult.runway.length,
|
||||
};
|
||||
},
|
||||
|
||||
#
|
||||
# Find nearest runway for given airport
|
||||
#
|
||||
# Return hash with distance to nearest runway threshold and runway object itself.
|
||||
#
|
||||
findRunway: func (airport, gliderCoord) {
|
||||
var result = {
|
||||
"runway" : nil,
|
||||
"distance" : 999999999,
|
||||
};
|
||||
|
||||
foreach (var runwayName; keys(airport.runways)) {
|
||||
var runway = airport.runways[runwayName];
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
var distanceToThreshold = rwyThreshold.distance_to(gliderCoord);
|
||||
if (distanceToThreshold < result.distance) {
|
||||
result.runway = runway;
|
||||
result.distance = distanceToThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
#
|
||||
# Initialize flight plan and set it to property tree
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
initial: func () {
|
||||
var location = me.getLocation();
|
||||
if (location == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var aircraft = Aircraft.getSelected(me.addon);
|
||||
|
||||
var isGliderPos = false;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
# Max altitude without limits
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/max-alt-agl", 0);
|
||||
|
||||
# inittial readonly waypoint
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/heading-change", me.heading);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/distance-m", 100);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/alt-change-agl-ft", aircraft.vs / 10);
|
||||
|
||||
# in air
|
||||
var wptData = [
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": aircraft.vs * 5},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": aircraft.vs},
|
||||
{"hdgChange": -90, "dist": 6000, "altChange": aircraft.vs * 6},
|
||||
{"hdgChange": -90, "dist": 1500, "altChange": aircraft.vs * 1.5},
|
||||
{"hdgChange": -90, "dist": 6000, "altChange": aircraft.vs * 6},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
];
|
||||
|
||||
# Default route
|
||||
# ^ - airport with heading direction to north
|
||||
# 1 - 1st waypoint
|
||||
# 2 - 2nd waypoint, etc.
|
||||
#
|
||||
# 2 . . 1 5
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . ^ .
|
||||
# . .
|
||||
# . .
|
||||
# 3 . . . . 4
|
||||
|
||||
var index = 0;
|
||||
foreach (var wpt; wptData) {
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/heading-change", wpt.hdgChange);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/distance-m", wpt.dist);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/alt-change-agl-ft", wpt.altChange);
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
me.routeDialog.calculateAltChangeAndTotals();
|
||||
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/description", "Default route around the start location");
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
#
|
||||
# Generate the XML file with the flight plane for our plane for AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/AI/FlightPlans/aerotown-addon-flightplan.xml.
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
generateXml: func () {
|
||||
var location = me.getLocation();
|
||||
if (location == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
me.flightPlanWriter.open();
|
||||
|
||||
var aircraft = Aircraft.getSelected(me.addon);
|
||||
|
||||
var isGliderPos = true;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
# Start at 2 o'clock from the glider...
|
||||
# Inital ktas must be >= 1.0
|
||||
me.addWptGround({"shift": {"hdgChange": 60, "dist": 25, "altChange": 0}, "ktas": 5}); # 1
|
||||
|
||||
# Reset coord and heading
|
||||
isGliderPos = false;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
var gliderOffsetM = me.getGliderOffsetFromRunwayThreshold(location);
|
||||
|
||||
# ... and line up with the runway
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": me.getInitialDistance() + gliderOffsetM, "altChange": 0}, "ktas": 2.5}); # 2
|
||||
|
||||
# Rolling
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10, "altChange": 0}, "ktas": 5}); # 3
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 20, "altChange": 0}, "ktas": 5}); # 4
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 20, "altChange": 0}, "ktas": aircraft.speed / 6}); # 5
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10, "altChange": 0}, "ktas": aircraft.speed / 5}); # 6
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 4}); # 7
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 3.5}); # 8
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 3}); # 9
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 2.5}); # 10
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 2}); # 11
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 1.75}); # 12
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 1.5}); # 13
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed / 1.25}); # 14
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 10 * aircraft.rolling, "altChange": 0}, "ktas": aircraft.speed}); # 15
|
||||
|
||||
# Take-off
|
||||
me.addWptAir({ "shift": {"hdgChange": 0, "dist": 100 * aircraft.rolling, "elevation": 3}, "ktas": aircraft.speed * 1.05}); # 16
|
||||
me.addWptAir({ "shift": {"hdgChange": 0, "dist": 100, "altChange": aircraft.vs / 10}, "ktas": aircraft.speed * 1.025}); # 17
|
||||
|
||||
|
||||
# 0 means without altitude limits
|
||||
var maxAltAgl = getprop(me.addonNodePath ~ "/addon-devel/route/wpts/max-alt-agl") or 0;
|
||||
var totalAlt = 0.0;
|
||||
var isAltLimit = false;
|
||||
|
||||
# Add waypoints according to user settings
|
||||
var speedInc = 1.0;
|
||||
foreach (var wptNode; props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts").getChildren("wpt")) {
|
||||
var distance = wptNode.getChild("distance-m").getValue();
|
||||
if (distance <= 0.0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var hdgChange = wptNode.getChild("heading-change").getValue();
|
||||
|
||||
# If we have reached the altitude limit, the altitude no longer changes (0)
|
||||
var altChange = isAltLimit ? 0 : aircraft.getAltChange(distance);
|
||||
if (maxAltAgl > 0 and altChange > 0 and totalAlt + altChange > maxAltAgl) {
|
||||
# We will exceed the altitude limit, so set the altChange to the altitude limit
|
||||
# and set isAltLimit flag that the limit is reached.
|
||||
altChange = maxAltAgl - totalAlt;
|
||||
isAltLimit = true;
|
||||
}
|
||||
|
||||
speedInc += ((distance / Aircraft.DISTANCE_DETERMINANT) * 0.025);
|
||||
var ktas = aircraft.speed * speedInc;
|
||||
if (ktas > aircraft.speedLimit) {
|
||||
ktas = aircraft.speedLimit;
|
||||
}
|
||||
|
||||
totalAlt += altChange;
|
||||
|
||||
me.addWptAir({"shift": {"hdgChange": hdgChange, "dist": distance, "altChange": altChange}, "ktas": ktas});
|
||||
}
|
||||
|
||||
# Back to airport if possible
|
||||
if (location.type == "runway") {
|
||||
# Add extra near waypoint to keep plane in whole designed track
|
||||
me.addWptAir({"shift": {"hdgChange": hdgChange, "dist": 100, "altChange": altChange}, "ktas": ktas});
|
||||
|
||||
var coordRwyThreshold = geo.Coord.new().set_latlon(location.lat, location.lon);;
|
||||
|
||||
# Check distance to runway threshold
|
||||
var dostanceToThreshold = me.coord.distance_to(coordRwyThreshold);
|
||||
|
||||
# Reset variables
|
||||
me.heading = location.heading; # runway heading
|
||||
me.coord = coordRwyThreshold;
|
||||
|
||||
# Move o the left of the runway threshold
|
||||
me.heading = me.correctHeading(me.heading - 90);
|
||||
me.coord.apply_course_distance(me.heading, 1000);
|
||||
|
||||
# Add a waypoint to the left of the runway + 3000 m to the middle of length
|
||||
# Descend as far as you can to max elevation + 3000 ft
|
||||
var halfRwyLenght = location.length / 2;
|
||||
var altAgl = me.altitude - location.elevation;
|
||||
var elevation = altAgl - (aircraft.getAltChange(dostanceToThreshold) * 2);
|
||||
if (elevation < 3000) {
|
||||
elevation = 3000;
|
||||
}
|
||||
me.addWptAir({"shift": {"hdgChange": 90, "dist": halfRwyLenght, "elevation": elevation}, "ktas": aircraft.speed});
|
||||
|
||||
# Fly downwind away of threshold, how far depend of the altitude
|
||||
var desiredElevation = 1400;
|
||||
var distance = (((elevation - desiredElevation) / (aircraft.vs * 2)) * 1000);
|
||||
if (distance < aircraft.minFinalLegDist) {
|
||||
distance = aircraft.minFinalLegDist;
|
||||
}
|
||||
me.addWptAir({"shift": {"hdgChange": -180, "dist": halfRwyLenght + distance, "elevation": desiredElevation}, "ktas": aircraft.speed});
|
||||
|
||||
# Turn to base leg
|
||||
me.addWptAir({"shift": {"hdgChange": -90, "dist": 1000, "elevation": 1000}, "ktas": aircraft.speed, "flapsDown": true});
|
||||
|
||||
# Reset variables
|
||||
me.coord = geo.Coord.new().set_latlon(location.lat, location.lon); # runway threshold
|
||||
|
||||
# Turn on final
|
||||
me.addWptAir({
|
||||
"coord" : me.coord,
|
||||
"crossAt" : location.elevation + 10,
|
||||
"ktas" : aircraft.speed * 0.75,
|
||||
"flapsDown": true,
|
||||
"gearDown" : true,
|
||||
});
|
||||
|
||||
# Reset variables
|
||||
me.heading = location.heading;
|
||||
|
||||
# Flare
|
||||
me.addWptAir({
|
||||
"shift" : {"hdgChange": 0, "dist": 100, "elevation": 10},
|
||||
"ktas" : aircraft.speed * 0.7,
|
||||
"flapsDown": true,
|
||||
"gearDown" : true,
|
||||
});
|
||||
|
||||
# Touchdown
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 200, "elevation": 0}, "ktas": aircraft.speed * 0.6});
|
||||
|
||||
# Break
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 200, "elevation": 0}, "ktas": aircraft.speed * 0.4});
|
||||
|
||||
# Slow down to 5 kt
|
||||
me.addWptGround({"shift": {"hdgChange": 0, "dist": 50, "elevation": 0}, "ktas": 5});
|
||||
|
||||
# Turn right out of runway and full stop
|
||||
me.addWptEnd({"shift": {"hdgChange": 90, "dist": 100, "elevation": 0}, "ktas": 0, "onGround": true});
|
||||
}
|
||||
else {
|
||||
me.addWptEnd();
|
||||
}
|
||||
|
||||
me.flightPlanWriter.close();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
#
|
||||
# Get initial distance AI plane from the glider that the tow is nearly tautened.
|
||||
#
|
||||
# Return distance in meters.
|
||||
#
|
||||
getInitialDistance: func () {
|
||||
var ropeLengthM = getprop("/sim/hitches/aerotow/tow/length") or 60;
|
||||
var tautenRelative = 0.68;
|
||||
return ropeLengthM * tautenRelative;
|
||||
},
|
||||
|
||||
#
|
||||
# Initialize AI aircraft variable
|
||||
#
|
||||
# location - Object of location from which the glider start.
|
||||
# isGliderPos - Pass true for set AI aircraft's coordinates as glider position, false set coordinates as runway threshold.
|
||||
#
|
||||
initAircraftVariable: func (location, isGliderPos) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
# Set coordinates as glider position or runway threshold
|
||||
me.coord = isGliderPos
|
||||
? gliderCoord
|
||||
: geo.Coord.new().set_latlon(location.lat, location.lon);
|
||||
|
||||
# Set airplane heading as runway or glider heading
|
||||
me.heading = location.heading;
|
||||
|
||||
# Set AI airplane altitude as glider altitude (assumed it's on the ground).
|
||||
# It is more accurate than airport.elevation.
|
||||
me.altitude = gliderCoord.alt() * globals.M2FT;
|
||||
},
|
||||
|
||||
#
|
||||
# Get distance from glider to runway threshold e.g. in case that the user taxi from the runway threshold
|
||||
#
|
||||
# location - Object of location from which the glider start.
|
||||
# Return the distance in metres, of the glider's displacement from the runway threshold.
|
||||
#
|
||||
getGliderOffsetFromRunwayThreshold: func (location) {
|
||||
if (location.type == "runway") {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(location.lat, location.lon);
|
||||
|
||||
return rwyThreshold.distance_to(gliderCoord);
|
||||
}
|
||||
|
||||
# We are not on the runway, return 0 distance
|
||||
return 0;
|
||||
},
|
||||
|
||||
#
|
||||
# Add new waypoint on ground
|
||||
#
|
||||
# wptData - hash object with waypoint data
|
||||
#
|
||||
addWptGround: func (wptData) {
|
||||
wptData.onGround = true;
|
||||
me.addWpt(wptData);
|
||||
},
|
||||
|
||||
#
|
||||
# Add new waypoint in air
|
||||
#
|
||||
# wptData - hash object wtih waypoint data
|
||||
#
|
||||
addWptAir: func (wptData) {
|
||||
if (contains(wptData, "onGround")) {
|
||||
wptData.onGround = nil;
|
||||
}
|
||||
|
||||
me.addWpt(wptData);
|
||||
},
|
||||
|
||||
#
|
||||
# Add "WAIT" waypoint
|
||||
#
|
||||
# sec - Number of seconds for wait
|
||||
#
|
||||
addWptWait: func (sec) {
|
||||
me.addWpt({"waitSec": sec});
|
||||
},
|
||||
|
||||
#
|
||||
# Add "END" waypoint with optional waypoint data
|
||||
#
|
||||
addWptEnd: func (wptData = nil) {
|
||||
if (wptData == nil) {
|
||||
wptData = {};
|
||||
}
|
||||
|
||||
wptData.name = "END";
|
||||
|
||||
me.addWpt(wptData);
|
||||
},
|
||||
|
||||
#
|
||||
# Add new waypoint with given waypoint data
|
||||
#
|
||||
# wptData = {
|
||||
# shift: { - Optionally hash with data to calculate next coordinates (lat, lon) and altitude of waypoint
|
||||
# hdgChange - How the aircraft's heading supposed to change? 0 - keep the same heading.
|
||||
# dist - Distance in meters to calculate next waypoint coordinates.
|
||||
# altChange - How the aircraft's altitude is supposed to change? 0 - keep the same altitude.
|
||||
# elevation - Set aircraft altitude as current terrain elevation + given value in feets.
|
||||
# It's best to use for the first point in the air to avoid the plane collapsing into
|
||||
# the ground in a bumpy airport.
|
||||
# },
|
||||
# coord - The geo.Coord object, required if shift is not given
|
||||
# crossAt - Altitude in feet, required if shift is not given
|
||||
# ktas - True airspeed in knots, required
|
||||
# onGround - If true then set on ground, otherwise set in air
|
||||
# flapsDown - If true then set flaps down, otherwise set flaps up
|
||||
# gearDown - If true then set gear down, otherwise set gear up
|
||||
# waitSec - Number of seconds for WIAT waypoint
|
||||
# }
|
||||
#
|
||||
addWpt: func (wptData) {
|
||||
if (contains(wptData, "shift")) {
|
||||
me.shiftWpt(wptData.shift);
|
||||
|
||||
# Set coord and crossAt updated by shiftWpt()
|
||||
wptData.coord = me.coord;
|
||||
wptData.crossAt = me.altitude;
|
||||
}
|
||||
|
||||
var wpt = Waypoint.new().setHashData(wptData);
|
||||
|
||||
me.flightPlanWriter.write(wpt);
|
||||
},
|
||||
|
||||
#
|
||||
# Calculate heading, coordinates and altitude from data in wptShift hash
|
||||
#
|
||||
# wptShift - hash object with data
|
||||
#
|
||||
shiftWpt: func (wptShift) {
|
||||
if (!contains(wptShift, "hdgChange")) {
|
||||
die("ERROR aerotow add-on: missing 'hdgChange' for computeWptShift");
|
||||
}
|
||||
|
||||
if (!contains(wptShift, "dist")) {
|
||||
die("ERROR aerotow add-on: missing 'dist' for computeWptShift");
|
||||
}
|
||||
|
||||
# Shift heading and coordinates
|
||||
me.heading = me.correctHeading(me.heading + wptShift.hdgChange);
|
||||
me.coord.apply_course_distance(me.heading, wptShift.dist);
|
||||
|
||||
if (contains(wptShift, "elevation")) {
|
||||
# Set the altitude as the elevation for coordinates of the point plus the given elevation
|
||||
me.altitude = me.getEleveationInFt(me.coord) + wptShift.elevation;
|
||||
return;
|
||||
}
|
||||
|
||||
if (contains(wptShift, "altChange")) {
|
||||
# Change altitude by given altChange
|
||||
me.altitude += wptShift.altChange;
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Correct the heading value that to be from 0 to 360
|
||||
#
|
||||
# heading - heading for correction
|
||||
# Return heading in range from 0 to 360.
|
||||
#
|
||||
correctHeading: func (heading) {
|
||||
if (heading < 0) {
|
||||
heading += 360;
|
||||
}
|
||||
|
||||
if (heading > 360) {
|
||||
heading -= 360;
|
||||
}
|
||||
|
||||
return heading;
|
||||
},
|
||||
|
||||
#
|
||||
# Get elevation of given coordinates in feet
|
||||
#
|
||||
# coord - geo.Coord object
|
||||
#
|
||||
getEleveationInFt: func (coord) {
|
||||
if (coord == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return geo.elevation(coord.lat(), coord.lon()) * globals.M2FT;
|
||||
},
|
||||
};
|
||||
125
nasal/io/flight-plan-writer.nas
Normal file
@@ -0,0 +1,125 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class FlightPlanWriter for write flight plan to the XML file
|
||||
#
|
||||
var FlightPlanWriter = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon) {
|
||||
var obj = { parents: [FlightPlanWriter] };
|
||||
|
||||
obj.fpFileHandler = nil; # Handler for wrire flight plan to file
|
||||
obj.flightPlanPath = addon.storagePath ~ "/AI/FlightPlans/" ~ FlightPlan.FILENAME_FLIGHTPLAN;
|
||||
obj.wptCount = 1;
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Open XML file to wrire flight plan
|
||||
#
|
||||
open: func () {
|
||||
me.wptCount = 1;
|
||||
|
||||
me.fpFileHandler = io.open(me.flightPlanPath, "w");
|
||||
|
||||
if (me.fpFileHandler) {
|
||||
io.write(
|
||||
me.fpFileHandler,
|
||||
"<?xml version=\"1.0\"?>\n\n" ~
|
||||
"<!-- This file is generated automatically by the Aerotow Everywhere add-on -->\n\n" ~
|
||||
"<PropertyList>\n" ~
|
||||
" <flightplan>\n"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Write single waypoint to XML file with flight plan
|
||||
#
|
||||
# wpt - Waypoint object
|
||||
#
|
||||
write: func (wpt) {
|
||||
if (!me.fpFileHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wpt.name == nil) {
|
||||
wpt.setName(me.wptCount);
|
||||
}
|
||||
|
||||
var str = " <wpt>\n"
|
||||
~ " <name>" ~ wpt.name ~ "</name>\n";
|
||||
|
||||
if (wpt.coord != nil) {
|
||||
str ~= " <lat>" ~ wpt.coord.lat() ~ "</lat>\n";
|
||||
str ~= " <lon>" ~ wpt.coord.lon() ~ "</lon>\n";
|
||||
str ~= " <!--\n"
|
||||
~ " " ~ wpt.coord.lat() ~ "," ~ wpt.coord.lon() ~ "\n"
|
||||
~ " -->\n";
|
||||
}
|
||||
|
||||
if (wpt.alt != nil) {
|
||||
str ~= " <alt>" ~ wpt.alt ~ "</alt>\n";
|
||||
}
|
||||
|
||||
if (wpt.crossAt != nil) {
|
||||
str ~= " <crossat>" ~ wpt.crossAt ~ "</crossat>\n";
|
||||
}
|
||||
|
||||
if (wpt.ktas != nil) {
|
||||
str ~= " <ktas>" ~ wpt.ktas ~ "</ktas>\n";
|
||||
}
|
||||
|
||||
if (wpt.onGround != nil) {
|
||||
str ~= " <on-ground>" ~ (wpt.onGround ? "true" : "false") ~ "</on-ground>\n";
|
||||
}
|
||||
|
||||
if (wpt.waitSec != nil) {
|
||||
str ~= " <time-sec>" ~ wpt.waitSec ~ "</time-sec>\n";
|
||||
}
|
||||
|
||||
if (wpt.flapsDown != nil) {
|
||||
str ~= " <flaps-down>" ~ (wpt.flapsDown ? "true" : "false") ~ "</flaps-down>\n";
|
||||
}
|
||||
|
||||
if (wpt.gearDown != nil) {
|
||||
str ~= " <gear-down>" ~ (wpt.gearDown ? "true" : "false") ~ "</gear-down>\n";
|
||||
}
|
||||
|
||||
str ~= " </wpt>\n";
|
||||
|
||||
io.write(me.fpFileHandler, str);
|
||||
|
||||
me.wptCount += 1;
|
||||
},
|
||||
|
||||
#
|
||||
# Close XML file with flight plan
|
||||
#
|
||||
close: func () {
|
||||
if (me.fpFileHandler) {
|
||||
io.write(
|
||||
me.fpFileHandler,
|
||||
" </flightplan>\n" ~
|
||||
"</PropertyList>\n\n"
|
||||
);
|
||||
|
||||
io.close(me.fpFileHandler);
|
||||
me.fpFileHandler = nil;
|
||||
}
|
||||
},
|
||||
};
|
||||
175
nasal/io/waypoint.nas
Normal file
@@ -0,0 +1,175 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Waypoint represent the data of waypoint in the flight plan.
|
||||
#
|
||||
var Waypoint = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
var obj = { parents: [Waypoint] };
|
||||
|
||||
obj.name = nil; # Name of waypoint. Special names are: "WAIT", "END".
|
||||
obj.coord = nil; # geo.Coord object
|
||||
obj.alt = nil; # Altitude AMSL in feet
|
||||
obj.crossAt = nil; # Altitude AMSL in feet
|
||||
obj.ktas = nil; # True airspeed in knots
|
||||
obj.onGround = nil; # If true then on the ground, otherwise in air
|
||||
obj.flapsDown = nil; # If true then flaps down, otherwise up
|
||||
obj.gearDown = nil; # If true then gear down, otherwise up
|
||||
obj.waitSec = nil; # Number of seconds for "WAIT" waypoint
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Set name of waypoint
|
||||
#
|
||||
# name - Name of waypoint
|
||||
#
|
||||
setName: func (name) {
|
||||
me.name = name;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set coordinates of waypoint
|
||||
#
|
||||
# coord - geo.Coord object
|
||||
#
|
||||
setCoord: func (coord) {
|
||||
me.coord = coord;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set altitude in feet of waypoint
|
||||
#
|
||||
# alt - altitude in feet
|
||||
#
|
||||
setAlt: func (alt) {
|
||||
me.alt = alt;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set altitude in feet of waypoint as cross at
|
||||
#
|
||||
# alt - altitude in feet
|
||||
#
|
||||
setCrossAt: func (crossAt) {
|
||||
me.crossAt = crossAt;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set true airspeed in knots for waypoint
|
||||
#
|
||||
# ktas - true airspeed in knots
|
||||
#
|
||||
setKtas: func (ktas) {
|
||||
me.ktas = ktas;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set waypoint on the ground
|
||||
#
|
||||
setOnGround: func () {
|
||||
me.onGround = 1;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set flaps down
|
||||
#
|
||||
setFlapsDown: func () {
|
||||
me.flapsDown = 1;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set gear down
|
||||
#
|
||||
setGearDown: func () {
|
||||
me.gearDown = 1;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set number of seconds for WAIT waypoint. This force to use "WAIT" name.
|
||||
#
|
||||
# waitSec - Number of seconds
|
||||
#
|
||||
setWaitSec: func (waitSec) {
|
||||
me.setName("WAIT"); # force WAIT name
|
||||
me.waitSec = waitSec;
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
#
|
||||
# Set all waypoint data from given hash object
|
||||
#
|
||||
setHashData: func (wptData) {
|
||||
if (wptData == nil) {
|
||||
return me;
|
||||
}
|
||||
|
||||
if (contains(wptData, "name")) {
|
||||
me.setName(wptData.name);
|
||||
}
|
||||
|
||||
if (contains(wptData, "coord")) {
|
||||
me.setCoord(wptData.coord);
|
||||
}
|
||||
|
||||
if (contains(wptData, "crossAt")) {
|
||||
me.setCrossAt(wptData.crossAt);
|
||||
}
|
||||
|
||||
if (contains(wptData, "alt")) {
|
||||
me.setAlt(wptData.alt);
|
||||
}
|
||||
|
||||
if (contains(wptData, "ktas")) {
|
||||
me.setKtas(wptData.ktas);
|
||||
}
|
||||
|
||||
if (contains(wptData, "onGround") and wptData.onGround) {
|
||||
me.setOnGround();
|
||||
}
|
||||
|
||||
if (contains(wptData, "flapsDown") and wptData.flapsDown) {
|
||||
me.setFlapsDown();
|
||||
}
|
||||
|
||||
if (contains(wptData, "gearDown") and wptData.gearDown) {
|
||||
me.setGearDown();
|
||||
}
|
||||
|
||||
if (contains(wptData, "waitSec")) {
|
||||
me.setWaitSec(wptData.waitSec);
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
};
|
||||
66
nasal/message.nas
Normal file
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Message for display messages on the screen with read by speech synthesizer.
|
||||
#
|
||||
var Message = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return { parents: [Message] };
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message as OK.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
success: func (message) {
|
||||
me.display(message, "ok");
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message as an error.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
error: func (message) {
|
||||
me.display(message, "error");
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
# type - The type of message. It can take values as "ok" or "error".
|
||||
#
|
||||
display: func (message, type) {
|
||||
# Print to console
|
||||
print("Aerotow Everywhere add-on: " ~ message);
|
||||
|
||||
# Read the message by speech synthesizer
|
||||
props.globals.getNode("/sim/sound/voices/ai-plane").setValue(message);
|
||||
|
||||
# Display message on the screen
|
||||
var durationInSec = int(size(message) / 12) + 3;
|
||||
var window = screen.window.new(nil, -40, 10, durationInSec);
|
||||
window.bg = [0.0, 0.0, 0.0, 0.40];
|
||||
|
||||
window.fg = type == "error"
|
||||
? [1.0, 0.0, 0.0, 1]
|
||||
: [0.0, 1.0, 0.0, 1];
|
||||
|
||||
window.align = "center";
|
||||
window.write(message);
|
||||
},
|
||||
};
|
||||
182
nasal/scenario.nas
Normal file
@@ -0,0 +1,182 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Scenario for create AI scenario XML file and attach it to scenario property list for load it.
|
||||
#
|
||||
var Scenario = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
SCENARIO_ID: "aerotow_addon",
|
||||
SCENARIO_NAME: "Aerotow Add-on",
|
||||
SCENARIO_DESC: "This scenario starts the towing plane at the airport where the pilot with the glider is located. Use Ctrl-o to hook the plane.",
|
||||
FILENAME_SCENARIO: "aerotown-addon.xml",
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [Scenario] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.listeners = [];
|
||||
obj.routeDialog = RouteDialog.new(addon, message);
|
||||
obj.flightPlan = FlightPlan.new(addon, message, obj.routeDialog);
|
||||
obj.isScenarioLoaded = false;
|
||||
obj.scenarioPath = addon.storagePath ~ "/" ~ Scenario.FILENAME_SCENARIO;
|
||||
|
||||
obj.flightPlan.initial();
|
||||
|
||||
append(obj.listeners, setlistener("/sim/presets/longitude-deg", func () {
|
||||
# User change airport/runway
|
||||
obj.flightPlan.initial();
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
me.routeDialog.del();
|
||||
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Generate the XML file with the AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/aerotown-addon.xml.
|
||||
#
|
||||
# Return true on successful, otherwise false
|
||||
#
|
||||
generateXml: func () {
|
||||
if (!me.flightPlan.generateXml()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var scenarioXml = {
|
||||
"PropertyList": {
|
||||
"scenario": {
|
||||
"name": Scenario.SCENARIO_NAME,
|
||||
"description": Scenario.SCENARIO_DESC,
|
||||
"entry": {
|
||||
"callsign": "FG-TOW",
|
||||
"type": "aircraft",
|
||||
"class": "aerotow-dragger",
|
||||
"model": Aircraft.getSelected(me.addon).modelPath,
|
||||
"flightplan": FlightPlan.FILENAME_FLIGHTPLAN,
|
||||
"repeat": true, # start again indefinitely, it will work if the aircraft stops on the ground
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var node = props.Node.new(scenarioXml);
|
||||
io.writexml(me.scenarioPath, node);
|
||||
|
||||
me.addScenarioToPropertyList();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
#
|
||||
# Add our new scenario to the "/sim/ai/scenarios" property list
|
||||
# so that FlightGear will be able to load it by "load-scenario" command.
|
||||
#
|
||||
addScenarioToPropertyList: func () {
|
||||
if (!me.isAlreadyAdded()) {
|
||||
var scenarioData = {
|
||||
"name": Scenario.SCENARIO_NAME,
|
||||
"id": Scenario.SCENARIO_ID,
|
||||
"description": Scenario.SCENARIO_DESC,
|
||||
"path": me.scenarioPath,
|
||||
};
|
||||
|
||||
props.globals.getNode("/sim/ai/scenarios").addChild("scenario").setValues(scenarioData);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Return true if scenario is already added to "/sim/ai/scenarios" property list, otherwise return false.
|
||||
#
|
||||
isAlreadyAdded: func () {
|
||||
foreach (var scenario; props.globals.getNode("/sim/ai/scenarios").getChildren("scenario")) {
|
||||
var id = scenario.getChild("id");
|
||||
if (id != nil and id.getValue() == Scenario.SCENARIO_ID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
#
|
||||
# Load scenario
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
load: func () {
|
||||
var args = props.Node.new({ "name": Scenario.SCENARIO_ID });
|
||||
if (fgcommand("load-scenario", args)) {
|
||||
me.isScenarioLoaded = true;
|
||||
me.message.success("Let's fly!");
|
||||
|
||||
# Enable engine sound
|
||||
setprop(me.addonNodePath ~ "/addon-devel/sound/enable", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
me.message.error("Tow failed!");
|
||||
return false;
|
||||
},
|
||||
|
||||
#
|
||||
# Unload scenario
|
||||
#
|
||||
# withMessages - Set true to display messages.
|
||||
#
|
||||
# Return true on successful, otherwise false.
|
||||
#
|
||||
unload: func (withMessages = 0) {
|
||||
if (me.isScenarioLoaded) {
|
||||
var args = props.Node.new({ "name": Scenario.SCENARIO_ID });
|
||||
if (fgcommand("unload-scenario", args)) {
|
||||
me.isScenarioLoaded = false;
|
||||
|
||||
if (withMessages) {
|
||||
me.message.success("Aerotown disabled");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (withMessages) {
|
||||
me.message.error("Aerotown disable failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (withMessages) {
|
||||
me.message.success("Aerotown already disabled");
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
41
nasal/timer.nas
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Timer for wrapping maketimer() function.
|
||||
#
|
||||
var Timer = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return { parents: [Timer] };
|
||||
},
|
||||
|
||||
#
|
||||
# Run timer as single shot
|
||||
#
|
||||
# delaySec - Delay in seconds for execute timer's callback.
|
||||
# self - Specifying what any "me" references in the function being called will refer to.
|
||||
# callback - Function to be called after given delay.
|
||||
#
|
||||
# Return timer handler object
|
||||
#
|
||||
singleShot: func (delaySec, self, callback) {
|
||||
var timer = maketimer(delaySec, self, func () {
|
||||
callback();
|
||||
});
|
||||
timer.singleShot = true;
|
||||
timer.start();
|
||||
|
||||
return timer;
|
||||
},
|
||||
};
|
||||
97
thermal.nas
@@ -1,97 +0,0 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
var addon = addons.getAddon("org.flightgear.addons.Aerotow");
|
||||
|
||||
#
|
||||
# Variables
|
||||
#
|
||||
var g_thermalListeners = [];
|
||||
|
||||
#
|
||||
# Initialize thermal module
|
||||
#
|
||||
var init = func () {
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/distance-m", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/distance-nm",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/distance-m") * globals.M2NM
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/strength-fps", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/strength-mps",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/strength-fps") * globals.FPS2KT * globals.KT2MPS
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-m",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft") * globals.FT2M
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl-m",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl") * globals.FT2M
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
#
|
||||
# Uninitialize thermal module
|
||||
#
|
||||
var uninit = func () {
|
||||
foreach (var listener; g_thermalListeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Add thermal 300 m before glider position.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var add = func () {
|
||||
var heading = getprop("/orientation/heading-deg") or 0;
|
||||
var distance = getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/distance-m") or 300;
|
||||
|
||||
var position = geo.aircraft_position();
|
||||
position.apply_course_distance(heading, distance);
|
||||
|
||||
# Get random layer from 1 do 4
|
||||
var layer = int(rand() * 4) + 1;
|
||||
|
||||
var args = props.Node.new({
|
||||
"type": "thermal",
|
||||
"model": "Models/Weather/altocumulus_layer" ~ layer ~ ".xml",
|
||||
"latitude": position.lat(),
|
||||
"longitude": position.lon(),
|
||||
"strength-fps": getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/strength-fps") or 16.0,
|
||||
"diameter-ft": getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft") or 4000,
|
||||
"height-msl": getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl") or 9000,
|
||||
"search-order": "DATA_ONLY"
|
||||
});
|
||||
|
||||
if (fgcommand("add-aiobject", args)) {
|
||||
messages.displayOk("The thermal has been added");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Adding thermal failed");
|
||||
return 0;
|
||||
}
|
||||