ATIS v0.3.0

This commit is contained in:
Frank
2019-10-02 18:10:42 +02:00
parent 7efd2a3053
commit d37a16f3ef
3 changed files with 533 additions and 143 deletions
+2 -2
View File
@@ -296,7 +296,7 @@ function RADIOQUEUE:Broadcast(transmission)
if sender then if sender then
-- Broadcasting from aircraft. Only players tuned in to the right frequency will see the message. -- Broadcasting from aircraft. Only players tuned in to the right frequency will see the message.
self:I(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName())) self:T(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName()))
-- Command to set the Frequency for the transmission. -- Command to set the Frequency for the transmission.
local commandFrequency={ local commandFrequency={
@@ -325,7 +325,7 @@ function RADIOQUEUE:Broadcast(transmission)
else else
-- Broadcasting from carrier. No subtitle possible. Need to send messages to players. -- Broadcasting from carrier. No subtitle possible. Need to send messages to players.
self:I(self.lid..string.format("Broadcasting via trigger.action.radioTransmission().")) self:T(self.lid..string.format("Broadcasting via trigger.action.radioTransmission()."))
-- Position from where to transmit. -- Position from where to transmit.
local vec3=nil local vec3=nil
+454 -83
View File
@@ -1,14 +1,42 @@
--- **Ops** - (R2.5) - Automatic Terminal Information Service. --- **Ops** - (R2.5) - Automatic Terminal Information Service (ATIS).
--
-- ===
-- --
-- **Main Features:** -- **Main Features:**
-- --
-- * Active runway -- * Wind direction and speed,
-- * Wind direction and speed -- * Visibility,
-- * Visibility -- * Cloud coverage, base and ceiling,
-- * Cloud coverage, base and ceiling -- * Temprature,
-- * Temprature -- * Pressure QNH/QFE,
-- * Pressure QNH/QFE -- * Weather phenomena: rain, thunderstorm, fog, dust,
-- * Weather phenomena: rain, thunderstorm, fog, dust -- * Active runway based on wind direction,
-- * Tower frequencies,
-- * More than 180 voice overs,
-- * Airbase names pronounced in locale accent (russian, US, french, arabic),
-- * Option to present information in imperial or metric units.
--
-- ===
--
-- ## Youtube Videos:
--
-- * [ATIS v0.1 Caucasus - Batumi (WIP)](https://youtu.be/MdH9FmbNabo)
-- * [ATIS Airport Names Sound Check](https://youtu.be/qIE_OUQNAc0)
-- * [ATIS v0.2 Nevada - Nellis AFB (WIP)](https://youtu.be/8CT_9AoPrTk)
-- * [ATIS v0.3 Persion Gulf - Abu Dhabi/Dubai International](https://youtu.be/NjkKvPz6ovM)
--
-- ===
--
-- ## Missions: Example missions will be added later.
--
-- ===
--
-- ## Sound files: Check out the pinned messages in the Moose discord #ops-atis channel.
--
-- ===
--
-- 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.
-- --
-- === -- ===
-- --
@@ -32,18 +60,131 @@
-- @field #string relayunitname Name of the radio relay unit. -- @field #string relayunitname Name of the radio relay unit.
-- @field #table towerfrequency Table with tower frequencies. -- @field #table towerfrequency Table with tower frequencies.
-- @field #string activerunway The active runway specified by the user. -- @field #string activerunway The active runway specified by the user.
-- @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 #number zuludiff Time difference local vs. zulu in hours.
-- @field #number magvar Magnetic declination/variation at the airport in degrees.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Be surprised! --- Be informed!
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\ATIS\ATIS_Main.jpg) -- ![Banner Image](..\Presentations\ATIS\ATIS_Main.png)
-- --
-- # The ATIS Concept -- # 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. -- 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. -- 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.
--
-- ## 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
-- 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.
--
-- 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.
--
-- 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.
--
-- ![Banner Image](..\Presentations\ATIS\ATIS_SoundFolder.png)
--
-- **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)
-- atisAbuDhabi:SetRadioRelayUnitName("Radio Relay Abu Dhabi International Airport")
-- atisAbuDhabi:SetMetricUnits()
-- atisAbuDhabi:SetActiveRunway("L")
-- atisAbuDhabi:Start()
--
-- --
-- @field #ATIS -- @field #ATIS
ATIS = { ATIS = {
@@ -60,6 +201,12 @@ ATIS = {
relayunitname = nil, relayunitname = nil,
towerfrequency = nil, towerfrequency = nil,
activerunway = nil, activerunway = nil,
subduration = nil,
metric = nil,
PmmHg = nil,
TDegF = nil,
zuludiff = nil,
magvar = nil,
} }
--- NATO alphabet. --- NATO alphabet.
@@ -97,13 +244,18 @@ ATIS.Alphabet = {
--- ATIS class version. --- ATIS class version.
-- @field #string version -- @field #string version
ATIS.version="0.2.0" ATIS.version="0.3.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot! -- TODO: Metric units.
-- TODO: Correct fog for elevation.
-- TODO: Set UTC correction.
-- TODO: Use local time.
-- TODO: Set magnetic variation.
-- TODO: Add stop/pause FMS functions.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
@@ -123,9 +275,15 @@ function ATIS:New(airbasename, frequency, modulation)
self.airbasename=airbasename self.airbasename=airbasename
self.airbase=AIRBASE:FindByName(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.frequency=frequency or 143.00
self.modulation=modulation or 0 self.modulation=modulation or 0
-- Get map.
self.theatre=env.mission.theatre self.theatre=env.mission.theatre
-- Set some string id for output to DCS.log file. -- Set some string id for output to DCS.log file.
@@ -133,6 +291,7 @@ function ATIS:New(airbasename, frequency, modulation)
-- Defaults: -- Defaults:
self:SetSoundfilesPath() self:SetSoundfilesPath()
self:SetSubtitleDuration()
-- Start State. -- Start State.
self:SetStartState("Stopped") self:SetStartState("Stopped")
@@ -166,7 +325,7 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
-- Debug trace. -- Debug trace.
if true then if false then
self.Debug=true self.Debug=true
BASE:TraceOnOff(true) BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName) BASE:TraceClass(self.ClassName)
@@ -182,25 +341,28 @@ end
--- Set sound files folder within miz file. --- Set sound files folder within miz file.
-- @param #ATIS self -- @param #ATIS self
-- @param #string path Path for sound files. Default "ATIS Soundfiles". -- @param #string path Path for sound files. Default "ATIS Soundfiles/". Mind the slash "/" at the end!
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetSoundfilesPath(path) function ATIS:SetSoundfilesPath(path)
self.soundpath=tostring(path or "ATIS Soundfiles/") self.soundpath=tostring(path or "ATIS Soundfiles/")
self:I(self.lid..string.format("Setting sound files path to %s", self.soundpath)) self:I(self.lid..string.format("Setting sound files path to %s", self.soundpath))
return self
end end
--- Set sound files folder within miz file. --- Set airborne unit (airplane or helicopter), used to transmit radio messages including subtitles.
-- Best is to place the unit on a parking spot of the airbase and set it to *uncontrolled* in the mission editor.
-- @param #ATIS self -- @param #ATIS self
-- @param #string unitname -- @param #string unitname Name of the unit.
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetRadioRelayUnitName(unitname) function ATIS:SetRadioRelayUnitName(unitname)
self.relayunitname=unitname self.relayunitname=unitname
self:I(self.lid..string.format("Setting radio relay unit to %s", self.relayunitname)) self:I(self.lid..string.format("Setting radio relay unit to %s", self.relayunitname))
return self
end end
--- Set tower frequencies. --- Set tower frequencies.
-- @param #ATIS self -- @param #ATIS self
-- @param #table freqs Frequencies in MHz. A single frequency can be given as a number. -- @param #table freqs Table of frequencies in MHz. A single frequency can be given as a plain number (i.e. must not be table).
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetTowerFrequencies(freqs) function ATIS:SetTowerFrequencies(freqs)
if type(freqs)=="table" then if type(freqs)=="table" then
@@ -209,6 +371,7 @@ function ATIS:SetTowerFrequencies(freqs)
freqs={freqs} freqs={freqs}
end end
self.towerfrequency=freqs self.towerfrequency=freqs
return self
end end
--- Set active runway. This can be used if the automatic runway determination via the wind direction gives incorrect results. --- Set active runway. This can be used if the automatic runway determination via the wind direction gives incorrect results.
@@ -218,8 +381,83 @@ end
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetActiveRunway(runway) function ATIS:SetActiveRunway(runway)
self.activerunway=tostring(runway) self.activerunway=tostring(runway)
return self
end end
--- Set duration how long subtitles are displayed.
-- @param #ATIS self
-- @param #number duration Duration in seconds. Default 10 seconds.
-- @return #ATIS self
function ATIS:SetSubtitleDuration(duration)
self.subduration=tonumber(duration) or 10
return self
end
--- Set unit system to metric units.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:SetMetricUnits()
self.metric=true
return self
end
--- Set unit system to imperial units.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:SetImperialUnits()
self.metric=false
return self
end
--- Set pressure unit to millimeters of mercury (mmHg).
-- Default is inHg for imperial and hPa (=mBar) for metric units.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:SetPressureMillimetersMercury()
self.PmmHg=true
return self
end
--- Set temperature to be given in degrees Fahrenheit.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:SetTemperatureFahrenheit()
self.TDegF=true
return self
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
function ATIS:SetMagneticDeclination(magvar)
self.magvar=magvar
return self
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
function ATIS:SetZuluTimeDifference(delta)
self.zuludiff=delta
return self
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status -- Start & Status
@@ -230,14 +468,18 @@ end
function ATIS:onafterStart(From, Event, To) function ATIS:onafterStart(From, Event, To)
-- Info. -- Info.
self:I(self.lid..string.format("Starting ATIS v%s for airbase %s", ATIS.version, self.airbasename)) 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. -- Start radio queue.
self.radioqueue=RADIOQUEUE:New(self.frequency, self.modulation) self.radioqueue=RADIOQUEUE:New(self.frequency, self.modulation)
-- Send coordinate is airbase coord.
self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate()) self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
-- Set relay unit if we have one.
self.radioqueue:SetSenderUnitName(self.relayunitname) self.radioqueue:SetSenderUnitName(self.relayunitname)
-- Init numbers.
self.radioqueue:SetDigit(0, "N-0.ogg", 0.55, self.soundpath) self.radioqueue:SetDigit(0, "N-0.ogg", 0.55, self.soundpath)
self.radioqueue:SetDigit(1, "N-1.ogg", 0.40, self.soundpath) self.radioqueue:SetDigit(1, "N-1.ogg", 0.40, self.soundpath)
self.radioqueue:SetDigit(2, "N-2.ogg", 0.35, self.soundpath) self.radioqueue:SetDigit(2, "N-2.ogg", 0.35, self.soundpath)
@@ -249,6 +491,7 @@ function ATIS:onafterStart(From, Event, To)
self.radioqueue:SetDigit(8, "N-8.ogg", 0.37, self.soundpath) self.radioqueue:SetDigit(8, "N-8.ogg", 0.37, self.soundpath)
self.radioqueue:SetDigit(9, "N-9.ogg", 0.38, self.soundpath) self.radioqueue:SetDigit(9, "N-9.ogg", 0.38, self.soundpath)
-- Start radio queue.
self.radioqueue:Start(1, 0.1) self.radioqueue:Start(1, 0.1)
-- Init status updates. -- Init status updates.
@@ -260,6 +503,7 @@ end
-- @param #ATIS self -- @param #ATIS self
function ATIS:onafterStatus(From, Event, To) function ATIS:onafterStatus(From, Event, To)
-- Get FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- Info text. -- Info text.
@@ -278,43 +522,64 @@ end
function ATIS:onafterCheckQueue(From, Event, To) function ATIS:onafterCheckQueue(From, Event, To)
if #self.radioqueue.queue==0 then if #self.radioqueue.queue==0 then
self:I(self.lid..string.format("Radio queue empty. Repeating message.")) self:T(self.lid..string.format("Radio queue empty. Repeating message."))
self:Broadcast() self:Broadcast()
else else
self:I(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue)) self:T2(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue))
end end
-- Check back in 5 seconds. -- Check back in 5 seconds.
self:__CheckQueue(5) self:__CheckQueue(5)
end end
--- Update status. --- Broadcast ATIS radio message.
-- @param #ATIS self -- @param #ATIS self
function ATIS:onafterBroadcast(From, Event, To) function ATIS:onafterBroadcast(From, Event, To)
-- Get current coordinate.
local coord=self.airbase:GetCoordinate() local coord=self.airbase:GetCoordinate()
-- Get elevation.
local height=coord:GetLandHeight()+10 local height=coord:GetLandHeight()+10
---------------- ----------------
--- Pressure --- --- Pressure ---
---------------- ----------------
-- Pressure in hPa.
local qfe=coord:GetPressure(height) local qfe=coord:GetPressure(height)
local qnh=coord:GetPressure(0) local qnh=coord:GetPressure(0)
-- Convert to inHg -- Convert to inHg.
qfe=UTILS.hPa2inHg(qfe) if self.PmmHg then
qnh=UTILS.hPa2inHg(qnh) qfe=UTILS.hPa2mmHg(qfe)
qnh=UTILS.hPa2mmHg(qnh)
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 QFE=UTILS.Split(string.format("%.2f", qfe), ".")
local QNH=UTILS.Split(string.format("%.2f", qnh), ".") local QNH=UTILS.Split(string.format("%.2f", qnh), ".")
if self.PmmHg then
QFE=UTILS.Split(string.format("%.1f", qfe), ".")
QNH=UTILS.Split(string.format("%.1f", qnh), ".")
else
if self.metric then
QFE=UTILS.Split(string.format("%.1f", qfe), ".")
QNH=UTILS.Split(string.format("%.1f", qnh), ".")
end
end
-------------- --------------
--- Runway --- --- Runway ---
-------------- --------------
-- Get runway based on wind direction. -- Get runway based on wind direction.
local runway=self.airbase:GetActiveRunway().idx local runway=self.airbase:GetActiveRunway(self.magvar).idx
-- Left or right in case there are two runways with the same heading. -- Left or right in case there are two runways with the same heading.
local rleft=false local rleft=false
@@ -322,7 +587,10 @@ function ATIS:onafterBroadcast(From, Event, To)
-- Check if user explicitly specified a runway. -- Check if user explicitly specified a runway.
if self.activerunway then if self.activerunway then
runway=self.activerunway:gsub("%D+", "") local runwayno=self.activerunway:gsub("%D+", "")
if runwayno~="" then
runway=runwayno
end
rleft=self.activerunway:lower():find("l") rleft=self.activerunway:lower():find("l")
rright=self.activerunway:lower():find("r") rright=self.activerunway:lower():find("r")
end end
@@ -330,24 +598,37 @@ function ATIS:onafterBroadcast(From, Event, To)
------------ ------------
--- Wind --- --- Wind ---
------------ ------------
-- Get wind direction and speed in m/s.
local windFrom, windSpeed=coord:GetWind(height) local windFrom, windSpeed=coord:GetWind(height)
local WINDFROM=string.format("%03d", windFrom) local WINDFROM=string.format("%03d", windFrom)
local WINDSPEED=string.format("%d", UTILS.MpsToKnots(windSpeed)) local WINDSPEED=string.format("%d", UTILS.MpsToKnots(windSpeed))
if self.metric then
WINDSPEED=string.format("%d", windSpeed)
end
------------ ------------
--- Time --- --- Time ---
------------ ------------
local time=timer.getAbsTime() local time=timer.getAbsTime()
-- Conversion to Zulu time. -- Conversion to Zulu time.
if self.theatre==DCSMAP.Caucasus then if self.zuludiff then
time=time-4*60*60 -- Caucasus UTC+4 hours -- User specified.
elseif self.theatre==DCSMAP.PersianGulf then time=time-self.zuludiff*60*60
time=time-4*60*60 -- Abu Dhabi UTC+4 hours else
elseif self.theatre==DCSMAP.NTTR then if self.theatre==DCSMAP.Caucasus then
time=time+7*60*60 -- Las Vegas UTC-7 hours time=time-4*60*60 -- Caucasus UTC+4 hours
elseif self.theatre==DCSMAP.Normandy then elseif self.theatre==DCSMAP.PersianGulf then
time=time-1*60*60 -- Calais UTC+1 hour time=time-4*60*60 -- Abu Dhabi UTC+4 hours
elseif self.theatre==DCSMAP.NTTR then
time=time+7*60*60 -- Las Vegas UTC-7 hours
elseif self.theatre==DCSMAP.Normandy then
time=time-1*60*60 -- Calais UTC+1 hour
end
end end
local clock=UTILS.SecondsToClock(time) local clock=UTILS.SecondsToClock(time)
@@ -355,64 +636,104 @@ function ATIS:onafterBroadcast(From, Event, To)
local ZULU=string.format("%s%s", zulu[1], zulu[2]) local ZULU=string.format("%s%s", zulu[1], zulu[2])
-- NATO time stamp. -- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc.
local NATO=ATIS.Alphabet[tonumber(zulu[1])-1] local NATO=ATIS.Alphabet[tonumber(zulu[1])+1]
self:I({nato=NATO}) -- Debug.
self:T3({nato=NATO})
------------------- -------------------
--- Temperature --- --- Temperature ---
------------------- -------------------
-- Temperature in °C.
local temperature=coord:GetTemperature(height) local temperature=coord:GetTemperature(height)
local TEMPERATURE=string.format("%d", temperature) local TEMPERATURE=string.format("%d", temperature)
-- Weather if self.TDegF then
local clouds, visibility, turbulence, fog, dust=self:GetStaticWeather() 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
end
------------------ ------------------
--- Visibility --- --- Visibility ---
------------------ ------------------
-- Get min visibility.
local visibilitymin=visibility local visibilitymin=visibility
if fog then if fog then
if fog.visibility<visibilitymin then if fog.visibility<visibilitymin then
visibilitymin=fog.visibility visibilitymin=fog.visibility
end end
end end
if dust then if dust then
if dust<visibilitymin then if dust<visibilitymin then
visibilitymin=dust visibilitymin=dust
end end
end end
-- Visibility in NM.
local VISIBILITY=string.format("%d", UTILS.Round(UTILS.MetersToNM(visibilitymin))) 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 --- --- Clouds ---
-------------- --------------
local cloudbase=clouds.base local cloudbase=clouds.base
local cloudceil=clouds.base+clouds.thickness local cloudceil=clouds.base+clouds.thickness
local clouddens=clouds.density local clouddens=clouds.density
-- Precepitation: 0=None, 1=Rain, 2=Thunderstorm.
local precepitation=tonumber(clouds.iprecptns) local precepitation=tonumber(clouds.iprecptns)
local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase)) local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase))
local CLOUDCEIL=string.format("%d", UTILS.MetersToFeet(cloudceil)) local CLOUDCEIL=string.format("%d", UTILS.MetersToFeet(cloudceil))
local CLOUDBASE0100=tostring(self:_GetHundreds( UTILS.MetersToFeet(cloudbase))) if self.metric then
local CLOUDBASE1000=tostring(self:_GetThousands(UTILS.MetersToFeet(cloudbase))) CLOUDBASE=string.format("%d", cloudbase)
local CLOUDCEIL0100=tostring(self:_GetHundreds( UTILS.MetersToFeet(cloudceil))) CLOUDCEIL=string.format("%d", cloudceil)
local CLOUDCEIL1000=tostring(self:_GetThousands(UTILS.MetersToFeet(cloudceil))) end
self:I({cloudbase0100=CLOUDBASE0100}) -- Cloud base/ceiling in thousands and hundrets of ft/meters.
self:I({cloudbase1000=CLOUDBASE1000}) 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. -- No cloud info for dynamic weather.
local CLOUDSogg="CloudsNotAvailable.ogg" local CLOUDSogg="CloudsNotAvailable.ogg"
local CLOUDSsub="Cloud coverage information not available" local CLOUDSsub="Cloud coverage information not available"
local CLOUDSdur=2.40 local CLOUDSdur=2.40
-- TODO: add static weather switch -- Only valid for static weather.
if true then if static then
if clouddens>=9 then if clouddens>=9 then
-- Overcast 9,10 -- Overcast 9,10
CLOUDSogg="CloudsOvercast.ogg" CLOUDSogg="CloudsOvercast.ogg"
@@ -447,12 +768,12 @@ function ATIS:onafterBroadcast(From, Event, To)
--- Transmission --- --- Transmission ---
-------------------- --------------------
local subduration=10 local subduration=self.subduration
local subtitle="" local subtitle=""
--Airbase name --Airbase name
subtitle=string.format("%s", self.airbasename) 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 then 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" subtitle=subtitle.." Airport"
end end
self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, subduration) self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, subduration)
@@ -468,16 +789,29 @@ function ATIS:onafterBroadcast(From, Event, To)
self.radioqueue:NewTransmission("TimeZulu.ogg", 0.89, self.soundpath, nil, 0.2, subtitle, subduration) self.radioqueue:NewTransmission("TimeZulu.ogg", 0.89, self.soundpath, nil, 0.2, subtitle, subduration)
-- Visibility -- Visibility
subtitle=string.format("Visibility %s NM", VISIBILITY) if self.metric then
subtitle=string.format("Visibility %s km", VISIBILITY)
else
subtitle=string.format("Visibility %s NM", VISIBILITY)
end
self.radioqueue:NewTransmission("Visibility.ogg", 0.8, self.soundpath, nil, 1.0, subtitle, subduration) self.radioqueue:NewTransmission("Visibility.ogg", 0.8, self.soundpath, nil, 1.0, subtitle, subduration)
self.radioqueue:Number2Transmission(VISIBILITY) self.radioqueue:Number2Transmission(VISIBILITY)
self.radioqueue:NewTransmission("NauticalMiles.ogg", 1.05, self.soundpath, nil, 0.2) 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)
end
-- Cloud base -- Cloud base
self.radioqueue:NewTransmission(CLOUDSogg, CLOUDSdur, self.soundpath, nil, 1.0, CLOUDSsub, subduration) self.radioqueue:NewTransmission(CLOUDSogg, CLOUDSdur, self.soundpath, nil, 1.0, CLOUDSsub, subduration)
if CLOUDBASE then if CLOUDBASE and static then
-- Base -- Base
self.radioqueue:NewTransmission("CloudBase.ogg", 0.81, self.soundpath, nil, 1.0, string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL), subduration) if self.metric then
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
else
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
end
self.radioqueue:NewTransmission("CloudBase.ogg", 0.81, self.soundpath, nil, 1.0, subtitle, subduration)
if tonumber(CLOUDBASE1000)>0 then if tonumber(CLOUDBASE1000)>0 then
self.radioqueue:Number2Transmission(CLOUDBASE1000) self.radioqueue:Number2Transmission(CLOUDBASE1000)
self.radioqueue:NewTransmission("Thousand.ogg", 0.55, self.soundpath, nil, 0.1) self.radioqueue:NewTransmission("Thousand.ogg", 0.55, self.soundpath, nil, 0.1)
@@ -496,7 +830,11 @@ function ATIS:onafterBroadcast(From, Event, To)
self.radioqueue:Number2Transmission(CLOUDCEIL0100) self.radioqueue:Number2Transmission(CLOUDCEIL0100)
self.radioqueue:NewTransmission("Hundred.ogg", 0.47, self.soundpath, nil, 0.1) self.radioqueue:NewTransmission("Hundred.ogg", 0.47, self.soundpath, nil, 0.1)
end end
self.radioqueue:NewTransmission("Feet.ogg", 0.45, self.soundpath, nil, 0.1) if self.metric then
self.radioqueue:NewTransmission("Meters.ogg", 0.59, self.soundpath, nil, 0.1)
else
self.radioqueue:NewTransmission("Feet.ogg", 0.45, self.soundpath, nil, 0.1)
end
end end
-- Weather phenomena -- Weather phenomena
@@ -542,8 +880,17 @@ function ATIS:onafterBroadcast(From, Event, To)
end end
end end
-- Altimeter QFE. -- Altimeter QNH/QFE.
self.radioqueue:NewTransmission("Altimeter.ogg", 0.7, self.soundpath, nil, 1.0, string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2]), subduration) if self.PmmHg then
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
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
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
end
end
self.radioqueue:NewTransmission("Altimeter.ogg", 0.7, self.soundpath, nil, 1.0, subtitle, subduration)
self.radioqueue:NewTransmission("QNH.ogg", 0.70, self.soundpath, nil, 0.5) self.radioqueue:NewTransmission("QNH.ogg", 0.70, self.soundpath, nil, 0.5)
self.radioqueue:Number2Transmission(QNH[1]) self.radioqueue:Number2Transmission(QNH[1])
self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2) self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2)
@@ -552,15 +899,36 @@ function ATIS:onafterBroadcast(From, Event, To)
self.radioqueue:Number2Transmission(QFE[1]) self.radioqueue:Number2Transmission(QFE[1])
self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2) self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2)
self.radioqueue:Number2Transmission(QFE[2]) self.radioqueue:Number2Transmission(QFE[2])
self.radioqueue:NewTransmission("InchesOfMercury.ogg", 1.16, self.soundpath, nil, 0.1) if self.PmmHg then
self.radioqueue:NewTransmission("MillimetersOfMercury.ogg", 1.53, self.soundpath, nil, 0.1)
else
if self.metric then
self.radioqueue:NewTransmission("HectoPascal.ogg", 1.15, self.soundpath, nil, 0.1)
else
self.radioqueue:NewTransmission("InchesOfMercury.ogg", 1.16, self.soundpath, nil, 0.1)
end
end
-- Temperature -- Temperature
self.radioqueue:NewTransmission("Temperature.ogg", 0.55, self.soundpath, nil, 1.0, string.format("Temperature %s C", TEMPERATURE), subduration) if self.TDegF then
subtitle=string.format("Temperature %s °F", TEMPERATURE)
else
subtitle=string.format("Temperature %s °C", TEMPERATURE)
end
self.radioqueue:NewTransmission("Temperature.ogg", 0.55, self.soundpath, nil, 1.0, subtitle, subduration)
self.radioqueue:Number2Transmission(TEMPERATURE) self.radioqueue:Number2Transmission(TEMPERATURE)
self.radioqueue:NewTransmission("DegreesCelsius.ogg", 1.28, self.soundpath, nil, 0.2) if self.TDegF then
self.radioqueue:NewTransmission("DegreesFahrenheit.ogg", 1.23, self.soundpath, nil, 0.2)
else
self.radioqueue:NewTransmission("DegreesCelsius.ogg", 1.28, self.soundpath, nil, 0.2)
end
-- Wind -- Wind
local subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED) if self.metric then
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
else
subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED)
end
if turbulence>0 then if turbulence>0 then
subtitle=subtitle..", gusting" subtitle=subtitle..", gusting"
end end
@@ -568,7 +936,11 @@ function ATIS:onafterBroadcast(From, Event, To)
self.radioqueue:Number2Transmission(WINDFROM) self.radioqueue:Number2Transmission(WINDFROM)
self.radioqueue:NewTransmission("At.ogg", 0.40, self.soundpath, nil, 0.2) self.radioqueue:NewTransmission("At.ogg", 0.40, self.soundpath, nil, 0.2)
self.radioqueue:Number2Transmission(WINDSPEED) self.radioqueue:Number2Transmission(WINDSPEED)
self.radioqueue:NewTransmission("Knots.ogg", 0.60, self.soundpath, nil, 0.2) if self.metric then
self.radioqueue:NewTransmission("MetersPerSecond.ogg", 1.14, self.soundpath, nil, 0.2)
else
self.radioqueue:NewTransmission("Knots.ogg", 0.60, self.soundpath, nil, 0.2)
end
if turbulence>0 then if turbulence>0 then
self.radioqueue:NewTransmission("Gusting.ogg", 0.55, self.soundpath, nil, 0.2) self.radioqueue:NewTransmission("Gusting.ogg", 0.55, self.soundpath, nil, 0.2)
end end
@@ -614,14 +986,15 @@ end
-- Misc Functions -- Misc Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get static weather of this mission from env.mission.weather. --- Get weather of this mission from env.mission.weather variable.
-- @param #ATIS self -- @param #ATIS self
-- @return #table Clouds table which has entries "thickness", "density", "base", "iprecptns". -- @return #table Clouds table which has entries "thickness", "density", "base", "iprecptns".
-- @return #number Visibility distance in meters. -- @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 #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 #number Dust density or nil if dust is disabled in the mission.
function ATIS:GetStaticWeather() -- @return #boolean static If true, static weather is used. If false, dynamic weather is used.
function ATIS:GetMissionWeather()
-- Weather data from mission file. -- Weather data from mission file.
local weather=env.mission.weather local weather=env.mission.weather
@@ -638,9 +1011,13 @@ function ATIS:GetStaticWeather()
]] ]]
local clouds=weather.clouds local clouds=weather.clouds
-- 0=static, 1=dynamic
local static=weather.atmosphere_type==0
-- Visibilty distance in meters. -- Visibilty distance in meters.
local visibility=weather.visibility.distance local visibility=weather.visibility.distance
-- Ground turbulence.
local turbulence=weather.groundTurbulence local turbulence=weather.groundTurbulence
-- Dust -- Dust
@@ -673,32 +1050,26 @@ function ATIS:GetStaticWeather()
self:T({turbulence=turbulence}) self:T({turbulence=turbulence})
self:T({fog=fog}) self:T({fog=fog})
self:T({dust=dust}) self:T({dust=dust})
return clouds, visibility, turbulence, fog, dust self:T({static=static})
return clouds, visibility, turbulence, fog, dust, static
end end
--- Get rounded hundreds of a number.
-- @param #ATIS self
-- @param #number n Number, e.g. 4359.
-- @return #number Thousands of n, e.g. 4 for 4359.
function ATIS:_GetHundreds(n)
local h=UTILS.Round(n/100 % 10, 0)
if h==10 then
h=0
end
return h
end
--- Get thousands of a number. --- Get thousands of a number.
-- @param #ATIS self -- @param #ATIS self
-- @param #number n Number, e.g. 4359. -- @param #number n Number, e.g. 4359.
-- @return #number Thousands of n, e.g. 4 for 4359. -- @return #string Thousands of n, e.g. "4" for 4359.
function ATIS:_GetThousands(n) -- @return #string Hundreds of n, e.g. "4" for 4359 because its rounded.
local h=UTILS.Round(n/100 % 10, 0) function ATIS:_GetThousandsAndHundreds(n)
local t=math.floor(n/1000 % 10)
if h==10 then local N=UTILS.Round(n/1000, 1)
t=t+1
end local S=UTILS.Split(string.format("%.1f", N), ".")
return t
local t=S[1]
local h=S[2]
return t, h
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+74 -55
View File
@@ -15,7 +15,6 @@
--- @type AIRBASE --- @type AIRBASE
-- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #string ClassName Name of the class, i.e. "AIRBASE".
-- @field #table CategoryName Names of airbase categories. -- @field #table CategoryName Names of airbase categories.
-- @field #table runways Table of runway data.
-- @field #number activerwyno Active runway number (forced). -- @field #number activerwyno Active runway number (forced).
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
@@ -58,7 +57,6 @@ AIRBASE = {
[Airbase.Category.HELIPAD] = "Helipad", [Airbase.Category.HELIPAD] = "Helipad",
[Airbase.Category.SHIP] = "Ship", [Airbase.Category.SHIP] = "Ship",
}, },
runways=nil,
activerwyno=nil, activerwyno=nil,
} }
@@ -548,7 +546,7 @@ function AIRBASE:GetParkingSpotsCoordinates(termtype)
-- Put coordinates of free spots into table. -- Put coordinates of free spots into table.
local spots={} local spots={}
for _,parkingspot in pairs(parkingdata) do for _,parkingspot in ipairs(parkingdata) do
-- Coordinates on runway are not returned unless explicitly requested. -- Coordinates on runway are not returned unless explicitly requested.
if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then
@@ -1011,15 +1009,12 @@ end
--- Get runways data. Only for airdromes! --- Get runways data. Only for airdromes!
-- @param #AIRBASE self -- @param #AIRBASE self
-- @param #number magvar (Optional) Magnetic variation in degrees.
-- @return #table Runway data. -- @return #table Runway data.
function AIRBASE:GetRunwayData() function AIRBASE:GetRunwayData(magvar)
-- Check if data is already initialized. -- Runway table.
if self.runways then local runways={}
return self.runways
else
self.runways={}
end
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
return {} return {}
@@ -1028,23 +1023,38 @@ function AIRBASE:GetRunwayData()
-- Get spawn points on runway. -- Get spawn points on runway.
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
self:T(string.format("Runway coords # = %d", #runwaycoords))
-- Magnetic declination. -- Magnetic declination.
local magvar=UTILS.GetMagneticDeclination() magvar=magvar or UTILS.GetMagneticDeclination()
for i=1,#runwaycoords,2 do local N=#runwaycoords
local dN=2
local ex=false
-- Assuming each runway has two points. local name=self:GetName()
local j=(i+1)/2 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. -- Coordinates of the two runway points.
local c1=runwaycoords[i] --Core.Point#COORDINATES local c1=runwaycoords[i] --Core.Point#COORDINATES
local c2=runwaycoords[i+1] --Core.Point#COORDINATES local c2=runwaycoords[j] --Core.Point#COORDINATES
-- Debug mark
--c1:MarkToAll("Runway Point 1")
--c2:MarkToAll("Runway Point 2")
-- Heading of runway. -- Heading of runway.
local hdg=c1:HeadingTo(c2) local hdg=c1:HeadingTo(c2)
@@ -1052,9 +1062,6 @@ function AIRBASE:GetRunwayData()
-- Runway ID: heading=070° ==> idx="07" -- Runway ID: heading=070° ==> idx="07"
local idx=string.format("%02d", UTILS.Round((hdg-magvar)/10, 0)) local idx=string.format("%02d", UTILS.Round((hdg-magvar)/10, 0))
-- Debug info.
self:T(string.format("Runway %d heading=%03d", j, hdg))
-- Runway table. -- Runway table.
local runway={} --#AIRBASE.Runway local runway={} --#AIRBASE.Runway
runway.heading=hdg runway.heading=hdg
@@ -1063,34 +1070,48 @@ function AIRBASE:GetRunwayData()
runway.position=c1 runway.position=c1
runway.endpoint=c2 runway.endpoint=c2
self:I(string.format("Airbase %s: Adding runway #%d: id=%s, heading=%03d, lendth=%d m", self:GetName(), #self.runways+1, runway.idx, runway.heading, runway.length)) -- 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))
-- Add runway. -- Add runway.
table.insert(self.runways, runway) table.insert(runways, runway)
-- Inverse runway.
local hdg=hdg-180
if hdg<0 then
hdg=hdg+360
end
-- Runway ID: heading=070° ==> ID="07"
local idx=string.format("%02d", UTILS.Round((hdg-magvar)/10, 0))
local runway={} --#AIRBASE.Runway
runway.heading=hdg
runway.idx=idx
runway.length=c1:Get2DDistance(c2)
runway.position=c2
runway.endpoint=c1
self:I(string.format("Airbase %s: Adding runway #%d: id=%s, heading=%03d, lendth=%d m", self:GetName(), #self.runways+1, runway.idx, runway.heading, runway.length))
-- Add inverse runway.
table.insert(self.runways, runway)
end end
return self.runways -- Get inverse runways
local inverse={}
for _,_runway in pairs(runways) do
local r=_runway --#AIRBASE.Runway
local runway={} --#AIRBASE.Runway
runway.heading=r.heading-180
if runway.heading<0 then
runway.heading=runway.heading+360
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))
-- Add runway.
table.insert(inverse, runway)
end
for _,runway in pairs(inverse) do
-- Add runway.
table.insert(runways, runway)
end
return runways
end end
--- Set the active runway in case it cannot be determined by the wind direction. --- Set the active runway in case it cannot be determined by the wind direction.
@@ -1102,11 +1123,12 @@ end
--- Get the active runway based on current wind direction. --- Get the active runway based on current wind direction.
-- @param #AIRBASE self -- @param #AIRBASE self
-- @param #number magvar (Optional) Magnetic variation in degrees.
-- @return #AIRBASE.Runway Active runway data table. -- @return #AIRBASE.Runway Active runway data table.
function AIRBASE:GetActiveRunway() function AIRBASE:GetActiveRunway(magvar)
-- Get runways data (initialize if necessary). -- Get runways data (initialize if necessary).
local runways=self:GetRunwayData() local runways=self:GetRunwayData(magvar)
-- Return user forced active runway if it was set. -- Return user forced active runway if it was set.
if self.activerwyno then if self.activerwyno then
@@ -1128,9 +1150,6 @@ function AIRBASE:GetActiveRunway()
Vwind.y=0 Vwind.y=0
Vwind.z=Vwind.z/norm Vwind.z=Vwind.z/norm
-- Debug.
self:T3({Vwind=Vwind})
-- Loop over runways. -- Loop over runways.
local dotmin=nil local dotmin=nil
for i,_runway in pairs(runways) do for i,_runway in pairs(runways) do
@@ -1146,7 +1165,7 @@ function AIRBASE:GetActiveRunway()
local dot=UTILS.VecDot(Vwind, Vrunway) local dot=UTILS.VecDot(Vwind, Vrunway)
-- Debug. -- 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? -- New min?
if dotmin==nil or dot<dotmin then if dotmin==nil or dot<dotmin then
@@ -1156,7 +1175,7 @@ function AIRBASE:GetActiveRunway()
end end
else else
self:I("WARNING: Norm of wind is zero! Cannot determine active runway based on wind direction.") self:E("WARNING: Norm of wind is zero! Cannot determine active runway based on wind direction.")
end end
return runways[iact] return runways[iact]