mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-11 11:48:38 +00:00
Merge branch 'develop' into FF/Fox2
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
--- **Ops** - (R2.5) - Automatic Terminal Information Service (ATIS).
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **Main Features:**
|
||||
@@ -28,9 +28,9 @@
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions: Example missions will be added later.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Sound files: Check out the pinned messages in the Moose discord #ops-atis channel.
|
||||
--
|
||||
-- ===
|
||||
@@ -63,7 +63,7 @@
|
||||
-- @field #number subduration Duration how long subtitles are displayed in seconds.
|
||||
-- @field #boolean metric If true, use metric units. If false, use imperial (default).
|
||||
-- @field #boolean PmmHg If true, give pressure in millimeters of Mercury. Default is inHg for imperial and hecto Pascal (=mili Bars) for metric units.
|
||||
-- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system.
|
||||
-- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system.
|
||||
-- @field #number zuludiff Time difference local vs. zulu in hours.
|
||||
-- @field #number magvar Magnetic declination/variation at the airport in degrees.
|
||||
-- @extends Core.Fsm#FSM
|
||||
@@ -75,108 +75,110 @@
|
||||
-- 
|
||||
--
|
||||
-- # The ATIS Concept
|
||||
--
|
||||
--
|
||||
-- Automatic terminal information service, or ATIS, is a continuous broadcast of recorded aeronautical information in busier terminal areas, i.e. airports and their immediate surroundings.
|
||||
-- ATIS broadcasts contain essential information, such as current weather information, active runways, and any other information required by the pilots.
|
||||
--
|
||||
--
|
||||
-- # DCS Limitations
|
||||
--
|
||||
--
|
||||
-- Unfortunately, the DCS API only allow to get the temperature, pressure as well as wind direction and speed. Therefore, some other information such as cloud coverage, base and ceiling are not available
|
||||
-- when dynamic weather is used.
|
||||
--
|
||||
--
|
||||
-- # Scripting
|
||||
--
|
||||
--
|
||||
-- The lua script to create an ATIS at an airport is pretty easy:
|
||||
--
|
||||
--
|
||||
-- -- ATIS at Batumi Airport on 143.00 MHz AM.
|
||||
-- atisBatumi=ATIS:New("Batumi", 143.00)
|
||||
-- atisBatumi:Start()
|
||||
--
|
||||
--
|
||||
-- The @{#ATIS.New}(*airbasename*, *frequency*) creates a new ATIS object. The parameter *airbasename* is the name of the airbase or airport. Note that this has to be spelled exactly as in the DCS mission editor.
|
||||
-- The parameter *frequency* is the frequency the ATIS broadcasts in MHz.
|
||||
--
|
||||
-- Broadcasting is started via the @{#ATIS.Start}() function. The start can be delayed by useing @{#ATIS.__Start}(*delay*), where *delay* is the delay in seconds.
|
||||
--
|
||||
--
|
||||
-- Broadcasting is started via the @{#ATIS.Start}() function. The start can be delayed by useing @{#ATIS.__Start}(*delay*), where *delay* is the delay in seconds.
|
||||
--
|
||||
-- ## Subtitles
|
||||
--
|
||||
-- Currently, DCS allows for displaying subtitles of radio transmissions only from airborne units, i.e. airplanes and helicopters. Therefore, if you want to have subtitles, it is necessary to place an
|
||||
--
|
||||
-- Currently, DCS allows for displaying subtitles of radio transmissions only from airborne units, i.e. airplanes and helicopters. Therefore, if you want to have subtitles, it is necessary to place an
|
||||
-- additonal aircraft on the ATIS airport and set it to uncontrolled. This unit can then function as a radio relay to transmit messages with subtitles. These subtitles will only be displayed, if the
|
||||
-- player has tuned in the correct ATIS frequency.
|
||||
--
|
||||
--
|
||||
-- Radio transmissions via an airborne unit can be set via the @{#ATIS.SetRadioRelayUnitName}(*unitname*) function, where the parameter *unitname* is the name of the unit passed as string, e.g.
|
||||
--
|
||||
--
|
||||
-- atisBatumi:SetRadioRelayUnitName("Radio Relay Batumi")
|
||||
--
|
||||
--
|
||||
-- With a unit set in the mission editor with name "Radio Relay Batumi".
|
||||
--
|
||||
--
|
||||
-- By default, subtitles are displayed for 10 seconds. This can be changed using @{#ATIS.SetSubtitleDuration}(*duration*) with *duration* being the duration in seconds.
|
||||
--
|
||||
--
|
||||
-- ## Active Runway
|
||||
--
|
||||
-- By default, the currently active runway is determined automatically by analysing the wind direction. However, there are special cases, where this does not yield the correct result.
|
||||
-- Also, there are airports with more than one runway facing in the same direction (usually denoted left and right). In this case, there is obviously no *unique* result depending on the wind vector.
|
||||
--
|
||||
--
|
||||
-- By default, the currently active runway is determined automatically by analysing the wind direction. Therefore, you should obviously set the wind speed to be greater zero in your mission.
|
||||
--
|
||||
-- Note however, there are a few special cases, where automatic detection does not yield the correct or desired result.
|
||||
-- For example, there are airports with more than one runway facing in the same direction (usually denoted left and right). In this case, there is obviously no *unique* result depending on the wind vector.
|
||||
--
|
||||
-- If the automatic runway detection fails, the active runway can be specified manually in the script via the @{#ATIS.SetActiveRunway}(*runway*) function.
|
||||
-- The parameter *runway* is a string which can be used to specify the runway heading and, if applicable, whether the left or right runway is in use.
|
||||
--
|
||||
--
|
||||
-- For example, setting runway 21L would be
|
||||
--
|
||||
--
|
||||
-- atisNellis:SetActiveRunway("21L")
|
||||
--
|
||||
--
|
||||
-- The script will examine the string and search for the characters "L" (left) and "R" (right).
|
||||
--
|
||||
--
|
||||
-- If only left or right should be set and the direction determined by the wind vector, the runway heading can be left out, e.g.
|
||||
--
|
||||
--
|
||||
-- atisAbuDhabi:SetActiveRunway("L")
|
||||
--
|
||||
--
|
||||
-- The first two digits of the runway are determined by converting the *true* runway heading into its magnetic heading. The magnetic declination (or variation) is assumed to be constant on the given map.
|
||||
-- The magnatic declinatin can also be specified for the specific airport using the @{#ATIS.SetMagneticDeclination}(*magvar*).
|
||||
--
|
||||
--
|
||||
-- ## Tower Frequencies
|
||||
--
|
||||
--
|
||||
-- The tower frequency (or frequencies) can also be included in the ATIS information. However, there is no way to get these automatically. Therefore, it is necessary to manually specify them in the script via the
|
||||
-- @{#ATIS.SetTowerFrequencies}(*frequencies*) function. The parameter *frequencies* can be a plain number if only one frequency is necessary or it can be a table of frequencies.
|
||||
--
|
||||
--
|
||||
-- ## Unit System
|
||||
--
|
||||
--
|
||||
-- By default, information is given in imperial units, i.e. wind speed in knots, pressure in inches of mercury, visibility in Nautical miles, etc.
|
||||
--
|
||||
--
|
||||
-- If you prefer metric units, you can enable this via the @{#ATIS.SetMetricUnits}() function,
|
||||
--
|
||||
--
|
||||
-- atisBatumi:SetMetricUnits()
|
||||
--
|
||||
--
|
||||
-- With this, wind speed is given in meters per second, pressure in hecto Pascal (mbar), visibility in kilometers etc.
|
||||
--
|
||||
--
|
||||
-- # Sound Files
|
||||
--
|
||||
-- More than 180 individual sound files have been created using a text-to-speech program. All ATIS information is given with en-US accent.
|
||||
--
|
||||
--
|
||||
-- More than 180 individual sound files have been created using a text-to-speech program. All ATIS information is given with en-US accent.
|
||||
--
|
||||
-- Check out the pinned messages in the Moose discord #ops-atis channel.
|
||||
--
|
||||
--
|
||||
-- To include the files, open the mission (.miz) file with, e.g., 7-zip. Then just drag-n-drop the file into the miz.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- **Note** that the default folder name is *ATIS Soundfiles/*. If you want to change it, you can use the @{#ATIS.SetSoundfilesPath}(*path*), where *path* is the path of the directory. This must end with a slash "/"!
|
||||
--
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
--
|
||||
-- ## Caucasus: Batumi
|
||||
--
|
||||
--
|
||||
-- -- ATIS Batumi Airport on 143.00 MHz AM.
|
||||
-- atisBatumi=ATIS:New(AIRBASE.Caucasus.Batumi, 143.00)
|
||||
-- atisBatumi:SetRadioRelayUnitName("Radio Relay Batumi")
|
||||
-- atisBatumi:Start()
|
||||
--
|
||||
--
|
||||
-- ## Nevada: Nellis AFB
|
||||
--
|
||||
--
|
||||
-- -- ATIS Nellis AFB on 270.100 MHz AM.
|
||||
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis_AFB, 270.100)
|
||||
-- atisNellis:SetRadioRelayUnitName("Radio Relay Nellis")
|
||||
-- atisNellis:SetActiveRunway("21L")
|
||||
-- atisNellis:SetTowerFrequencies({327.000, 132.550})
|
||||
-- atisNellis:Start()
|
||||
--
|
||||
--
|
||||
-- ## Persian Gulf: Abu Dhabi International Airport
|
||||
--
|
||||
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_International_Airport, 125.1)
|
||||
@@ -184,7 +186,7 @@
|
||||
-- atisAbuDhabi:SetMetricUnits()
|
||||
-- atisAbuDhabi:SetActiveRunway("L")
|
||||
-- atisAbuDhabi:Start()
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #ATIS
|
||||
ATIS = {
|
||||
@@ -271,24 +273,24 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
|
||||
-- Inherit everything from WAREHOUSE class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #ATIS
|
||||
|
||||
|
||||
self.airbasename=airbasename
|
||||
self.airbase=AIRBASE:FindByName(airbasename)
|
||||
|
||||
|
||||
if self.airbase==nil then
|
||||
self:E("ERROR: Airbase %s for ATIS could not be found!", tostring(airbasename))
|
||||
end
|
||||
|
||||
|
||||
-- Default freq and modulation.
|
||||
self.frequency=frequency or 143.00
|
||||
self.modulation=modulation or 0
|
||||
|
||||
|
||||
-- Get map.
|
||||
self.theatre=env.mission.theatre
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("ATIS %s | ", self.airbasename)
|
||||
|
||||
|
||||
-- Defaults:
|
||||
self:SetSoundfilesPath()
|
||||
self:SetSubtitleDuration()
|
||||
@@ -302,7 +304,7 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
self:AddTransition("*", "Status", "*") -- Update status.
|
||||
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
|
||||
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
|
||||
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
------------------------
|
||||
@@ -367,7 +369,7 @@ end
|
||||
function ATIS:SetTowerFrequencies(freqs)
|
||||
if type(freqs)=="table" then
|
||||
-- nothing to do
|
||||
else
|
||||
else
|
||||
freqs={freqs}
|
||||
end
|
||||
self.towerfrequency=freqs
|
||||
@@ -410,7 +412,7 @@ function ATIS:SetImperialUnits()
|
||||
end
|
||||
|
||||
--- Set pressure unit to millimeters of mercury (mmHg).
|
||||
-- Default is inHg for imperial and hPa (=mBar) for metric units.
|
||||
-- Default is inHg for imperial and hPa (=mBar) for metric units.
|
||||
-- @param #ATIS self
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetPressureMillimetersMercury()
|
||||
@@ -427,14 +429,14 @@ function ATIS:SetTemperatureFahrenheit()
|
||||
end
|
||||
|
||||
--- Set magnetic declination/variation at the airport.
|
||||
--
|
||||
--
|
||||
-- Default is per map:
|
||||
--
|
||||
--
|
||||
-- * Caucasus +6 (East), year ~ 2011
|
||||
-- * NTTR +12 (East), year ~ 2011
|
||||
-- * Normandy -10 (West), year ~ 1944
|
||||
-- * Persian Gulf +2 (East), year ~ 2011
|
||||
--
|
||||
--
|
||||
-- @param #ATIS self
|
||||
-- @param #number magvar Magnetic variation in degrees.
|
||||
-- @return #ATIS self
|
||||
@@ -445,12 +447,12 @@ end
|
||||
|
||||
--- Set time local difference with respect to Zulu time.
|
||||
-- Default is per map:
|
||||
--
|
||||
--
|
||||
-- * Caucasus +4
|
||||
-- * Nevada -7
|
||||
-- * Normandy +1
|
||||
-- * Persian Gulf +4
|
||||
--
|
||||
--
|
||||
-- @param #ATIS self
|
||||
-- @param #number delta Time difference in hours.
|
||||
-- @return #ATIS self
|
||||
@@ -469,16 +471,16 @@ function ATIS:onafterStart(From, Event, To)
|
||||
|
||||
-- Info.
|
||||
self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation))
|
||||
|
||||
|
||||
-- Start radio queue.
|
||||
self.radioqueue=RADIOQUEUE:New(self.frequency, self.modulation)
|
||||
|
||||
|
||||
-- Send coordinate is airbase coord.
|
||||
self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
|
||||
|
||||
|
||||
-- Set relay unit if we have one.
|
||||
self.radioqueue:SetSenderUnitName(self.relayunitname)
|
||||
|
||||
|
||||
-- Init numbers.
|
||||
self.radioqueue:SetDigit(0, "N-0.ogg", 0.55, self.soundpath)
|
||||
self.radioqueue:SetDigit(1, "N-1.ogg", 0.40, self.soundpath)
|
||||
@@ -490,7 +492,7 @@ function ATIS:onafterStart(From, Event, To)
|
||||
self.radioqueue:SetDigit(7, "N-7.ogg", 0.42, self.soundpath)
|
||||
self.radioqueue:SetDigit(8, "N-8.ogg", 0.37, self.soundpath)
|
||||
self.radioqueue:SetDigit(9, "N-9.ogg", 0.38, self.soundpath)
|
||||
|
||||
|
||||
-- Start radio queue.
|
||||
self.radioqueue:Start(1, 0.1)
|
||||
|
||||
@@ -505,11 +507,11 @@ function ATIS:onafterStatus(From, Event, To)
|
||||
|
||||
-- Get FSM state.
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
|
||||
-- Info text.
|
||||
local text=string.format("State %s", fsmstate)
|
||||
self:I(self.lid..text)
|
||||
|
||||
|
||||
self:__Status(30)
|
||||
end
|
||||
|
||||
@@ -527,7 +529,7 @@ function ATIS:onafterCheckQueue(From, Event, To)
|
||||
else
|
||||
self:T2(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue))
|
||||
end
|
||||
|
||||
|
||||
-- Check back in 5 seconds.
|
||||
self:__CheckQueue(5)
|
||||
end
|
||||
@@ -538,29 +540,29 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
|
||||
-- Get current coordinate.
|
||||
local coord=self.airbase:GetCoordinate()
|
||||
|
||||
|
||||
-- Get elevation.
|
||||
local height=coord:GetLandHeight()+10
|
||||
|
||||
|
||||
----------------
|
||||
--- Pressure ---
|
||||
----------------
|
||||
|
||||
|
||||
-- Pressure in hPa.
|
||||
local qfe=coord:GetPressure(height)
|
||||
local qnh=coord:GetPressure(0)
|
||||
|
||||
|
||||
-- Convert to inHg.
|
||||
if self.PmmHg then
|
||||
qfe=UTILS.hPa2mmHg(qfe)
|
||||
qnh=UTILS.hPa2mmHg(qnh)
|
||||
else
|
||||
else
|
||||
if not self.metric then
|
||||
qfe=UTILS.hPa2inHg(qfe)
|
||||
qnh=UTILS.hPa2inHg(qnh)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local QFE=UTILS.Split(string.format("%.2f", qfe), ".")
|
||||
local QNH=UTILS.Split(string.format("%.2f", qnh), ".")
|
||||
|
||||
@@ -573,18 +575,18 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
QNH=UTILS.Split(string.format("%.1f", qnh), ".")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------------
|
||||
--- Runway ---
|
||||
--------------
|
||||
|
||||
|
||||
-- Get runway based on wind direction.
|
||||
local runway=self.airbase:GetActiveRunway(self.magvar).idx
|
||||
|
||||
|
||||
-- Left or right in case there are two runways with the same heading.
|
||||
local rleft=false
|
||||
local rright=false
|
||||
|
||||
|
||||
-- Check if user explicitly specified a runway.
|
||||
if self.activerunway then
|
||||
local runwayno=self.activerunway:gsub("%D+", "")
|
||||
@@ -594,27 +596,27 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
rleft=self.activerunway:lower():find("l")
|
||||
rright=self.activerunway:lower():find("r")
|
||||
end
|
||||
|
||||
|
||||
------------
|
||||
--- Wind ---
|
||||
------------
|
||||
|
||||
|
||||
-- Get wind direction and speed in m/s.
|
||||
local windFrom, windSpeed=coord:GetWind(height)
|
||||
|
||||
|
||||
|
||||
|
||||
local WINDFROM=string.format("%03d", windFrom)
|
||||
local WINDSPEED=string.format("%d", UTILS.MpsToKnots(windSpeed))
|
||||
|
||||
|
||||
if self.metric then
|
||||
WINDSPEED=string.format("%d", windSpeed)
|
||||
end
|
||||
|
||||
|
||||
------------
|
||||
--- Time ---
|
||||
------------
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
|
||||
-- Conversion to Zulu time.
|
||||
if self.zuludiff then
|
||||
-- User specified.
|
||||
@@ -630,43 +632,43 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
time=time-1*60*60 -- Calais UTC+1 hour
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
local zulu=UTILS.Split(clock, ":")
|
||||
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
||||
|
||||
|
||||
|
||||
|
||||
-- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc.
|
||||
local NATO=ATIS.Alphabet[tonumber(zulu[1])+1]
|
||||
|
||||
|
||||
-- Debug.
|
||||
self:T3({nato=NATO})
|
||||
|
||||
|
||||
-------------------
|
||||
--- Temperature ---
|
||||
-------------------
|
||||
|
||||
-- Temperature in °C.
|
||||
|
||||
-- Temperature in �C.
|
||||
local temperature=coord:GetTemperature(height)
|
||||
|
||||
|
||||
local TEMPERATURE=string.format("%d", temperature)
|
||||
|
||||
|
||||
if self.TDegF then
|
||||
TEMPERATURE=string.format("%d", UTILS.CelciusToFarenheit(temperature))
|
||||
end
|
||||
|
||||
|
||||
---------------
|
||||
--- Weather ---
|
||||
---------------
|
||||
|
||||
|
||||
-- Get mission weather info. Most of this is static.
|
||||
local clouds, visibility, turbulence, fog, dust, static=self:GetMissionWeather()
|
||||
|
||||
|
||||
-- Check that fog is actually "thick" enough to reach the airport. If an airport is in the mountains, fog might not affect it as it is measured from sea level.
|
||||
if fog and fog.thickness<height then
|
||||
fog=nil
|
||||
end
|
||||
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if dust and height>UTILS.FeetToMeters(1500) then
|
||||
dust=nil
|
||||
@@ -675,63 +677,63 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
------------------
|
||||
--- Visibility ---
|
||||
------------------
|
||||
|
||||
|
||||
-- Get min visibility.
|
||||
local visibilitymin=visibility
|
||||
|
||||
|
||||
if fog then
|
||||
if fog.visibility<visibilitymin then
|
||||
visibilitymin=fog.visibility
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if dust then
|
||||
if dust<visibilitymin then
|
||||
visibilitymin=dust
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Visibility in NM.
|
||||
local VISIBILITY=string.format("%d", UTILS.Round(UTILS.MetersToNM(visibilitymin)))
|
||||
|
||||
|
||||
-- Visibility in km.
|
||||
if self.metric then
|
||||
VISIBILITY=string.format("%d", UTILS.Round(visibilitymin/1000))
|
||||
end
|
||||
|
||||
|
||||
--------------
|
||||
--- Clouds ---
|
||||
--------------
|
||||
|
||||
|
||||
local cloudbase=clouds.base
|
||||
local cloudceil=clouds.base+clouds.thickness
|
||||
local clouddens=clouds.density
|
||||
|
||||
|
||||
-- Precepitation: 0=None, 1=Rain, 2=Thunderstorm.
|
||||
local precepitation=tonumber(clouds.iprecptns)
|
||||
|
||||
|
||||
local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase))
|
||||
local CLOUDCEIL=string.format("%d", UTILS.MetersToFeet(cloudceil))
|
||||
|
||||
|
||||
if self.metric then
|
||||
CLOUDBASE=string.format("%d", cloudbase)
|
||||
CLOUDCEIL=string.format("%d", cloudceil)
|
||||
end
|
||||
|
||||
|
||||
-- Cloud base/ceiling in thousands and hundrets of ft/meters.
|
||||
local CLOUDBASE1000, CLOUDBASE0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudbase))
|
||||
local CLOUDCEIL1000, CLOUDCEIL0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudceil))
|
||||
|
||||
|
||||
if self.metric then
|
||||
CLOUDBASE1000, CLOUDBASE0100=self:_GetThousandsAndHundreds(cloudbase)
|
||||
CLOUDCEIL1000, CLOUDCEIL0100=self:_GetThousandsAndHundreds(cloudceil)
|
||||
end
|
||||
|
||||
|
||||
-- No cloud info for dynamic weather.
|
||||
local CLOUDSogg="CloudsNotAvailable.ogg"
|
||||
local CLOUDSsub="Cloud coverage information not available"
|
||||
local CLOUDSdur=2.40
|
||||
|
||||
|
||||
-- Only valid for static weather.
|
||||
if static then
|
||||
if clouddens>=9 then
|
||||
@@ -763,31 +765,31 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
CLOUDSdur=1.00
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------------------
|
||||
--- Transmission ---
|
||||
--------------------
|
||||
|
||||
|
||||
local subduration=self.subduration
|
||||
local subtitle=""
|
||||
|
||||
|
||||
--Airbase name
|
||||
subtitle=string.format("%s", self.airbasename)
|
||||
if self.airbasename:find("AFB")==nil and self.airbasename:find("Airport")==nil and self.airbasename:find("Airstrip")==nil and self.airbasename:find("airfield")==nil and self.airbasename:find("AB")==nil then
|
||||
subtitle=subtitle.." Airport"
|
||||
end
|
||||
self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, subduration)
|
||||
|
||||
|
||||
-- Information tag
|
||||
subtitle=string.format("Information %s", NATO)
|
||||
self.radioqueue:NewTransmission("Information.ogg", 0.85, self.soundpath, nil, 0.5, subtitle, subduration)
|
||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||
|
||||
|
||||
-- Zulu Time
|
||||
subtitle=string.format("%s Zulu Time", ZULU)
|
||||
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
|
||||
self.radioqueue:NewTransmission("TimeZulu.ogg", 0.89, self.soundpath, nil, 0.2, subtitle, subduration)
|
||||
|
||||
|
||||
-- Visibility
|
||||
if self.metric then
|
||||
subtitle=string.format("Visibility %s km", VISIBILITY)
|
||||
@@ -799,9 +801,9 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if self.metric then
|
||||
self.radioqueue:NewTransmission("Kilometers.ogg", 0.78, self.soundpath, nil, 0.2)
|
||||
else
|
||||
self.radioqueue:NewTransmission("NauticalMiles.ogg", 1.05, self.soundpath, nil, 0.2)
|
||||
self.radioqueue:NewTransmission("NauticalMiles.ogg", 1.05, self.soundpath, nil, 0.2)
|
||||
end
|
||||
|
||||
|
||||
-- Cloud base
|
||||
self.radioqueue:NewTransmission(CLOUDSogg, CLOUDSdur, self.soundpath, nil, 1.0, CLOUDSsub, subduration)
|
||||
if CLOUDBASE and static then
|
||||
@@ -815,7 +817,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if tonumber(CLOUDBASE1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE1000)
|
||||
self.radioqueue:NewTransmission("Thousand.ogg", 0.55, self.soundpath, nil, 0.1)
|
||||
end
|
||||
end
|
||||
if tonumber(CLOUDBASE0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE0100)
|
||||
self.radioqueue:NewTransmission("Hundred.ogg", 0.47, self.soundpath, nil, 0.1)
|
||||
@@ -825,7 +827,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if tonumber(CLOUDCEIL1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL1000)
|
||||
self.radioqueue:NewTransmission("Thousand.ogg", 0.55, self.soundpath, nil, 0.1)
|
||||
end
|
||||
end
|
||||
if tonumber(CLOUDCEIL0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL0100)
|
||||
self.radioqueue:NewTransmission("Hundred.ogg", 0.47, self.soundpath, nil, 0.1)
|
||||
@@ -836,7 +838,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:NewTransmission("Feet.ogg", 0.45, self.soundpath, nil, 0.1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Weather phenomena
|
||||
local wp=false
|
||||
local wpsub=""
|
||||
@@ -853,16 +855,16 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if fog then
|
||||
if wp then
|
||||
wpsub=wpsub..","
|
||||
end
|
||||
end
|
||||
wpsub=wpsub.." fog"
|
||||
wp=true
|
||||
wp=true
|
||||
end
|
||||
if dust then
|
||||
if wp then
|
||||
wpsub=wpsub..","
|
||||
end
|
||||
end
|
||||
wpsub=wpsub.." dust"
|
||||
wp=true
|
||||
wp=true
|
||||
end
|
||||
-- Actual output
|
||||
if wp then
|
||||
@@ -876,14 +878,14 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:NewTransmission("Fog.ogg", 0.81, self.soundpath, nil, 0.5)
|
||||
end
|
||||
if dust then
|
||||
self.radioqueue:NewTransmission("Dust.ogg", 0.81, self.soundpath, nil, 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
self.radioqueue:NewTransmission("Dust.ogg", 0.81, self.soundpath, nil, 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
-- Altimeter QNH/QFE.
|
||||
if self.PmmHg then
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
else
|
||||
else
|
||||
if self.metric then
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
else
|
||||
@@ -908,7 +910,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:NewTransmission("InchesOfMercury.ogg", 1.16, self.soundpath, nil, 0.1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Temperature
|
||||
if self.TDegF then
|
||||
subtitle=string.format("Temperature %s °F", TEMPERATURE)
|
||||
@@ -922,7 +924,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
else
|
||||
self.radioqueue:NewTransmission("DegreesCelsius.ogg", 1.28, self.soundpath, nil, 0.2)
|
||||
end
|
||||
|
||||
|
||||
-- Wind
|
||||
if self.metric then
|
||||
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
|
||||
@@ -944,7 +946,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if turbulence>0 then
|
||||
self.radioqueue:NewTransmission("Gusting.ogg", 0.55, self.soundpath, nil, 0.2)
|
||||
end
|
||||
|
||||
|
||||
-- Active runway.
|
||||
local subtitle=string.format("Active runway %s", runway)
|
||||
if rleft then
|
||||
@@ -959,7 +961,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
elseif rright then
|
||||
self.radioqueue:NewTransmission("Right.ogg", 0.43, self.soundpath, nil, 0.2)
|
||||
end
|
||||
|
||||
|
||||
-- Tower frequency.
|
||||
if self.towerfrequency then
|
||||
local freqs=""
|
||||
@@ -972,14 +974,14 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:NewTransmission("TowerFrequency.ogg", 1.19, self.soundpath, nil, 1.0, string.format("Tower frequency %s", freqs), subduration)
|
||||
for _,freq in pairs(self.towerfrequency) do
|
||||
local f=string.format("%.3f", freq)
|
||||
f=UTILS.Split(f, ".")
|
||||
f=UTILS.Split(f, ".")
|
||||
self.radioqueue:Number2Transmission(f[1], nil, 0.5)
|
||||
self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2)
|
||||
self.radioqueue:Number2Transmission(f[2])
|
||||
self.radioqueue:NewTransmission("MegaHertz.ogg", 0.86, self.soundpath, nil, 0.2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -990,7 +992,7 @@ end
|
||||
-- @param #ATIS self
|
||||
-- @return #table Clouds table which has entries "thickness", "density", "base", "iprecptns".
|
||||
-- @return #number Visibility distance in meters.
|
||||
-- @return #number Ground turbulence in m/s.
|
||||
-- @return #number Ground turbulence in m/s.
|
||||
-- @return #table Fog table, which has entries "thickness", "visibility" or nil if fog is disabled in the mission.
|
||||
-- @return #number Dust density or nil if dust is disabled in the mission.
|
||||
-- @return #boolean static If true, static weather is used. If false, dynamic weather is used.
|
||||
@@ -1010,13 +1012,13 @@ function ATIS:GetMissionWeather()
|
||||
}, -- end of ["clouds"]
|
||||
]]
|
||||
local clouds=weather.clouds
|
||||
|
||||
|
||||
-- 0=static, 1=dynamic
|
||||
local static=weather.atmosphere_type==0
|
||||
|
||||
-- Visibilty distance in meters.
|
||||
local visibility=weather.visibility.distance
|
||||
|
||||
|
||||
-- Ground turbulence.
|
||||
local turbulence=weather.groundTurbulence
|
||||
|
||||
@@ -1063,12 +1065,12 @@ end
|
||||
function ATIS:_GetThousandsAndHundreds(n)
|
||||
|
||||
local N=UTILS.Round(n/1000, 1)
|
||||
|
||||
|
||||
local S=UTILS.Split(string.format("%.1f", N), ".")
|
||||
|
||||
|
||||
local t=S[1]
|
||||
local h=S[2]
|
||||
|
||||
|
||||
return t, h
|
||||
end
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
--- **Wrapper** -- AIRBASE is a wrapper class to handle the DCS Airbase objects.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
--
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Wrapper.Airbase
|
||||
-- @image Wrapper_Airbase.JPG
|
||||
|
||||
@@ -19,40 +19,40 @@
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle the DCS Airbase objects:
|
||||
--
|
||||
--
|
||||
-- * Support all DCS Airbase APIs.
|
||||
-- * Enhance with Airbase specific APIs not in the DCS Airbase API set.
|
||||
--
|
||||
--
|
||||
-- ## AIRBASE reference methods
|
||||
--
|
||||
--
|
||||
-- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object.
|
||||
-- This is done at the beginning of the mission (when the mission starts).
|
||||
--
|
||||
--
|
||||
-- The AIRBASE class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference
|
||||
-- using the DCS Airbase or the DCS AirbaseName.
|
||||
--
|
||||
-- Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object.
|
||||
--
|
||||
-- Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object.
|
||||
-- The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution.
|
||||
-- If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file.
|
||||
--
|
||||
--
|
||||
-- The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance:
|
||||
--
|
||||
--
|
||||
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
|
||||
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
|
||||
--
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
|
||||
--
|
||||
--
|
||||
-- ## DCS Airbase APIs
|
||||
--
|
||||
--
|
||||
-- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method.
|
||||
-- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call,
|
||||
-- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}()
|
||||
-- is implemented in the AIRBASE class as @{#AIRBASE.GetName}().
|
||||
--
|
||||
--
|
||||
-- @field #AIRBASE AIRBASE
|
||||
AIRBASE = {
|
||||
ClassName="AIRBASE",
|
||||
CategoryName = {
|
||||
CategoryName = {
|
||||
[Airbase.Category.AIRDROME] = "Airdrome",
|
||||
[Airbase.Category.HELIPAD] = "Helipad",
|
||||
[Airbase.Category.SHIP] = "Ship",
|
||||
@@ -61,9 +61,9 @@ AIRBASE = {
|
||||
}
|
||||
|
||||
--- Enumeration to identify the airbases in the Caucasus region.
|
||||
--
|
||||
--
|
||||
-- These are all airbases of Caucasus:
|
||||
--
|
||||
--
|
||||
-- * AIRBASE.Caucasus.Gelendzhik
|
||||
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
||||
-- * AIRBASE.Caucasus.Sukhumi_Babushara
|
||||
@@ -85,7 +85,7 @@ AIRBASE = {
|
||||
-- * AIRBASE.Caucasus.Nalchik
|
||||
-- * AIRBASE.Caucasus.Mozdok
|
||||
-- * AIRBASE.Caucasus.Beslan
|
||||
--
|
||||
--
|
||||
-- @field Caucasus
|
||||
AIRBASE.Caucasus = {
|
||||
["Gelendzhik"] = "Gelendzhik",
|
||||
@@ -112,7 +112,7 @@ AIRBASE.Caucasus = {
|
||||
}
|
||||
|
||||
--- These are all airbases of Nevada:
|
||||
--
|
||||
--
|
||||
-- * AIRBASE.Nevada.Creech_AFB
|
||||
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
||||
-- * AIRBASE.Nevada.McCarran_International_Airport
|
||||
@@ -131,7 +131,7 @@ AIRBASE.Caucasus = {
|
||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||
-- @field Nevada
|
||||
-- @field Nevada
|
||||
AIRBASE.Nevada = {
|
||||
["Creech_AFB"] = "Creech AFB",
|
||||
["Groom_Lake_AFB"] = "Groom Lake AFB",
|
||||
@@ -154,7 +154,7 @@ AIRBASE.Nevada = {
|
||||
}
|
||||
|
||||
--- These are all airbases of Normandy:
|
||||
--
|
||||
--
|
||||
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
||||
-- * AIRBASE.Normandy.Lignerolles
|
||||
-- * AIRBASE.Normandy.Cretteville
|
||||
@@ -264,7 +264,7 @@ AIRBASE.PersianGulf = {
|
||||
["Bandar_Abbas_Intl"] = "Bandar Abbas Intl",
|
||||
["Bandar_Lengeh"] = "Bandar Lengeh",
|
||||
["Bandar_e_Jask_airfield"] = "Bandar-e-Jask airfield",
|
||||
["Dubai_Intl"] = "Dubai Intl",
|
||||
["Dubai_Intl"] = "Dubai Intl",
|
||||
["Fujairah_Intl"] = "Fujairah Intl",
|
||||
["Havadarya"] = "Havadarya",
|
||||
["Jiroft_Airport"] = "Jiroft Airport",
|
||||
@@ -284,7 +284,7 @@ AIRBASE.PersianGulf = {
|
||||
["Tunb_Island_AFB"] = "Tunb Island AFB",
|
||||
["Tunb_Kochak"] = "Tunb Kochak",
|
||||
}
|
||||
|
||||
|
||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||
-- @type AIRBASE.ParkingSpot
|
||||
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
||||
@@ -294,11 +294,11 @@ AIRBASE.PersianGulf = {
|
||||
-- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment.
|
||||
-- @field #number TerminalID0 Unknown what this means. If you know, please tell us!
|
||||
-- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID.
|
||||
|
||||
|
||||
--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking
|
||||
--
|
||||
--
|
||||
-- Supported types are:
|
||||
--
|
||||
--
|
||||
-- * AIRBASE.TerminalType.Runway = 16: Valid spawn points on runway.
|
||||
-- * AIRBASE.TerminalType.HelicopterOnly = 40: Special spots for Helicopers.
|
||||
-- * AIRBASE.TerminalType.Shelter = 68: Hardened Air Shelter. Currently only on Caucaus map.
|
||||
@@ -307,7 +307,7 @@ AIRBASE.PersianGulf = {
|
||||
-- * AIRBASE.TerminalType.OpenMedOrBig = 176: Combines OpenMed and OpenBig spots.
|
||||
-- * AIRBASE.TerminalType.HelicopterUnsable = 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.FighterAircraft = 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
--
|
||||
--
|
||||
-- @type AIRBASE.TerminalType
|
||||
-- @field #number Runway 16: Valid spawn points on runway.
|
||||
-- @field #number HelicopterOnly 40: Special spots for Helicopers.
|
||||
@@ -331,13 +331,13 @@ AIRBASE.TerminalType = {
|
||||
--- Runway data.
|
||||
-- @type AIRBASE.Runway
|
||||
-- @field #number heading Heading of the runway in degrees.
|
||||
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
||||
-- @field #string idx Runway ID: heading 070� ==> idx="07".
|
||||
-- @field #number length Length of runway in meters.
|
||||
-- @field Core.Point#COORDINATE position Position of runway start.
|
||||
-- @field Core.Point#COORDINATE endpoint End point of runway.
|
||||
|
||||
-- Registration.
|
||||
|
||||
|
||||
--- Create a new AIRBASE from DCSAirbase.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #string AirbaseName The name of the airbase.
|
||||
@@ -368,7 +368,7 @@ end
|
||||
-- @param #string AirbaseName The Airbase Name.
|
||||
-- @return #AIRBASE self
|
||||
function AIRBASE:FindByName( AirbaseName )
|
||||
|
||||
|
||||
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
|
||||
return AirbaseFound
|
||||
end
|
||||
@@ -378,18 +378,18 @@ end
|
||||
-- @param #number id Airbase ID.
|
||||
-- @return #AIRBASE self
|
||||
function AIRBASE:FindByID(id)
|
||||
|
||||
|
||||
for name,_airbase in pairs(_DATABASE.AIRBASES) do
|
||||
local airbase=_airbase --#AIRBASE
|
||||
|
||||
|
||||
local aid=tonumber(airbase:GetID())
|
||||
|
||||
|
||||
if aid==id then
|
||||
return airbase
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -398,11 +398,11 @@ end
|
||||
-- @return DCS#Airbase DCS airbase object.
|
||||
function AIRBASE:GetDCSObject()
|
||||
local DCSAirbase = Airbase.getByName( self.AirbaseName )
|
||||
|
||||
|
||||
if DCSAirbase then
|
||||
return DCSAirbase
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -418,7 +418,7 @@ end
|
||||
-- @param #number category (Optional) Return only airbases of a certain category, e.g. Airbase.Category.FARP
|
||||
-- @return #table Table containing all airbase objects of the current map.
|
||||
function AIRBASE.GetAllAirbases(coalition, category)
|
||||
|
||||
|
||||
local airbases={}
|
||||
for _,_airbase in pairs(_DATABASE.AIRBASES) do
|
||||
local airbase=_airbase --#AIRBASE
|
||||
@@ -428,28 +428,28 @@ function AIRBASE.GetAllAirbases(coalition, category)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return airbases
|
||||
end
|
||||
|
||||
|
||||
--- Returns a table of parking data for a given airbase. If the optional parameter *available* is true only available parking will be returned, otherwise all parking at the base is returned. Term types have the following enumerated values:
|
||||
--
|
||||
--
|
||||
-- * 16 : Valid spawn points on runway
|
||||
-- * 40 : Helicopter only spawn
|
||||
-- * 40 : Helicopter only spawn
|
||||
-- * 68 : Hardened Air Shelter
|
||||
-- * 72 : Open/Shelter air airplane only
|
||||
-- * 104: Open air spawn
|
||||
--
|
||||
--
|
||||
-- Note that only Caucuses will return 68 as it is the only map currently with hardened air shelters.
|
||||
-- 104 are also generally larger, but does not guarantee a large aircraft like the B-52 or a C-130 are capable of spawning there.
|
||||
--
|
||||
--
|
||||
-- Table entries:
|
||||
--
|
||||
--
|
||||
-- * Term_index is the id for the parking
|
||||
-- * vTerminal pos is its vec3 position in the world
|
||||
-- * fDistToRW is the distance to the take-off position for the active runway from the parking.
|
||||
--
|
||||
--
|
||||
-- @param #AIRBASE self
|
||||
-- @param #boolean available If true, only available parking spots will be returned.
|
||||
-- @return #table Table with parking data. See https://wiki.hoggitworld.com/view/DCS_func_getParking
|
||||
@@ -458,13 +458,13 @@ function AIRBASE:GetParkingData(available)
|
||||
|
||||
-- Get DCS airbase object.
|
||||
local DCSAirbase=self:GetDCSObject()
|
||||
|
||||
|
||||
-- Get parking data.
|
||||
local parkingdata=nil
|
||||
if DCSAirbase then
|
||||
parkingdata=DCSAirbase:getParking(available)
|
||||
end
|
||||
|
||||
|
||||
self:T2({parkingdata=parkingdata})
|
||||
return parkingdata
|
||||
end
|
||||
@@ -477,27 +477,27 @@ function AIRBASE:GetParkingSpotsNumber(termtype)
|
||||
|
||||
-- Get free parking spots data.
|
||||
local parkingdata=self:GetParkingData(false)
|
||||
|
||||
|
||||
local nspots=0
|
||||
for _,parkingspot in pairs(parkingdata) do
|
||||
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
|
||||
nspots=nspots+1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return nspots
|
||||
end
|
||||
|
||||
--- Get number of free parking spots at an airbase.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #AIRBASE.TerminalType termtype Terminal type.
|
||||
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
|
||||
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
|
||||
-- @return #number Number of free parking spots at this airbase.
|
||||
function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC)
|
||||
|
||||
-- Get free parking spots data.
|
||||
local parkingdata=self:GetParkingData(true)
|
||||
|
||||
|
||||
local nfree=0
|
||||
for _,parkingspot in pairs(parkingdata) do
|
||||
-- Spots on runway are not counted unless explicitly requested.
|
||||
@@ -507,7 +507,7 @@ function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return nfree
|
||||
end
|
||||
|
||||
@@ -520,7 +520,7 @@ function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC)
|
||||
|
||||
-- Get free parking spots data.
|
||||
local parkingdata=self:GetParkingData(true)
|
||||
|
||||
|
||||
-- Put coordinates of free spots into table.
|
||||
local spots={}
|
||||
for _,parkingspot in pairs(parkingdata) do
|
||||
@@ -531,7 +531,7 @@ function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return spots
|
||||
end
|
||||
|
||||
@@ -543,23 +543,23 @@ function AIRBASE:GetParkingSpotsCoordinates(termtype)
|
||||
|
||||
-- Get all parking spots data.
|
||||
local parkingdata=self:GetParkingData(false)
|
||||
|
||||
|
||||
-- Put coordinates of free spots into table.
|
||||
local spots={}
|
||||
for _,parkingspot in ipairs(parkingdata) do
|
||||
|
||||
|
||||
-- Coordinates on runway are not returned unless explicitly requested.
|
||||
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
|
||||
|
||||
|
||||
-- Get coordinate from Vec3 terminal position.
|
||||
local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos)
|
||||
|
||||
|
||||
-- Add to table.
|
||||
table.insert(spots, _coord)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return spots
|
||||
end
|
||||
|
||||
@@ -570,11 +570,11 @@ end
|
||||
-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||
function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
|
||||
-- Get parking data of all spots (free or occupied)
|
||||
-- Get parking data of all spots (free or occupied)
|
||||
local parkingdata=self:GetParkingData(false)
|
||||
-- Get parking data of all free spots.
|
||||
local parkingfree=self:GetParkingData(true)
|
||||
|
||||
|
||||
-- Function to ckeck if any parking spot is free.
|
||||
local function _isfree(_tocheck)
|
||||
for _,_spot in pairs(parkingfree) do
|
||||
@@ -584,7 +584,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- Put coordinates of parking spots into table.
|
||||
local spots={}
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
@@ -595,22 +595,22 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
table.insert(spots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:T2({ spots = spots } )
|
||||
|
||||
|
||||
return spots
|
||||
end
|
||||
|
||||
--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #AIRBASE.TerminalType termtype Terminal type.
|
||||
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
|
||||
-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships.
|
||||
-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||
function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
|
||||
-- Get parking data of all free spots.
|
||||
local parkingfree=self:GetParkingData(true)
|
||||
|
||||
|
||||
-- Put coordinates of free spots into table.
|
||||
local freespots={}
|
||||
for _,_spot in pairs(parkingfree) do
|
||||
@@ -621,7 +621,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return freespots
|
||||
end
|
||||
|
||||
@@ -634,10 +634,10 @@ function AIRBASE:GetParkingSpotData(TerminalID)
|
||||
|
||||
-- Get parking data.
|
||||
local parkingdata=self:GetParkingSpotsTable()
|
||||
|
||||
|
||||
-- Debug output.
|
||||
self:T2({parkingdata=parkingdata})
|
||||
|
||||
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
local spot=_spot --#AIRBASE.ParkingSpot
|
||||
self:T({TerminalID=spot.TerminalID,TerminalType=spot.TerminalType})
|
||||
@@ -645,7 +645,7 @@ function AIRBASE:GetParkingSpotData(TerminalID)
|
||||
return spot
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:E("ERROR: Could not find spot with Terminal ID="..tostring(TerminalID))
|
||||
return nil
|
||||
end
|
||||
@@ -667,18 +667,18 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
|
||||
-- Get airbase name.
|
||||
local airbasename=self:GetName()
|
||||
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype)))
|
||||
|
||||
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
|
||||
|
||||
-- Mark text.
|
||||
local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m",
|
||||
_spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
|
||||
|
||||
|
||||
-- Create mark on the F10 map.
|
||||
if mark then
|
||||
_spot.Coordinate:MarkToAll(_text)
|
||||
end
|
||||
|
||||
|
||||
-- Info to DCS.log file.
|
||||
local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m",
|
||||
airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
|
||||
@@ -696,7 +696,7 @@ end
|
||||
-- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true.
|
||||
-- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters.
|
||||
-- @param #boolean verysafe (Optional) If true, wait until an aircraft has taken off until the parking spot is considered to be free. Defaul false.
|
||||
-- @param #number nspots (Optional) Number of freeparking spots requested. Default is the number of aircraft in the group.
|
||||
-- @param #number nspots (Optional) Number of freeparking spots requested. Default is the number of aircraft in the group.
|
||||
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function.
|
||||
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
|
||||
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
|
||||
@@ -714,8 +714,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
end
|
||||
if verysafe==nil then
|
||||
verysafe=false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Function calculating the overlap of two (square) objects.
|
||||
local function _overlap(object1, object2, dist)
|
||||
local pos1=object1 --Wrapper.Positionable#POSITIONABLE
|
||||
@@ -723,107 +723,107 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
local r1=pos1:GetBoundingRadius()
|
||||
local r2=pos2:GetBoundingRadius()
|
||||
if r1 and r2 then
|
||||
local safedist=(r1+r2)*1.1
|
||||
local safedist=(r1+r2)*1.1
|
||||
local safe = (dist > safedist)
|
||||
self:T2(string.format("r1=%.1f r2=%.1f s=%.1f d=%.1f ==> safe=%s", r1, r2, safedist, dist, tostring(safe)))
|
||||
return safe
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Get airport name.
|
||||
local airport=self:GetName()
|
||||
|
||||
|
||||
-- Get parking spot data table. This contains free and "non-free" spots.
|
||||
-- Note that there are three major issues with the DCS getParking() function:
|
||||
-- 1. A spot is considered as NOT free until an aircraft that is present has finally taken off. This might be a bit long especiall at smaller airports.
|
||||
-- 2. A "free" spot does not take the aircraft size into accound. So if two big aircraft are spawned on spots next to each other, they might overlap and get destroyed.
|
||||
-- 3. The routine return a free spot, if there a static objects placed on the spot.
|
||||
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
|
||||
|
||||
|
||||
-- Get the aircraft size, i.e. it's longest side of x,z.
|
||||
local aircraft=group:GetUnit(1)
|
||||
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||
|
||||
|
||||
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
||||
local _nspots=nspots or group:GetSize()
|
||||
|
||||
|
||||
-- Debug info.
|
||||
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
|
||||
|
||||
|
||||
-- Table of valid spots.
|
||||
local validspots={}
|
||||
local nvalid=0
|
||||
|
||||
|
||||
-- Test other stuff if no parking spot is available.
|
||||
local _test=false
|
||||
if _test then
|
||||
return validspots
|
||||
end
|
||||
|
||||
|
||||
-- Mark all found obstacles on F10 map for debugging.
|
||||
local markobstacles=false
|
||||
|
||||
|
||||
-- Loop over all known parking spots
|
||||
for _,parkingspot in pairs(parkingdata) do
|
||||
|
||||
|
||||
-- Coordinate of the parking spot.
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
local _termid=parkingspot.TerminalID
|
||||
|
||||
|
||||
self:T2({_termid=_termid})
|
||||
|
||||
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then
|
||||
|
||||
|
||||
-- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off.
|
||||
if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then
|
||||
|
||||
|
||||
-- DCS getParking() routine returned that spot is not free.
|
||||
self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.", airport, parkingspot.TerminalID, tostring(parkingspot.Free), tostring(parkingspot.TOAC)))
|
||||
|
||||
|
||||
else
|
||||
|
||||
|
||||
-- Scan a radius of 50 meters around the spot.
|
||||
local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery)
|
||||
|
||||
|
||||
-- Loop over objects within scan radius.
|
||||
local occupied=false
|
||||
|
||||
-- Check all units.
|
||||
|
||||
-- Check all units.
|
||||
for _,unit in pairs(_units) do
|
||||
local _coord=unit:GetCoordinate()
|
||||
local _dist=_coord:Get2DDistance(_spot)
|
||||
local _dist=_coord:Get2DDistance(_spot)
|
||||
local _safe=_overlap(aircraft, unit, _dist)
|
||||
|
||||
|
||||
if markobstacles then
|
||||
local l,x,y,z=unit:GetObjectSize()
|
||||
_coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", unit:GetName(),x,y,z,l,_dist, _termid, tostring(_safe)))
|
||||
end
|
||||
|
||||
|
||||
if scanunits and not _safe then
|
||||
occupied=true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Check all statics.
|
||||
for _,static in pairs(_statics) do
|
||||
local _static=STATIC:Find(static)
|
||||
local _vec3=static:getPoint()
|
||||
local _coord=COORDINATE:NewFromVec3(_vec3)
|
||||
local _dist=_coord:Get2DDistance(_spot)
|
||||
local _dist=_coord:Get2DDistance(_spot)
|
||||
local _safe=_overlap(aircraft,_static,_dist)
|
||||
|
||||
|
||||
if markobstacles then
|
||||
local l,x,y,z=_static:GetObjectSize()
|
||||
_coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", static:getName(),x,y,z,l,_dist, _termid, tostring(_safe)))
|
||||
end
|
||||
|
||||
|
||||
if scanstatics and not _safe then
|
||||
occupied=true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Check all scenery.
|
||||
for _,scenery in pairs(_sceneries) do
|
||||
local _scenery=SCENERY:Register(scenery:getTypeName(), scenery)
|
||||
@@ -831,17 +831,17 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
local _coord=COORDINATE:NewFromVec3(_vec3)
|
||||
local _dist=_coord:Get2DDistance(_spot)
|
||||
local _safe=_overlap(aircraft,_scenery,_dist)
|
||||
|
||||
|
||||
if markobstacles then
|
||||
local l,x,y,z=scenery:GetObjectSize(scenery)
|
||||
_coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", scenery:getTypeName(),x,y,z,l,_dist, _termid, tostring(_safe)))
|
||||
end
|
||||
|
||||
|
||||
if scanscenery and not _safe then
|
||||
occupied=true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Now check the already given spots so that we do not put a large aircraft next to one we already assigned a nearby spot.
|
||||
for _,_takenspot in pairs(validspots) do
|
||||
local _dist=_takenspot.Coordinate:Get2DDistance(_spot)
|
||||
@@ -850,7 +850,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
occupied=true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied)))
|
||||
if occupied then
|
||||
self:I(string.format("%s: Parking spot id %d occupied.", airport, _termid))
|
||||
@@ -862,16 +862,16 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
nvalid=nvalid+1
|
||||
self:I(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots))
|
||||
end
|
||||
|
||||
|
||||
end -- loop over units
|
||||
|
||||
|
||||
-- We found enough spots.
|
||||
if nvalid>=_nspots then
|
||||
return validspots
|
||||
end
|
||||
end -- check terminal type
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Retrun spots we found, even if there were not enough.
|
||||
return validspots
|
||||
end
|
||||
@@ -880,54 +880,54 @@ end
|
||||
-- @param #AIRBASE self
|
||||
-- @param Wrapper.Group#GROUP group Group to be checked.
|
||||
-- @param #number radius Radius around the spawn point to be checked. Default is 50 m.
|
||||
-- @param #boolean despawn If true, the group is destroyed.
|
||||
-- @param #boolean despawn If true, the group is destroyed.
|
||||
-- @return #boolean True if group is within radius around spawn points on runway.
|
||||
function AIRBASE:CheckOnRunWay(group, radius, despawn)
|
||||
|
||||
-- Default radius.
|
||||
radius=radius or 50
|
||||
|
||||
|
||||
-- We only check at real airbases (not FARPS or ships).
|
||||
if self:GetDesc().category~=Airbase.Category.AIRDROME then
|
||||
return false
|
||||
end
|
||||
|
||||
if group and group:IsAlive() then
|
||||
|
||||
|
||||
-- Debug.
|
||||
self:T(string.format("%s, checking if group %s is on runway?",self:GetName(), group:GetName()))
|
||||
|
||||
|
||||
-- Get coordinates on runway.
|
||||
local runwaypoints=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
|
||||
|
||||
|
||||
-- Mark runway spawn points.
|
||||
--[[
|
||||
for _i,_coord in pairs(runwaypoints) do
|
||||
_coord:MarkToAll(string.format("runway %d",_i))
|
||||
end
|
||||
]]
|
||||
|
||||
|
||||
-- Get units of group.
|
||||
local units=group:GetUnits()
|
||||
|
||||
|
||||
-- Loop over units.
|
||||
for _,_unit in pairs(units) do
|
||||
|
||||
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
|
||||
-- Check if unit is alive and not in air.
|
||||
if unit and unit:IsAlive() and not unit:InAir() then
|
||||
self:T(string.format("%s, checking if unit %s is on runway?",self:GetName(), unit:GetName()))
|
||||
|
||||
|
||||
-- Loop over runway spawn points.
|
||||
for _i,_coord in pairs(runwaypoints) do
|
||||
|
||||
-- Distance between unit and spawn pos.
|
||||
local dist=unit:GetCoordinate():Get2DDistance(_coord)
|
||||
|
||||
|
||||
-- Mark unit spawn points for debugging.
|
||||
--unit:GetCoordinate():MarkToAll(string.format("unit %s distance to rwy %d = %d",unit:GetName(),_i, dist))
|
||||
|
||||
|
||||
-- Check if unit is withing radius.
|
||||
if dist<radius then
|
||||
self:E(string.format("%s, unit %s of group %s was spawned on runway #%d. Distance %.1f < radius %.1f m. Despawn = %s.", self:GetName(), unit:GetName(), group:GetName(),_i, dist, radius, tostring(despawn)))
|
||||
@@ -940,16 +940,16 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn)
|
||||
self:T(string.format("%s, unit %s of group %s was NOT spawned on runway #%d. Distance %.1f > radius %.1f m. Despawn = %s.", self:GetName(), unit:GetName(), group:GetName(),_i, dist, radius, tostring(despawn)))
|
||||
--unit:FlareGreen()
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
else
|
||||
self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(), unit:GetName(), group:GetName()))
|
||||
end
|
||||
self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(), unit:GetName(), group:GetName()))
|
||||
end
|
||||
end
|
||||
else
|
||||
self:T(string.format("%s, checking if group %s is on runway. Group is NOT alive.",self:GetName(), group:GetName()))
|
||||
end
|
||||
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -980,15 +980,15 @@ function AIRBASE._CheckTerminalType(Term_Type, termtype)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Init no match.
|
||||
local match=false
|
||||
|
||||
-- Standar case.
|
||||
|
||||
-- Standar case.
|
||||
if Term_Type==termtype then
|
||||
match=true
|
||||
end
|
||||
|
||||
|
||||
-- Artificial cases. Combination of terminal types.
|
||||
if termtype==AIRBASE.TerminalType.OpenMedOrBig then
|
||||
if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then
|
||||
@@ -1003,114 +1003,119 @@ function AIRBASE._CheckTerminalType(Term_Type, termtype)
|
||||
match=true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return match
|
||||
end
|
||||
|
||||
--- Get runways data. Only for airdromes!
|
||||
-- @param #AIRBASE self
|
||||
-- @param #number magvar (Optional) Magnetic variation in degrees.
|
||||
-- @param #boolean mark (Optional) Place markers with runway data on F10 map.
|
||||
-- @return #table Runway data.
|
||||
function AIRBASE:GetRunwayData(magvar)
|
||||
function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Runway table.
|
||||
local runways={}
|
||||
|
||||
|
||||
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get spawn points on runway.
|
||||
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
|
||||
|
||||
|
||||
-- Magnetic declination.
|
||||
magvar=magvar or UTILS.GetMagneticDeclination()
|
||||
|
||||
|
||||
local N=#runwaycoords
|
||||
local dN=2
|
||||
local ex=false
|
||||
|
||||
|
||||
local name=self:GetName()
|
||||
if name==AIRBASE.Nevada.Jean_Airport or
|
||||
name==AIRBASE.Nevada.Creech_AFB or
|
||||
if name==AIRBASE.Nevada.Jean_Airport or
|
||||
name==AIRBASE.Nevada.Creech_AFB or
|
||||
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
||||
name==AIRBASE.PersianGulf.Dubai_Intl or
|
||||
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
||||
name==AIRBASE.PersianGulf.Kish_International_Airport then
|
||||
|
||||
|
||||
N=#runwaycoords/2
|
||||
dN=1
|
||||
ex=true
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
for i=1,N,dN do
|
||||
|
||||
|
||||
local j=i+1
|
||||
if ex then
|
||||
--j=N+i
|
||||
j=#runwaycoords-i+1
|
||||
end
|
||||
|
||||
|
||||
-- Coordinates of the two runway points.
|
||||
local c1=runwaycoords[i] --Core.Point#COORDINATES
|
||||
local c2=runwaycoords[j] --Core.Point#COORDINATES
|
||||
|
||||
|
||||
-- Heading of runway.
|
||||
local hdg=c1:HeadingTo(c2)
|
||||
|
||||
-- Runway ID: heading=070° ==> idx="07"
|
||||
|
||||
-- Runway ID: heading=070� ==> idx="07"
|
||||
local idx=string.format("%02d", UTILS.Round((hdg-magvar)/10, 0))
|
||||
|
||||
|
||||
-- Runway table.
|
||||
local runway={} --#AIRBASE.Runway
|
||||
runway.heading=hdg
|
||||
runway.idx=idx
|
||||
runway.length=c1:Get2DDistance(c2)
|
||||
runway.length=c1:Get2DDistance(c2)
|
||||
runway.position=c1
|
||||
runway.endpoint=c2
|
||||
|
||||
|
||||
-- Debug info.
|
||||
self:T(string.format("Airbase %s: Adding runway id=%s, heading=%03d, length=%d m", self:GetName(), runway.idx, runway.heading, runway.length))
|
||||
|
||||
|
||||
-- Debug mark
|
||||
runway.position:MarkToAll(string.format("Runway %s Heading=%03d", runway.idx, runway.heading))
|
||||
|
||||
if mark then
|
||||
runway.position:MarkToAll(string.format("Runway %s: true heading=%03d, length=%d m", runway.idx, runway.heading, runway.length))
|
||||
end
|
||||
|
||||
-- Add runway.
|
||||
table.insert(runways, runway)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Get inverse runways
|
||||
local inverse={}
|
||||
for _,_runway in pairs(runways) do
|
||||
local r=_runway --#AIRBASE.Runway
|
||||
|
||||
local runway={} --#AIRBASE.Runway
|
||||
|
||||
local runway={} --#AIRBASE.Runway
|
||||
runway.heading=r.heading-180
|
||||
if runway.heading<0 then
|
||||
runway.heading=runway.heading+360
|
||||
end
|
||||
end
|
||||
runway.idx=string.format("%02d", math.max(0, UTILS.Round((runway.heading-magvar)/10, 0)))
|
||||
runway.length=r.length
|
||||
runway.position=r.endpoint
|
||||
runway.endpoint=r.position
|
||||
|
||||
|
||||
-- Debug info.
|
||||
self:T(string.format("Airbase %s: Adding runway id=%s, heading=%03d, length=%d m", self:GetName(), runway.idx, runway.heading, runway.length))
|
||||
|
||||
|
||||
-- Debug mark
|
||||
runway.position:MarkToAll(string.format("Runway %s Heading=%03d", runway.idx, runway.heading))
|
||||
|
||||
if mark then
|
||||
runway.position:MarkToAll(string.format("Runway %s: true heading=%03d, length=%d m", runway.idx, runway.heading, runway.length))
|
||||
end
|
||||
|
||||
-- Add runway.
|
||||
table.insert(inverse, runway)
|
||||
table.insert(inverse, runway)
|
||||
end
|
||||
|
||||
|
||||
-- Add inverse runway.
|
||||
for _,runway in pairs(inverse) do
|
||||
-- Add runway.
|
||||
table.insert(runways, runway)
|
||||
table.insert(runways, runway)
|
||||
end
|
||||
|
||||
|
||||
return runways
|
||||
end
|
||||
|
||||
@@ -1138,48 +1143,45 @@ function AIRBASE:GetActiveRunway(magvar)
|
||||
-- Get wind vector.
|
||||
local Vwind=self:GetCoordinate():GetWindWithTurbulenceVec3()
|
||||
local norm=UTILS.VecNorm(Vwind)
|
||||
|
||||
|
||||
-- Active runway number.
|
||||
local iact=1
|
||||
|
||||
|
||||
-- Check if wind is blowing (norm>0).
|
||||
if norm>0 then
|
||||
|
||||
|
||||
-- Normalize wind (not necessary).
|
||||
Vwind.x=Vwind.x/norm
|
||||
Vwind.y=0
|
||||
Vwind.z=Vwind.z/norm
|
||||
|
||||
|
||||
-- Loop over runways.
|
||||
local dotmin=nil
|
||||
for i,_runway in pairs(runways) do
|
||||
local runway=_runway --#AIRBASE.Runway
|
||||
|
||||
|
||||
-- Angle in rad.
|
||||
local alpha=math.rad(runway.heading)
|
||||
|
||||
|
||||
-- Runway vector.
|
||||
local Vrunway={x=math.cos(alpha), y=0, z=math.sin(alpha)}
|
||||
|
||||
|
||||
-- Dot product: parallel component of the two vectors.
|
||||
local dot=UTILS.VecDot(Vwind, Vrunway)
|
||||
|
||||
|
||||
-- Debug.
|
||||
env.info(string.format("runway=%03d° dot=%.3f", runway.heading, dot))
|
||||
|
||||
--env.info(string.format("runway=%03d� dot=%.3f", runway.heading, dot))
|
||||
|
||||
-- New min?
|
||||
if dotmin==nil or dot<dotmin then
|
||||
dotmin=dot
|
||||
iact=i
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
else
|
||||
self:E("WARNING: Norm of wind is zero! Cannot determine active runway based on wind direction.")
|
||||
end
|
||||
|
||||
|
||||
return runways[iact]
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user