Fix AI aircraft going underground during take-off due to uneven runway elevations.

Add possibility to set thermal with configuration of its parameters.
This commit is contained in:
PlayeRom
2022-08-16 03:27:37 +02:00
parent 2fa6110ba0
commit 602396729c
8 changed files with 424 additions and 36 deletions
+12
View File
@@ -0,0 +1,12 @@
# CHANGELOG
## v.1.0.1
- Fix AI aircraft going underground during take-off due to uneven runway elevations.
- Possibility to add thermal with configuration of its parameters.
## v.1.0.0
- Possibility to call a towing aircraft at any airport
- One defined route for the towing aircraft
- 3 types of tow airplanes
+17 -3
View File
@@ -32,14 +32,28 @@ You can disconnect from the aircraft at any time, most often by pressing the `o`
## Menu of add-on
This add-on add a new item to main menu named "Aerotow Everywhere" with following items:
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. `Help` - display help dialog.
6. `About` - display about dialog with add-on information.
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.
## Adding thermals
An additional feature of this add-on is the possibility of placing the thermals just in front of the glider. To do this, go to menu `Aerotow Everywhere` -> `Add thermal` where you can configure the following parameters:
1. `Distance` - the distance in metres at which the thermals will be placed in front of the gliders.
2. `Strength` - thermal strength in feet per second.
3. `Diameter` - diameter of thermal in feet.
4. `Height` - height of thermal in feet above mean sea level.
Click `Add thermal` button for add the thermal.
Many thanks to the forum user "wlbragg" for proposing and presenting a solution to this feature.
## Limitations
+10
View File
@@ -27,6 +27,16 @@
<org.flightgear.addons.Aerotow>
<addon-devel>
<ai-model type="string">Cub</ai-model>
<add-thermal>
<distance-m type="int">300</distance-m>
<distance-nm type="double">0.16</distance-nm>
<strength-fps type="double">16.0</strength-fps>
<strength-mps type="double">4.88</strength-mps>
<diameter-ft type="int">4000</diameter-ft>
<diameter-m type="int">1219</diameter-m>
<height-msl type="int">9000</height-msl>
<height-msl-m type="int">2742</height-msl-m>
</add-thermal>
<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).
+13 -7
View File
@@ -21,11 +21,15 @@ var unload = func(addon) {
#
# Other resources should be freed by adding the corresponding code here,
# e.g. myCanvas.del();
aerotow.uninit();
thermal.uninit();
}
var main = func(addon) {
print("Aerotow Everywhere add-on initialized from path ", addon.basePath);
# Create $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow directory
addon.createStorageDir();
# Create /AI/FlightPlans/ directory in $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/
@@ -36,17 +40,19 @@ var main = func(addon) {
loadExtraNasalFiles(addon);
setlistener(addon.node.getPath() ~ "/addon-devel/ai-model", func(n) {
aerotow.startAerotow();
});
aerotow.init();
thermal.init();
}
#
# Load extra Nasal files in main add-on directory
#
var loadExtraNasalFiles = func (addon) {
foreach (var scriptName; ["aerotow", "messages"]) {
foreach (var scriptName; ["aerotow", "messages", "thermal"]) {
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
print("Loading Aerotown Add-on module ", fileName);
io.load_nasal(fileName, scriptName);
if (io.load_nasal(fileName, scriptName)) {
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
}
}
}
+12 -5
View File
@@ -53,10 +53,6 @@
<value type="string">c182</value>
</binding>
</item>
<item>
<label>------------------</label>
<enabled>false</enabled>
</item>
<item>
<label>Disable tow aircraft</label>
<binding>
@@ -65,7 +61,18 @@
</binding>
</item>
<item>
<label>------------------</label>
<label>---------------------</label>
<enabled>false</enabled>
</item>
<item>
<label>Add thermal</label>
<binding>
<command>dialog-show</command>
<dialog-name>add-thermal</dialog-name>
</binding>
</item>
<item>
<label>---------------------</label>
<enabled>false</enabled>
</item>
<item>
+49 -21
View File
@@ -30,6 +30,26 @@ 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 = [];
#
# Initialize thermal module
#
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();
}));
}
#
# Uninitialize thermal module
#
var uninit = func () {
foreach (var listener; g_listeners) {
removelistener(listener);
}
}
#
# Main function to prepare AI scenario and run it.
@@ -235,7 +255,7 @@ var generateFlightPlanXml = func () {
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed});
# Takeof
addWptAir({"hdgChange": 0, "dist": 100 * perf.rolling}, {"altChange": 3, "ktas": perf.speed * 1.05});
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
@@ -304,7 +324,7 @@ var getAircraftPerformance = func () {
"rolling": 2,
};
}
if (aiModel == "c182") {
return {
"vs": 295, # ft per 1000 m
@@ -332,7 +352,7 @@ var initAircraftVariable = func (airport, runway, isGliderPos = 1) {
var gliderCoord = geo.aircraft_position();
# Set coordinates as glider position or runway threshold
g_coord = isGliderPos
g_coord = isGliderPos
? gliderCoord
: geo.Coord.new().set_latlon(runway.lat, runway.lon);
@@ -365,7 +385,7 @@ var getMinRunwayLength = func () {
if (aiModel == "DR400") {
return 470;
}
if (aiModel == "c182") {
return 508;
}
@@ -404,14 +424,14 @@ var addWptAir = func (coordOffset, performance) {
# sec - Number of seconds for wait
#
var addWptWait = func (sec) {
wrireWpt("WAIT", {"hdgChange": nil, "dist": nil}, {"altChange": nil, "ktas": nil}, nil, sec);
wrireWpt("WAIT", {}, {}, nil, sec);
}
#
# Add "END" waypoint
#
var addWptEnd = func () {
wrireWpt("END", {"hdgChange": nil, "dist": nil}, {"altChange": nil, "ktas": nil});
wrireWpt("END", {}, {});
}
#
@@ -421,6 +441,9 @@ var addWptEnd = func () {
# 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
@@ -432,8 +455,8 @@ var wrireWpt = func (
groundAir = nil,
sec = nil
) {
var localCoord = nil;
if (coordOffset.hdgChange != nil and coordOffset.dist != 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;
@@ -444,24 +467,29 @@ var wrireWpt = func (
}
g_coord.apply_course_distance(g_heading, coordOffset.dist);
localCoord = g_coord;
coord = g_coord;
}
var localAlt = nil;
if (performance.altChange != nil) {
g_altitude = g_altitude + performance.altChange;
localAlt = g_altitude;
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,
localCoord,
localAlt,
performance.ktas,
groundAir,
sec
);
var data = getWptString(name, coord, alt, ktas, groundAir, sec);
io.write(g_fpFileHandler, data);
+214
View File
@@ -0,0 +1,214 @@
<?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>add-thermal</name>
<resizable>false</resizable>
<layout>vbox</layout>
<default-padding>3</default-padding>
<group>
<layout>hbox</layout>
<empty>
<stretch>1</stretch>
</empty>
<text>
<label>Add thermal</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> This option allows the thermal to be placed at the distance </label>
</text>
<text>
<halign>left</halign>
<label> designated below in front of the glider along with other parameters. </label>
</text>
<hrule/>
<group>
<layout>table</layout>
<text>
<row>0</row>
<col>0</col>
<halign>right</halign>
<label> Distance in front of the glider</label>
</text>
<input>
<row>0</row>
<col>1</col>
<label>m</label>
<halign>left</halign>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/distance-m</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<row>0</row>
<col>2</col>
<label>Dummy label</label>
<halign>left</halign>
<format>(%.2f nm)</format>
<live>true</live>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/distance-nm</property>
</text>
<text>
<row>1</row>
<col>0</col>
<halign>right</halign>
<label>Strength</label>
</text>
<input>
<row>1</row>
<col>1</col>
<label>ft/s</label>
<halign>left</halign>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/strength-fps</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<row>1</row>
<col>2</col>
<label>Dummy label</label>
<halign>left</halign>
<format> (%.2f m/s)</format>
<live>true</live>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/strength-mps</property>
</text>
<text>
<row>2</row>
<col>0</col>
<halign>right</halign>
<label>Diameter</label>
</text>
<input>
<row>2</row>
<col>1</col>
<label>ft</label>
<halign>left</halign>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/diameter-ft</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<row>2</row>
<col>2</col>
<label>Dummy label</label>
<halign>left</halign>
<format>(%.f m)</format>
<live>true</live>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/diameter-m</property>
</text>
<text>
<row>3</row>
<col>0</col>
<halign>right</halign>
<label>Height MSL</label>
</text>
<input>
<row>3</row>
<col>1</col>
<label>ft</label>
<halign>left</halign>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/height-msl</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
</binding>
</input>
<text>
<row>3</row>
<col>2</col>
<label>Dummy label</label>
<halign>left</halign>
<format>(%.f m)</format>
<live>true</live>
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/height-msl-m</property>
</text>
</group>
<hrule/>
<group>
<empty>
<stretch>true</stretch>
</empty>
<layout>hbox</layout>
<button>
<legend>Add thermal</legend>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script><![CDATA[thermal.add();]]></script>
</binding>
<binding>
<command>dialog-close</command>
</binding>
</button>
<button>
<legend>Cancel</legend>
<equal>true</equal>
<default>true</default>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
<empty>
<stretch>true</stretch>
</empty>
</group>
</PropertyList>
+97
View File
@@ -0,0 +1,97 @@
#
# 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;
}