diff --git a/Moose Development/Moose/Core/Beacon.lua b/Moose Development/Moose/Core/Beacon.lua index c5203a6b3..5c466e583 100644 --- a/Moose Development/Moose/Core/Beacon.lua +++ b/Moose Development/Moose/Core/Beacon.lua @@ -1,9 +1,9 @@ --- **Core** - TACAN and other beacons. --- +-- -- === --- +-- -- ## Features: --- +-- -- * Provide beacon functionality to assist pilots. -- -- === @@ -14,34 +14,34 @@ -- @image Core_Radio.JPG --- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon --- +-- -- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. --- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. +-- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon. -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is --- attach to a cargo crate, for exemple. --- --- ## AA TACAN Beacon usage --- --- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. --- Use @#BEACON:StopAATACAN}() to stop it. --- +-- attach to a cargo crate, for exemple. +-- +-- ## Aircraft TACAN Beacon usage +-- +-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:ActivateTACAN}() to set the beacon parameters and start the beacon. +-- Use @#BEACON:StopRadioBeacon}() to stop it. +-- -- ## General Purpose Radio Beacon usage --- +-- -- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with -- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- Use @{#BEACON:StopRadioBeacon}() to stop it. --- +-- -- @type BEACON -- @field #string ClassName Name of the class "BEACON". -- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities. -- @extends Core.Base#BASE BEACON = { - ClassName = "BEACON", + ClassName = "BEACON", Positionable = nil, - name = nil, + name = nil, } ---- Beacon types supported by DCS. +--- Beacon types supported by DCS. -- @type BEACON.Type -- @field #number NULL -- @field #number VOR @@ -65,19 +65,19 @@ BEACON = { -- @field #number ICLS_GLIDESLOPE -- @field #number NAUTICAL_HOMER BEACON.Type={ - NULL = 0, + NULL = 0, VOR = 1, DME = 2, - VOR_DME = 3, + VOR_DME = 3, TACAN = 4, - VORTAC = 5, + VORTAC = 5, RSBN = 128, - BROADCAST_STATION = 1024, + BROADCAST_STATION = 1024, HOMER = 8, - AIRPORT_HOMER = 4104, - AIRPORT_HOMER_WITH_MARKER = 4136, + AIRPORT_HOMER = 4104, + AIRPORT_HOMER_WITH_MARKER = 4136, ILS_FAR_HOMER = 16408, - ILS_NEAR_HOMER = 16424, + ILS_NEAR_HOMER = 16424, ILS_LOCALIZER = 16640, ILS_GLIDESLOPE = 16896, PRMG_LOCALIZER = 33024, @@ -95,26 +95,26 @@ BEACON.Type={ -- @field #number TACAN TACtical Air Navigation system on ground. -- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band. -- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band. --- @field #number VOR Very High Frequency Omnidirectional Range +-- @field #number VOR Very High Frequency Omni-Directional Range -- @field #number ILS_LOCALIZER ILS localizer -- @field #number ILS_GLIDESLOPE ILS glide slope. -- @field #number PRGM_LOCALIZER PRGM localizer. -- @field #number PRGM_GLIDESLOPE PRGM glide slope. -- @field #number BROADCAST_STATION Broadcast station. --- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. +-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. -- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band. -- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band. -- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME). -- @field #number ICLS_LOCALIZER Carrier landing system. -- @field #number ICLS_GLIDESLOPE Carrier landing system. BEACON.System={ - PAR_10 = 1, - RSBN_5 = 2, - TACAN = 3, + PAR_10 = 1, + RSBN_5 = 2, + TACAN = 3, TACAN_TANKER_X = 4, TACAN_TANKER_Y = 5, - VOR = 6, - ILS_LOCALIZER = 7, + VOR = 6, + ILS_LOCALIZER = 7, ILS_GLIDESLOPE = 8, PRMG_LOCALIZER = 9, PRMG_GLIDESLOPE = 10, @@ -131,27 +131,28 @@ BEACON.System={ -- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead. -- @param #BEACON self -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. --- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid. -function BEACON:New( Positionable ) +-- @return #BEACON Beacon object or #nil if the positionable is invalid. +function BEACON:New(Positionable) -- Inherit BASE. - local self = BASE:Inherit( self, BASE:New() ) -- #BEACON - + local self=BASE:Inherit(self, BASE:New()) --#BEACON + -- Debug. - self:F( Positionable ) - + self:F(Positionable) + -- Set positionable. - if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure POSITIONABLE is valid + if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid self.Positionable = Positionable - self.name = Positionable:GetName() - self:I( string.format( "New BEACON %s", tostring( self.name ) ) ) + self.name=Positionable:GetName() + self:I(string.format("New BEACON %s", tostring(self.name))) return self end - - self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } ) + + self:E({"The passed positionable is invalid, no BEACON created", Positionable}) return nil end + --- Activates a TACAN BEACON. -- @param #BEACON self -- @param #number Channel TACAN channel, i.e. the "10" part in "10Y". @@ -161,55 +162,60 @@ end -- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @return #BEACON self -- @usage --- -- -- Let's create a TACAN Beacon for a tanker --- local myUnit = UNIT:FindByName("MyUnit") +-- local myUnit = UNIT:FindByName("MyUnit") -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon --- +-- -- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon --- -function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration ) - self:T( { channel = Channel, mode = Mode, callsign = Message, bearing = Bearing, duration = Duration } ) - +function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration) + self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration}) + + Mode=Mode or "Y" + -- Get frequency. - local Frequency = UTILS.TACANToFrequency( Channel, Mode ) - + local Frequency=UTILS.TACANToFrequency(Channel, Mode) + -- Check. - if not Frequency then - self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) + if not Frequency then + self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) return self end - + -- Beacon type. - local Type = BEACON.Type.TACAN - + local Type=BEACON.Type.TACAN + -- Beacon system. - local System = BEACON.System.TACAN - + local System=BEACON.System.TACAN + -- Check if unit is an aircraft and set system accordingly. - local AA = self.Positionable:IsAir() + local AA=self.Positionable:IsAir() + + if AA then - System = 5 -- NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER + System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER -- Check if "Y" mode is selected for aircraft. - if Mode ~= "Y" then - self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } ) + if Mode=="X" then + --self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable}) + System=BEACON.System.TACAN_TANKER_X + else + System=BEACON.System.TACAN_TANKER_Y end end - + -- Attached unit. - local UnitID = self.Positionable:GetID() - + local UnitID=self.Positionable:GetID() + -- Debug. - self:I( { string.format( "BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring( self.name ), Channel, Mode, Message, tostring( Bearing ), tostring( Duration ) ) } ) - + self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))}) + -- Start beacon. - self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing ) - + self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing) + -- Stop scheduler. if Duration then - self.Positionable:DeactivateBeacon( Duration ) + self.Positionable:DeactivateBeacon(Duration) end - + return self end @@ -219,27 +225,28 @@ end -- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon. -- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @return #BEACON self -function BEACON:ActivateICLS( Channel, Callsign, Duration ) - self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } ) - +function BEACON:ActivateICLS(Channel, Callsign, Duration) + self:F({Channel=Channel, Callsign=Callsign, Duration=Duration}) + -- Attached unit. - local UnitID = self.Positionable:GetID() - + local UnitID=self.Positionable:GetID() + -- Debug - self:T2( { "ICLS BEACON started!" } ) - + self:T2({"ICLS BEACON started!"}) + -- Start beacon. - self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign ) - + self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign) + -- Stop scheduler if Duration then -- Schedule the stop of the BEACON if asked by the MD - self.Positionable:DeactivateBeacon( Duration ) + self.Positionable:DeactivateBeacon(Duration) end - + return self end ---- Activates a TACAN BEACON on an Aircraft. +--- DEPRECATED: Please use @{BEACON:ActivateTACAN}() instead. +-- Activates a TACAN BEACON on an Aircraft. -- @param #BEACON self -- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels -- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon @@ -247,57 +254,58 @@ end -- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. -- @return #BEACON self -- @usage --- -- -- Let's create a TACAN Beacon for a tanker --- local myUnit = UNIT:FindByName("MyUnit") +-- local myUnit = UNIT:FindByName("MyUnit") -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon --- +-- -- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon --- -function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration ) - self:F( { TACANChannel, Message, Bearing, BeaconDuration } ) - +function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration) + self:F({TACANChannel, Message, Bearing, BeaconDuration}) + local IsValid = true - + if not self.Positionable:IsAir() then - self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } ) + self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable}) IsValid = false end - - local Frequency = self:_TACANToFrequency( TACANChannel, "Y" ) - if not Frequency then - self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) + + local Frequency = self:_TACANToFrequency(TACANChannel, "Y") + if not Frequency then + self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) IsValid = false end - - -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing - -- or 14 (TACAN_AA_MODE_Y) if it does not + + -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not local System if Bearing then - System = 5 + System = BEACON.System.TACAN_TANKER_Y else - System = 14 + System = BEACON.System.TACAN_AA_MODE_Y end - + if IsValid then -- Starts the BEACON - self:T2( { "AA TACAN BEACON started !" } ) - self.Positionable:SetCommand( { + self:T2({"AA TACAN BEACON started !"}) + self.Positionable:SetCommand({ id = "ActivateBeacon", params = { - type = 4, + type = BEACON.Type.TACAN, system = System, callsign = Message, + AA = true, frequency = Frequency, - }, - } ) - + bearing = Bearing, + modeChannel = "Y", + } + }) + if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD - SCHEDULER:New( nil, function() + SCHEDULER:New(nil, + function() self:StopAATACAN() - end, {}, BeaconDuration ) + end, {}, BeaconDuration) end end - + return self end @@ -307,19 +315,21 @@ end function BEACON:StopAATACAN() self:F() if not self.Positionable then - self:E( { "Start the beacon first before stopping it!" } ) + self:E({"Start the beacon first before stoping it !"}) else - self.Positionable:SetCommand( { - id = 'DeactivateBeacon', - params = {}, - } ) + self.Positionable:SetCommand({ + id = 'DeactivateBeacon', + params = { + } + }) end end + --- Activates a general purpose Radio Beacon -- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. --- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. --- They can home in on these specific frequencies : +-- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc. +-- The following e.g. can home in on these specific frequencies : -- * **Mi8** -- * R-828 -> 20-60MHz -- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM @@ -342,63 +352,64 @@ end -- -- -- Set the beacon and start it -- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60) -function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration ) - self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } ) +function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration) + self:F({FileName, Frequency, Modulation, Power, BeaconDuration}) local IsValid = false - + -- Check the filename - if type( FileName ) == "string" then - if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then - if not FileName:find( "l10n/DEFAULT/" ) then + if type(FileName) == "string" then + if FileName:find(".ogg") or FileName:find(".wav") then + if not FileName:find("l10n/DEFAULT/") then FileName = "l10n/DEFAULT/" .. FileName end IsValid = true end end if not IsValid then - self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } ) + self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName}) end - + -- Check the Frequency - if type( Frequency ) ~= "number" and IsValid then - self:E( { "Frequency invalid. ", Frequency } ) + if type(Frequency) ~= "number" and IsValid then + self:E({"Frequency invalid. ", Frequency}) IsValid = false end Frequency = Frequency * 1000000 -- Conversion to Hz - + -- Check the modulation - if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ? - self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } ) + if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ? + self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation}) IsValid = false end - + -- Check the Power - if type( Power ) ~= "number" and IsValid then - self:E( { "Power is invalid. ", Power } ) + if type(Power) ~= "number" and IsValid then + self:E({"Power is invalid. ", Power}) IsValid = false end - Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that - + Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that + if IsValid then - self:T2( { "Activating Beacon on ", Frequency, Modulation } ) + self:T2({"Activating Beacon on ", Frequency, Modulation}) -- Note that this is looped. I have to give this transmission a unique name, I use the class ID - trigger.action.radioTransmission( FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring( self.ID ) ) - - if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD - SCHEDULER:New( nil, function() - self:StopRadioBeacon() - end, {}, BeaconDuration ) - end - end + trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID)) + + if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD + SCHEDULER:New( nil, + function() + self:StopRadioBeacon() + end, {}, BeaconDuration) + end + end end ---- Stops the AA TACAN BEACON +--- Stop the Radio Beacon -- @param #BEACON self -- @return #BEACON self function BEACON:StopRadioBeacon() self:F() -- The unique name of the transmission is the class ID - trigger.action.stopRadioTransmission( tostring( self.ID ) ) + trigger.action.stopRadioTransmission(tostring(self.ID)) return self end @@ -406,26 +417,26 @@ end -- @param #BEACON self -- @param #number TACANChannel -- @param #string TACANMode --- @return #number Frequency +-- @return #number Frequecy -- @return #nil if parameters are invalid -function BEACON:_TACANToFrequency( TACANChannel, TACANMode ) - self:F3( { TACANChannel, TACANMode } ) +function BEACON:_TACANToFrequency(TACANChannel, TACANMode) + self:F3({TACANChannel, TACANMode}) - if type( TACANChannel ) ~= "number" then + if type(TACANChannel) ~= "number" then if TACANMode ~= "X" and TACANMode ~= "Y" then return nil -- error in arguments end end - - -- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. - -- I have no idea what it does but it seems to work + +-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. +-- I have no idea what it does but it seems to work local A = 1151 -- 'X', channel >= 64 - local B = 64 -- channel >= 64 - + local B = 64 -- channel >= 64 + if TACANChannel < 64 then B = 1 end - + if TACANMode == 'Y' then A = 1025 if TACANChannel < 64 then @@ -436,6 +447,6 @@ function BEACON:_TACANToFrequency( TACANChannel, TACANMode ) A = 962 end end - + return (A + TACANChannel - B) * 1000000 end