mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-17 03:11:28 +00:00
ATIS
This commit is contained in:
@@ -562,7 +562,7 @@ do -- COORDINATE
|
||||
|
||||
--- Return the height of the land at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number
|
||||
-- @return #number Land height (ASL) in meters.
|
||||
function COORDINATE:GetLandHeight()
|
||||
local Vec2 = { x = self.x, y = self.z }
|
||||
return land.getHeight( Vec2 )
|
||||
|
||||
@@ -56,6 +56,7 @@ RADIOQUEUE = {
|
||||
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||
-- @field #number interval Interval in seconds before next transmission.
|
||||
|
||||
|
||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||
@@ -219,6 +220,7 @@ function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval,
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
transmission.subtitle=subtitle
|
||||
transmission.interval=interval or 0
|
||||
if transmission.subtitle then
|
||||
transmission.subduration=subduration or 5
|
||||
else
|
||||
@@ -322,7 +324,7 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
else
|
||||
|
||||
-- Broadcasting from carrier. No subtitle possible. Need to send messages to players.
|
||||
self:I(self.lid..string.format("Broadcasting from carrier via trigger.action.radioTransmission()."))
|
||||
self:I(self.lid..string.format("Broadcasting via trigger.action.radioTransmission()."))
|
||||
|
||||
-- Position from where to transmit.
|
||||
local vec3=nil
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
-- @field #number modulation Radio modulation 0=AM or 1=FM.
|
||||
-- @field Core.RadioQueue#RADIOQUEUE radioqueue Radio queue for broadcasing messages.
|
||||
-- @field #string soundpath Path to sound files.
|
||||
-- @field #string relayunitname Name of the radio relay unit.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Be surprised!
|
||||
@@ -46,6 +47,7 @@ ATIS = {
|
||||
modulation = nil,
|
||||
radioqueue = nil,
|
||||
soundpath = nil,
|
||||
relayunitname = nil,
|
||||
}
|
||||
|
||||
--- ATIS class version.
|
||||
@@ -92,8 +94,8 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||
self:AddTransition("*", "Status", "*") -- Update status.
|
||||
self:AddTransition("*", "Broadcast", "*") -- Update status.
|
||||
self:AddTransition("*", "CheckQueue", "*") -- Update status.
|
||||
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
|
||||
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@@ -123,8 +125,6 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
BASE:TraceClass(self.ClassName)
|
||||
BASE:TraceLevel(1)
|
||||
end
|
||||
|
||||
self:__Start(1)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -142,6 +142,14 @@ function ATIS:SetSoundfilesPath(path)
|
||||
self:I(self.lid..string.format("Setting sound files path to %s", self.soundpath))
|
||||
end
|
||||
|
||||
--- Set sound files folder within miz file.
|
||||
-- @param #ATIS self
|
||||
-- @param #string unitname
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetRadioRelayUnitName(unitname)
|
||||
self.relayunitname=unitname
|
||||
self:I(self.lid..string.format("Setting radio relay unit to %s", self.relayunitname))
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Start & Status
|
||||
@@ -158,24 +166,24 @@ function ATIS:onafterStart(From, Event, To)
|
||||
self.radioqueue=RADIOQUEUE:New(self.frequency, self.modulation)
|
||||
|
||||
self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
|
||||
self.radioqueue:SetSenderUnitName(self.relayunitname)
|
||||
|
||||
self.radioqueue:Start(1, 0.01)
|
||||
|
||||
self.radioqueue:SetDigit(0, "0-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(1, "1-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(2, "2-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(3, "3-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(4, "4-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(5, "5-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(6, "6-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(7, "7-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(8, "8-continue.wav", 0.8, self.soundpath)
|
||||
self.radioqueue:SetDigit(9, "9-continue.wav", 0.8, 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(2, "N-2.ogg", 0.35, self.soundpath)
|
||||
self.radioqueue:SetDigit(3, "N-3.ogg", 0.40, self.soundpath)
|
||||
self.radioqueue:SetDigit(4, "N-4.ogg", 0.36, self.soundpath)
|
||||
self.radioqueue:SetDigit(5, "N-5.ogg", 0.42, self.soundpath)
|
||||
self.radioqueue:SetDigit(6, "N-6.ogg", 0.53, self.soundpath)
|
||||
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)
|
||||
|
||||
self.radioqueue:Start(1, 0.05)
|
||||
|
||||
-- Init status updates.
|
||||
self:__Status(-1)
|
||||
self:__CheckQueue(-2)
|
||||
self:__Status(-2)
|
||||
self:__CheckQueue(-3)
|
||||
end
|
||||
|
||||
--- Update status.
|
||||
@@ -188,7 +196,7 @@ function ATIS:onafterStatus(From, Event, To)
|
||||
local text=string.format("State %s", fsmstate)
|
||||
self:I(self.lid..text)
|
||||
|
||||
self:__Status(-30)
|
||||
self:__Status(30)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -202,17 +210,106 @@ function ATIS:onafterCheckQueue(From, Event, To)
|
||||
if #self.radioqueue.queue==0 then
|
||||
self:I(self.lid..string.format("Radio queue empty. Repeating message."))
|
||||
self:Broadcast()
|
||||
else
|
||||
self:I(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue))
|
||||
end
|
||||
|
||||
-- Check back in 5 seconds.
|
||||
self:__CheckQueue(-5)
|
||||
self:__CheckQueue(5)
|
||||
end
|
||||
|
||||
--- Update status.
|
||||
-- @param #ATIS self
|
||||
function ATIS:onafterBroadcast(From, Event, To)
|
||||
|
||||
self.radioqueue:Number2Transmission("0123456789", 0, 0)
|
||||
local coord=self.airbase:GetCoordinate()
|
||||
|
||||
local height=coord:GetLandHeight()
|
||||
|
||||
----------------
|
||||
--- Pressure ---
|
||||
----------------
|
||||
local qfe=coord:GetPressure(height+5)
|
||||
local qnh=coord:GetPressure(0)
|
||||
|
||||
-- Convert to inHg
|
||||
qfe=UTILS.hPa2inHg(qfe)
|
||||
qnh=UTILS.hPa2inHg(qnh)
|
||||
|
||||
local QFE=UTILS.Split(string.format("%.2f", qfe), ".")
|
||||
local QNH=UTILS.Split(string.format("%.2f", qnh), ".")
|
||||
|
||||
|
||||
local runway=self.airbase:GetActiveRunway()
|
||||
|
||||
local windFrom, windSpeed=coord:GetWind()
|
||||
local WINDFROM=string.format("%03d", windFrom)
|
||||
local WINDSPEED=string.format("%d", UTILS.MpsToKnots(windSpeed))
|
||||
|
||||
-- Time
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
-- TODO add zulu time correction.
|
||||
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
local zulu=UTILS.Split(clock, ":")
|
||||
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
||||
|
||||
-- Temperature
|
||||
local temperature=coord:GetTemperature(height+5)
|
||||
local TEMPERATURE=string.format("%d", temperature)
|
||||
|
||||
-- Weather
|
||||
local clouds, visibility, fog, dust=self:GetStaticWeather()
|
||||
local cloudbase=clouds.base
|
||||
local cloudceiling=clouds.base+clouds.thickness
|
||||
local clouddensity=clouds.density
|
||||
|
||||
local subduration=10
|
||||
|
||||
-- Batumi Airbase.
|
||||
self.radioqueue:NewTransmission("Batumi.ogg", 0.52, self.soundpath, nil, nil, "Batumi Airport", subduration)
|
||||
self.radioqueue:NewTransmission("Airport.ogg", 0.65, self.soundpath)
|
||||
|
||||
-- 1300 Zulu
|
||||
self.radioqueue:Number2Transmission(ZULU)
|
||||
self.radioqueue:NewTransmission("TimeZulu.ogg", 0.89, self.soundpath, nil, nil, string.format("%s Zulu Time", ZULU), subduration)
|
||||
|
||||
-- Visibility
|
||||
local visibility="3"
|
||||
self.radioqueue:NewTransmission("Visibility.ogg", 0.8, self.soundpath, nil, 1.0, string.format("Visibility %s NM", visibility), subduration)
|
||||
self.radioqueue:Number2Transmission(visibility)
|
||||
-- TODO: 0.8
|
||||
self.radioqueue:NewTransmission("NauticalMiles.ogg", 0.8, self.soundpath, nil, 0.2)
|
||||
|
||||
-- Altimeter QFE.
|
||||
self.radioqueue:NewTransmission("Altimeter.ogg", 0.7, self.soundpath, nil, 2.0, string.format("Altimeter QFE %s.%s inHg, QNH %s.%s inHg", QFE[1], QFE[2], QNH[1], QNH[2]), subduration)
|
||||
self.radioqueue:NewTransmission("QFE.ogg", 0.62, self.soundpath)
|
||||
self.radioqueue:Number2Transmission(QFE[1])
|
||||
self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2)
|
||||
self.radioqueue:Number2Transmission(QFE[2])
|
||||
self.radioqueue:NewTransmission("QNH.ogg", 0.70, self.soundpath, nil, 2.0)
|
||||
self.radioqueue:Number2Transmission(QNH[1])
|
||||
self.radioqueue:NewTransmission("Decimal.ogg", 0.58, self.soundpath, nil, 0.2)
|
||||
self.radioqueue:Number2Transmission(QNH[2])
|
||||
|
||||
-- Temperature
|
||||
self.radioqueue:NewTransmission("Temperature.ogg", 0.7, self.soundpath, nil, 2.0, string.format("Temperature %s C", TEMPERATURE), subduration)
|
||||
self.radioqueue:Number2Transmission(TEMPERATURE)
|
||||
-- TODO: Celcius --> Celsius, 0.9?
|
||||
self.radioqueue:NewTransmission("DegreesCelsius.ogg", 0.9, self.soundpath, nil, 0.2)
|
||||
|
||||
-- Wind
|
||||
self.radioqueue:NewTransmission("WindFrom.ogg", 0.7, self.soundpath, nil, 2.0, string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED), subduration)
|
||||
self.radioqueue:Number2Transmission(WINDFROM)
|
||||
self.radioqueue:NewTransmission("At.ogg", 0.7, self.soundpath, nil, 0.2)
|
||||
self.radioqueue:Number2Transmission(WINDSPEED)
|
||||
self.radioqueue:NewTransmission("Knots.ogg", 0.7, self.soundpath, nil, 0.2)
|
||||
|
||||
-- Active runway.
|
||||
self.radioqueue:NewTransmission("ActiveRunway.ogg", 1.05, self.soundpath, nil, 2.0, string.format("Active runway %s", runway.idx), subduration)
|
||||
self.radioqueue:Number2Transmission(runway.idx)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
--- @type AIRBASE
|
||||
-- @field #string ClassName Name of the class, i.e. "AIRBASE".
|
||||
-- @field #table CategoryName Names of airbase categories.
|
||||
-- @field #table runways Table of runway data.
|
||||
-- @field #number activerwyno Active runway number (forced).
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle the DCS Airbase objects:
|
||||
@@ -56,6 +58,8 @@ AIRBASE = {
|
||||
[Airbase.Category.HELIPAD] = "Helipad",
|
||||
[Airbase.Category.SHIP] = "Ship",
|
||||
},
|
||||
runways=nil,
|
||||
activerwyno=nil,
|
||||
}
|
||||
|
||||
--- Enumeration to identify the airbases in the Caucasus region.
|
||||
@@ -326,6 +330,14 @@ AIRBASE.TerminalType = {
|
||||
FighterAircraft=244,
|
||||
}
|
||||
|
||||
--- Runway data.
|
||||
-- @type AIRBASE.Runway
|
||||
-- @field #number heading Heading of the runway in degrees.
|
||||
-- @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.
|
||||
@@ -405,13 +417,17 @@ end
|
||||
|
||||
--- Get all airbases of the current map. This includes ships and FARPS.
|
||||
-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned.
|
||||
-- @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)
|
||||
function AIRBASE.GetAllAirbases(coalition, category)
|
||||
|
||||
local airbases={}
|
||||
for _,airbase in pairs(_DATABASE.AIRBASES) do
|
||||
for _,_airbase in pairs(_DATABASE.AIRBASES) do
|
||||
local airbase=_airbase --#AIRBASE
|
||||
if (coalition~=nil and airbase:GetCoalition()==coalition) or coalition==nil then
|
||||
table.insert(airbases, airbase)
|
||||
if category==nil or category==airbase:GetAirbaseCategory() then
|
||||
table.insert(airbases, airbase)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -993,7 +1009,153 @@ function AIRBASE._CheckTerminalType(Term_Type, termtype)
|
||||
return match
|
||||
end
|
||||
|
||||
|
||||
--- Get runways data. Only for airdromes!
|
||||
-- @param #AIRBASE self
|
||||
-- @return #table Runway data.
|
||||
function AIRBASE:GetRunwayData()
|
||||
|
||||
-- Check if data is already initialized.
|
||||
if self.runways then
|
||||
return self.runways
|
||||
else
|
||||
self.runways={}
|
||||
end
|
||||
|
||||
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get spawn points on runway.
|
||||
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
|
||||
|
||||
self:T(string.format("Runway coords # = %d", #runwaycoords))
|
||||
|
||||
for i=1,#runwaycoords,2 do
|
||||
|
||||
-- Assuming each runway has two points.
|
||||
local j=(i+1)/2
|
||||
|
||||
-- Coordinates of the two runway points.
|
||||
local c1=runwaycoords[i] --Core.Point#COORDINATES
|
||||
local c2=runwaycoords[i+1] --Core.Point#COORDINATES
|
||||
|
||||
-- Debug mark
|
||||
--c1:MarkToAll("Runway Point 1")
|
||||
--c2:MarkToAll("Runway Point 2")
|
||||
|
||||
-- Heading of runway.
|
||||
local hdg=c1:HeadingTo(c2)
|
||||
|
||||
-- Runway ID: heading=070° ==> idx="07"
|
||||
local idx=string.format("%02d", UTILS.Round(hdg/10, 0))
|
||||
|
||||
-- Debug info.
|
||||
self:T(string.format("Runway %d heading=%03d", j, hdg))
|
||||
|
||||
-- Runway table.
|
||||
local runway={} --#AIRBASE.Runway
|
||||
runway.heading=hdg
|
||||
runway.idx=idx
|
||||
runway.length=c1:Get2DDistance(c2)
|
||||
runway.position=c1
|
||||
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))
|
||||
|
||||
-- Add runway.
|
||||
table.insert(self.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/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
|
||||
|
||||
return self.runways
|
||||
end
|
||||
|
||||
--- Set the active runway in case it cannot be determined by the wind direction.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #number iactive Number of the active runway in the runway data table.
|
||||
function AIRBASE:SetActiveRunway(iactive)
|
||||
self.activerwyno=iactive
|
||||
end
|
||||
|
||||
--- Get the active runway based on current wind direction.
|
||||
-- @param #AIRBASE self
|
||||
-- @return #AIRBASE.Runway Active runway data table.
|
||||
function AIRBASE:GetActiveRunway()
|
||||
|
||||
-- Get runways data (initialize if necessary).
|
||||
local runways=self:GetRunwayData()
|
||||
|
||||
-- Return user forced active runway if it was set.
|
||||
if self.activerwyno then
|
||||
return runways[self.activerwyno]
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
-- Debug.
|
||||
self:T3({Vwind=Vwind})
|
||||
|
||||
-- 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))
|
||||
|
||||
-- New min?
|
||||
if dotmin==nil or dot<dotmin then
|
||||
dotmin=dot
|
||||
iact=i
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return runways[iact]
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user