diff --git a/Moose Development/Moose/Core/RadioQueue.lua b/Moose Development/Moose/Core/RadioQueue.lua index 756b5a967..33857ba43 100644 --- a/Moose Development/Moose/Core/RadioQueue.lua +++ b/Moose Development/Moose/Core/RadioQueue.lua @@ -113,18 +113,20 @@ end -- @param #number dt (Optional) Time step in seconds for checking the queue. Default 0.01 sec. -- @return #RADIOQUEUE self The RADIOQUEUE object. function RADIOQUEUE:Start(delay, dt) - + + -- Delay before start. self.delay=delay or 1 + -- Time interval for queue check. self.dt=dt or 0.01 + -- Debug message. self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)", self.alias, self.frequency/1000000, self.modulation, delay, dt)) - + -- Start Scheduler. if self.schedonce then - self:_CheckRadioQueueDelayed(self.delta) + self:_CheckRadioQueueDelayed(delay) else - --self.RQid=self.scheduler:Schedule(self, self._CheckRadioQueue, {}, delay, dt) self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, delay, dt) end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d36441d4c..2cfe1865d 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3846,7 +3846,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if group:IsAlive()==true then self:_DebugMessage(string.format("Removing group %s.", group:GetName()), 5) -- Setting parameter to false, i.e. creating NO dead or remove unit event, seems to not confuse the dispatcher logic. - -- Create a RemoveUnit event to let the FLIGHTCONTROL now, the parking is now free. + -- Create a RemoveUnit event to let the FLIGHTCONTROL know, the parking is now free. -- TODO: Need to check if my above comment not to confuse the dispatcher logic is broken by this! group:Destroy() end diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index b4f667eb4..cf771cd38 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -1102,7 +1102,7 @@ function AIRWING:onafterNewAsset(From, Event, To, asset, assignment) squad:GetModex(asset) - asset.terminalType=AIRBASE.TerminalType.OpenBig + --asset.terminalType=AIRBASE.TerminalType.OpenBig -- Add asset to squadron. table.insert(squad.assets, asset) @@ -1143,11 +1143,15 @@ function AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) -- Set airwing. flightgroup:SetAirwing(self) - --flightgroup.group:OptionProhibitAfterburner(true) - -- Set asset flightgroup. asset.flightgroup=flightgroup + local squadron=self:GetSquadronOfAsset(asset) + local Tacan=squadron:GetTACAN() + if Tacan then + flightgroup:SetTACAN(Tacan) + end + -- Not requested any more. asset.requested=nil diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index d03a5bba7..d8923dc57 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -581,6 +581,43 @@ function AUFTRAG:NewCAS(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, Targ return mission end +--- Create a CAS mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. +-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged. +-- @param #table TargetTypes Table of target types. Default {"Helicopters", "Ground Units", "Light armed ships"}. +-- @return #AUFTRAG self +function AUFTRAG:NewFACA(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, TargetTypes) + + -- Ensure given TargetTypes parameter is a table. + if TargetTypes then + if type(TargetTypes)~="table" then + TargetTypes={TargetTypes} + end + end + + -- Create ORBIT first. + local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg) + + -- CAS paramters. + mission.type=AUFTRAG.Type.FACA + + mission.engageZone=ZoneCAS or ZONE_RADIUS:New("CAS Zone", OrbitCoordinate:GetVec2(), Leg) + mission.engageTargetTypes=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"} + + mission:_SetLogID() + + mission.missionTask=ENUMS.MissionTask.AFAC + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end --- Create a BAI mission. @@ -2004,7 +2041,7 @@ function AUFTRAG:GetDCSMissionTask() -- FAC Mission -- ----------------- - local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageAsGroup, self.engageWeaponType, Designation, Datalink) + local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageAsGroup, self.engageWeaponType, self.facDesignation, self.facDatalink) table.insert(DCStasks, DCStask) @@ -2087,6 +2124,8 @@ function AUFTRAG:GetDCSMissionTask() ----------------------- -- TODO: What? + + local DCStask=CONTROLLABLE.TaskEmbarking(self,Vec2,GroupSetForEmbarking,Duration,DistributionGroupSet) else self:E(self.lid..string.format("ERROR: Unknown mission task!")) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 940376f3c..cfe5dc6c3 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -1888,11 +1888,12 @@ function FLIGHTCONTROL:_LandAI(flight, parking) self:_RemoveFlightFromQueue(self.Qholding, flight, "holding") -- Altitude above ground for a glide slope of 3 degrees.. - local alpha=math.rad(3) - local x1=UTILS.NMToMeters(10) - local x2=UTILS.NMToMeters(5) + local x1=flight.ishelo and UTILS.NMToMeters(5.0) or UTILS.NMToMeters(10) + local x2=flight.ishelo and UTILS.NMToMeters(2.5) or UTILS.NMToMeters(5) + local alpha=math.rad(3) local h1=x1*math.tan(alpha) local h2=x2*math.tan(alpha) + local SpeedLand=140 local SpeedTo=180 diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index e3c436579..aa7e54d44 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -68,8 +68,11 @@ -- @field #number TACANchannel TACAN channel. -- @field #string TACANmode TACAN mode, i.e. "Y" (or "X"). -- @field #string TACANmorse TACAN morse code. --- @field #number RadioFreq Default Radio frequency in MHz. --- @field #number RadioModu Default Radio modulation `radio.modulation.AM` or `radio.modulation.FM`. +-- @field #number radioFreq Default Radio frequency in MHz. +-- @field #number radioModu Default Radio modulation `radio.modulation.AM` or `radio.modulation.FM`. +-- @field #boolean radioUse Use radio. If false, communications are turned off. +-- @field #boolean radioOn If true, radio is currently turned on. +-- @field Core.RadioQueue#RADIOQUEUE radioQueue Radio queue. -- @field #number CallsignName Call sign name. -- @field #number CallsignNumber Call sign number. -- @field #boolean EPLRS If true, turn EPLRS data link on. @@ -81,6 +84,10 @@ -- -- @field Ops.Auftrag#AUFTRAG missionpaused Paused mission. -- +-- @field Core.Point#COORDINATE position Current position of the group. +-- @field #number traveldist Distance traveled in meters. This is a lower bound! +-- @field #number fuelmass Inital fuel in kg. +-- -- @extends Core.Fsm#FSM --- *To invent an airplane is nothing. To build one is something. To fly is everything.* -- Otto Lilienthal @@ -213,7 +220,7 @@ FLIGHTGROUP = { inzones = nil, groupinitialized = nil, ishelo = nil, - beacon = nil, + beacon = nil, } @@ -368,7 +375,7 @@ FLIGHTGROUP.version="0.3.8" -- DONE: Get ammo. -- DONE: Get pylons. -- DONE: Fuel threshhold ==> RTB. --- TODO: ROE +-- DONE: ROE -- TODO: Options EPLRS, Afterburner restrict etc. -- TODO: Add TACAN beacon. -- NOGO: Respawn? With correct loadout, fuelstate. Solved in DCS 2.5.6! @@ -812,6 +819,112 @@ function FLIGHTGROUP:SetParkingSpots(airbase, spots) return self end +--- Set TACAN parameters. AA TACANs are aleays on "Y" band. +-- @param #FLIGHTGROUP self +-- @param #number Channel TACAN channel. +-- @param #string Morse Morse code. Default "XX". +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetTACAN(Channel, Morse) + + self.TACANchannel=Channel + self.TACANmorse=Morse or "XXX" + self.TACANactive=false + self.TACANon=true + + return self +end + + +--- Set Radio parameters. +-- @param #FLIGHTGROUP self +-- @param #number Frequency Radio frequency in MHz. Default 305 MHz. +-- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetRadio(Frequency, Modulation) + + self.radioFreq=Frequency or 305 + self.radioModu=Modulation or radio.modulation.AM + + self.radioOn=false + + self.radioUse=true + + return self +end + + + +--- Activate TACAN beacon. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SwitchTACANOn() + + if self:IsAlive() then + + local unit=self.group:GetUnit(1) + + if unit and unit:IsAlive() then + + -- Create a new beacon and activate TACAN. + if not self.beacon then + self.beacon=BEACON:New(unit) + end + + self.beacon:ActivateTACAN(self.TACANchannel, "Y", self.TACANmorse, true) + + self.TACANactive=true + + end + + + end + + return self +end + +--- Deactivate TACAN beacon. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SwitchTACANOff() + + if self.beacon then + self.beacon:StopAATACAN() + end + + self.TACANactive=false + +end + + +--- Turn radio on. +-- @param #FLIGHTGROUP self +-- @param #number Frequency Radio frequency in MHz. Default 305 MHz. +-- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SwitchRadioOn() + + self.group:SetOption(AI.Option.Air.id.SILENCE, false) + + self.group:CommandSetFrequency(self.radioFreq, self.radioModu) + + self.radioOn=true + + return self +end + +--- Turn radio off. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SwitchRadioOff() + + self.group:SetOption(AI.Option.Air.id.SILENCE, true) + + self.radioOn=false + + return self +end + + --- Set the FLIGHTCONTROL controlling this flight group. -- @param #FLIGHTGROUP self -- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol The FLIGHTCONTROL object. @@ -1578,8 +1691,13 @@ function FLIGHTGROUP:onafterFlightStatus(From, Event, To) -- Fuel State --- + local position=self:GetCoordinate() + local dist=self.position:Get3DDistance(position) + self.traveldist=self.traveldist+dist + + -- Only if group is in air. - if self:IsAlive() and self.group:IsAirborne() then + if self:IsAlive() and self.group:IsAirborne(true) then local fuelmin=self:GetFuelMin() @@ -2197,6 +2315,15 @@ function FLIGHTGROUP:onafterFlightSpawned(From, Event, To) self.group:SetOption(AI.Option.Air.id.PROHIBIT_JETT, true) self.group:SetOption(AI.Option.Air.id.PROHIBIT_AB, true) self.group:SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) + + if self.TACANon then + self:SwitchTACANOn() + end + + if self.RadioOn then + + end + --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) @@ -2743,18 +2870,19 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp -- Defaults: SpeedTo=SpeedTo or UTILS.KmphToKnots(self.speedmax*0.75) - SpeedHold=SpeedHold or 250 - SpeedLand=SpeedLand or 170 + SpeedHold=SpeedHold or (self.ishelo and 80 or 250) + SpeedLand=SpeedLand or (self.ishelo and 40 or 170) -- Debug message. local text=string.format("Flight group set to hold at airbase %s. SpeedTo=%d, SpeedHold=%d, SpeedLand=%d", airbase:GetName(), SpeedTo, SpeedHold, SpeedLand) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) self:T(self.lid..text) - + + local althold=self.ishelo and 1000 or 6000 -- Holding points. local c0=self.group:GetCoordinate() - local p0=airbase:GetZone():GetRandomCoordinate():SetAltitude(UTILS.FeetToMeters(6000)) + local p0=airbase:GetZone():GetRandomCoordinate():SetAltitude(UTILS.FeetToMeters(althold)) local p1=nil local wpap=nil @@ -2779,7 +2907,7 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp self.flightcontrol:_AddFlightToInboundQueue(self) end - -- Altitude above ground for a glide slope of 3°. + -- Altitude above ground for a glide slope of 3�. local x1=self.ishelo and UTILS.NMToMeters(5.0) or UTILS.NMToMeters(10) local x2=self.ishelo and UTILS.NMToMeters(2.5) or UTILS.NMToMeters(5) local alpha=math.rad(3) @@ -2876,7 +3004,7 @@ function FLIGHTGROUP:onafterWait(From, Event, To, Coord, Altitude, Speed) end if Tsuspend and not allowed then - self:__WAIT(Tsuspend, Coord, Altitude, Speed) + self:__Wait(Tsuspend, Coord, Altitude, Speed) end return allowed @@ -2894,8 +3022,8 @@ end function FLIGHTGROUP:onafterWait(From, Event, To, Coord, Altitude, Speed) Coord=Coord or self.group:GetCoordinate() - Altitude=Altitude or 10000 - Speed=Speed or 250 + Altitude=Altitude or (self.ishelo and 1000 or 10000) + Speed=Speed or (self.ishelo and 80 or 250) -- Debug message. local text=string.format("Flight group set to wait/orbit at altitude %d m and speed %.1f km/h", Altitude, Speed) @@ -2909,6 +3037,7 @@ function FLIGHTGROUP:onafterWait(From, Event, To, Coord, Altitude, Speed) -- Set task. self:SetTask(TaskOrbit) + end @@ -3866,6 +3995,18 @@ function FLIGHTGROUP:_InitGroup() -- Cruise speed: 70% of max speed but within limit. self.speedCruise=math.min(self.speedmax*0.7, speedCruiseLimit) + -- Initial fuel mass. + self.fuelmass=self.template.pylons.fuel + + self.traveldist=0 + self.position=self:GetCoordinate() + + -- Radio parameters from template. + -- TODO: Need to check that I don't overwrite the settings from :SetRadio() + self.radioOn=self.template.communication + self.radioFreq=self.template.frequency + self.radioModu=self.template.modulation + local unit=self.group:GetUnit(1) if unit then @@ -4848,20 +4989,18 @@ end --- Set parking spot of element. -- @param #FLIGHTGROUP self --- @param #FLIGHTGROUP.FlightElement Element The element. +-- @param #FLIGHTGROUP.Element Element The element. -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Spot Parking Spot. function FLIGHTGROUP:_SetElementParkingAt(Element, Spot) -- Element is parking here. Element.parking=Spot - if Spot then - if self.flightcontrol then + if Spot and self.flightcontrol then - -- Set parking spot to OCCUPIED. - self.flightcontrol:SetParkingOccupied(Element.parking, Element.name) - - end + -- Set parking spot to OCCUPIED. + self.flightcontrol:SetParkingOccupied(Element.parking, Element.name) + end end @@ -4874,7 +5013,7 @@ function FLIGHTGROUP:_SetElementParkingFree(Element) if Element.parking then -- Set parking to FREE. - if self.flightcontrol and Element.parking then + if self.flightcontrol then self.flightcontrol:SetParkingFree(Element.parking) end diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 76ccd1111..5a1a1c2e5 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -36,6 +36,9 @@ -- @field #string attribute Generalized attribute of the squadron template group. -- @field #number tankerSystem For tanker squads, the refuel system used (boom=0 or probpe=1). Default nil. -- @field #number refuelSystem For refuelable squads, the refuel system used (boom=0 or probpe=1). Default nil. +-- @field #number TACANmin TACAN min channel. +-- @field #number TACANmax TACAN max channel. +-- @field #table TACANused Table of used TACAN channels. -- @extends Core.Fsm#FSM --- Be surprised! @@ -68,6 +71,9 @@ SQUADRON = { engageRange = nil, tankerSystem = nil, refuelSystem = nil, + TACANmin = nil, + TACANmax = nil, + TACANused = {}, } --- SQUADRON class version. @@ -361,6 +367,35 @@ function SQUADRON:GetModex(Asset) end +--- Get an unused TACAN channel. +-- @param #SQUADRON self +-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. +-- @return #number TACAN channel or *nil* if no channel is free. +function SQUADRON:GetTACAN() + + if self.TACANmin and self.TACANmax then + + for channel=self.TACANmin, self.TACANmax do + + if not self.TACANused[channel] then + self.TACANused[channel]=true + return channel + end + + end + + end + + return nil +end + +--- "Return" a used TACAN channel. +-- @param #SQUADRON self +-- @param #number channel The channel that is available again. +function SQUADRON:ReturnTACAN(channel) + self.TACANused[channel]=false +end + --- Check if squadron is "OnDuty". -- @param #SQUADRON self -- @return #boolean If true, squdron is in state "OnDuty". diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index b12cc4095..b5b424f51 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -785,13 +785,39 @@ function CONTROLLABLE:CommandEPLRS(SwitchOnOff, Delay) if Delay and Delay>0 then SCHEDULER:New(nil, self.CommandEPLRS, {self, SwitchOnOff}, Delay) else - self:T(string.format("EPLRS=%s for controllable %s (id=%s)", tostring(SwitchOnOff), tostring(self:GetName()), tostring(_id))) + self:T(string.format("EPLRS=%s for controllable %s (id=%s)", tostring(SwitchOnOff), tostring(self:GetName()), tostring(self:GetID()))) self:SetCommand(CommandEPLRS) end return self end +--- Set radio frequency. See [DCS command EPLRS](https://wiki.hoggitworld.com/view/DCS_command_setFrequency) +-- @param #CONTROLLABLE self +-- @param #number Frequency Radio frequency in MHz. +-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`. +-- @param #number Delay (Optional) Delay in seconds before the frequncy is set. Default is immediately. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay) + + local CommandSetFrequency = { + id = 'SetFrequency', + params = { + frequency = Frequency, + modulation = Modulation or radio.modulation.AM, + } + } + + if Delay and Delay>0 then + SCHEDULER:New(nil, self.CommandSetFrequency, {self, Frequency, Modulation}, Delay) + else + self:SetCommand(CommandSetFrequency) + end + + return self +end + + --- Set EPLRS data link on/off. -- @param #CONTROLLABLE self -- @param #boolean SwitchOnOff If true (or nil) switch EPLRS on. If false switch off. @@ -1034,9 +1060,9 @@ end --- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. -- @param #CONTROLLABLE self -- @param DCS#Vec2 Vec2 The point where to wait. Needs to have x and y components. --- @param Core.Set#SET_GROUP GroupSetForEmparking Set of groups to embark. +-- @param Core.Set#SET_GROUP GroupSetForEmbarking Set of groups to embark. -- @param #number Duration (Optional) The maximum duration in seconds to wait until all groups have embarked. --- @param Core.Set#SET_GROUP (Optional) DistributionGroupSet Set of groups identifying the groups needing to board specific helicopters. +-- @param Core.Set#SET_GROUP DistributionGroupSet (Optional) Set of groups identifying the groups needing to board specific helicopters. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskEmbarking(Vec2, GroupSetForEmbarking, Duration, DistributionGroupSet)