This commit is contained in:
funkyfranky 2017-10-24 23:03:57 +02:00
parent 70e5ce30bb
commit f4a0b83619

View File

@ -88,11 +88,9 @@
-- @field #table airports All airports of friedly coalitions. -- @field #table airports All airports of friedly coalitions.
-- @field #boolean random_departure By default a random friendly airport is chosen as departure. -- @field #boolean random_departure By default a random friendly airport is chosen as departure.
-- @field #boolean random_destination By default a random friendly airport is chosen as destination. -- @field #boolean random_destination By default a random friendly airport is chosen as destination.
-- @field #table departure_zones Array containing the names of the departure zones.
-- @field #table departure_ports Array containing the names of the destination airports. -- @field #table departure_ports Array containing the names of the destination airports.
-- @field #table destination_ports Array containing the names of the destination airports. -- @field #table destination_ports Array containing the names of the destination airports.
-- @field #table excluded_ports Array containing the names of explicitly excluded airports. -- @field #table excluded_ports Array containing the names of explicitly excluded airports.
-- @field #table destination_zones Array containing the names of the destination zones.
-- @field #boolean destinationzone Destination is a zone and not an airport. -- @field #boolean destinationzone Destination is a zone and not an airport.
-- @field #table return_zones Array containing the names of the return zones. -- @field #table return_zones Array containing the names of the return zones.
-- @field #boolean returnzone Zone where aircraft will fly to before returning to their departure airport. -- @field #boolean returnzone Zone where aircraft will fly to before returning to their departure airport.
@ -303,10 +301,8 @@ RAT={
airports={}, -- All airports of friedly coalitions. airports={}, -- All airports of friedly coalitions.
random_departure=true, -- By default a random friendly airport is chosen as departure. random_departure=true, -- By default a random friendly airport is chosen as departure.
random_destination=true, -- By default a random friendly airport is chosen as destination. random_destination=true, -- By default a random friendly airport is chosen as destination.
departure_zones={}, -- Array containing the names of the departure zones.
departure_ports={}, -- Array containing the names of the departure airports. departure_ports={}, -- Array containing the names of the departure airports.
destination_ports={}, -- Array containing the names of the destination airports. destination_ports={}, -- Array containing the names of the destination airports.
destination_zones={}, -- Array containing the names of destination zones.
destinationzone=false, -- Destination is a zone and not an airport. destinationzone=false, -- Destination is a zone and not an airport.
return_zones={}, -- Array containing the names of return zones. return_zones={}, -- Array containing the names of return zones.
returnzone=false, -- Aircraft will fly to a zone and back. returnzone=false, -- Aircraft will fly to a zone and back.
@ -391,7 +387,7 @@ RAT.status={
EventBirth="Ready and starting engines", EventBirth="Ready and starting engines",
EventEngineStartAir="Started engines (in air)", EventEngineStartAir="Started engines (in air)",
EventEngineStart="Started engines and taxiing", EventEngineStart="Started engines and taxiing",
EventTakeoff="Airborn after take-off", EventTakeoff="Airborne after take-off",
EventLand="Landed and taxiing", EventLand="Landed and taxiing",
EventEngineShutdown="Engines off", EventEngineShutdown="Engines off",
EventDead="Dead", EventDead="Dead",
@ -487,6 +483,8 @@ RAT.id="RAT | "
--DONE: Handle the case where more than 10 RAT objects are spawned. Likewise, more than 10 groups of one object. Causes problems with the number of menu items! ==> not now! --DONE: Handle the case where more than 10 RAT objects are spawned. Likewise, more than 10 groups of one object. Causes problems with the number of menu items! ==> not now!
--DONE: Add custom livery choice if possible. --DONE: Add custom livery choice if possible.
--TODO: When only a destination is set, it should be checked that the departure is within range. Also, that departure and destination are not the same. --TODO: When only a destination is set, it should be checked that the departure is within range. Also, that departure and destination are not the same.
--TODO: Add function to include all airports to selected destinations/departures.
--TODO: Find way to respawn aircraft at same position where the last was despawned for commute and journey.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -517,7 +515,7 @@ function RAT:New(groupname, alias)
-- Check the group actually exists. -- Check the group actually exists.
if DCSgroup==nil then if DCSgroup==nil then
env.error("Group with name "..groupname.." does not exist in the mission editor!") env.error(RAT.id.."Group with name "..groupname.." does not exist in the mission editor!")
return nil return nil
end end
@ -593,25 +591,11 @@ function RAT:Spawn(naircraft)
env.error(RAT.id.."Destination zone _and_ return to zone not possible! Disabling return to zone.") env.error(RAT.id.."Destination zone _and_ return to zone not possible! Disabling return to zone.")
self.returnzone=false self.returnzone=false
end end
-- If returning to a zone, we set the landing type to the takeoff type. Default landing is ground. -- If returning to a zone, we set the landing type to "air" if takeoff is in air.
-- But if we start in air we want to end in air. -- Because if we start in air we want to end in air. But default landing is ground.
if self.returnzone and self.takeoff==RAT.wp.air then if self.returnzone and self.takeoff==RAT.wp.air then
self.landing=self.takeoff self.landing=RAT.wp.air
end end
-- Return zone and commute does not make sense.
-- TODO: Actually it makes sense if the departure airport should stay the same.
-- We could achive the same by setting only one departure or destination but this is generally not the case!
if self.returnzone and self.continuejourney then
--env.error(RAT.id.."Combination return zone _and_ commute does not make sense! Disabling commute.")
--self.commute=false
end
-- Return zone and commute does not make sense.
if self.returnzone and self.commute then
-- env.error(RAT.id.."Combination return zone _and_ commute does not make sense! Disabling commute.")
-- self.commute=false
end
-- Settings info -- Settings info
local text=string.format("\n******************************************************\n") local text=string.format("\n******************************************************\n")
@ -653,6 +637,12 @@ function RAT:Spawn(naircraft)
text=text..string.format("Radio comms : %s\n", tostring(self.radio)) text=text..string.format("Radio comms : %s\n", tostring(self.radio))
text=text..string.format("Radio frequency : %s\n", tostring(self.frequency)) text=text..string.format("Radio frequency : %s\n", tostring(self.frequency))
text=text..string.format("Radio modulation : %s\n", tostring(self.frequency)) text=text..string.format("Radio modulation : %s\n", tostring(self.frequency))
if self.livery then
text=text..string.format("Available liveries:\n")
for _,livery in pairs(self.livery) do
text=text..string.format("- %s\n", livery)
end
end
text=text..string.format("******************************************************\n") text=text..string.format("******************************************************\n")
env.info(RAT.id..text) env.info(RAT.id..text)
@ -778,7 +768,7 @@ function RAT:SetDeparture(departurenames)
names={departurenames} names={departurenames}
else else
-- error message -- error message
env.error("Input parameter must be a string or a table!") env.error(RAT.id.."Input parameter must be a string or a table in SetDeparture()!")
end end
-- Put names into arrays. -- Put names into arrays.
@ -789,7 +779,7 @@ function RAT:SetDeparture(departurenames)
table.insert(self.departure_ports, name) table.insert(self.departure_ports, name)
elseif self:_ZoneExists(name) then elseif self:_ZoneExists(name) then
-- If it is not an airport, we assume it is a zone. -- If it is not an airport, we assume it is a zone.
table.insert(self.departure_zones, name) table.insert(self.departure_ports, name)
else else
env.error(RAT.id.."ERROR! No departure airport or zone found with name "..name) env.error(RAT.id.."ERROR! No departure airport or zone found with name "..name)
end end
@ -815,7 +805,7 @@ function RAT:SetDestination(destinationnames)
names={destinationnames} names={destinationnames}
else else
-- Error message. -- Error message.
env.error("Input parameter must be a string or a table!") env.error(RAT.id.."Input parameter must be a string or a table in SetDestination()!")
end end
-- Put names into arrays. -- Put names into arrays.
@ -838,52 +828,41 @@ end
--- Destinations are treated as zones. Aircraft will not land but rather be despawned when they reach a random point in the zone. --- Destinations are treated as zones. Aircraft will not land but rather be despawned when they reach a random point in the zone.
-- @param #RAT self -- @param #RAT self
function RAT:DestinationZone() function RAT:DestinationZone()
-- Random destination is deactivated now that user specified destination zone(s).
self.random_destination=false
-- Destination is a zone. Needs special care. -- Destination is a zone. Needs special care.
self.destinationzone=true self.destinationzone=true
-- Landing type is "air" because we don't actually land at the airport -- Landing type is "air" because we don't actually land at the airport.
self.landing=RAT.wp.air self.landing=RAT.wp.air
-- No ATC required.
self.ATCswitch=false
end end
--- Aircraft will fly to a random point within a zone and then return to its departure airport or zone. --- Aircraft will fly to a random point within a zone and then return to its departure airport or zone.
-- @param #RAT self -- @param #RAT self
function RAT:ReturnZone() function RAT:ReturnZone()
-- Random destination is deactivated now that user specified destination zone(s).
self.random_destination=false
-- Destination is a zone. Needs special care. -- Destination is a zone. Needs special care.
self.returnzone=true self.returnzone=true
end end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the airports lie. -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
function RAT:SetDestinationsFromZone(zone) function RAT:SetDestinationsFromZone(zone)
-- Random departure is deactivated now that user specified departure ports. -- Random departure is deactivated now that user specified departure ports.
self.random_destination=false self.random_destination=false
-- Set zone.
self.destination_Azone=zone self.destination_Azone=zone
end end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the airports lie. -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
function RAT:SetDeparturesFromZone(zone) function RAT:SetDeparturesFromZone(zone)
-- Random departure is deactivated now that user specified departure ports. -- Random departure is deactivated now that user specified departure ports.
self.random_departure=false self.random_departure=false
-- Set zone.
self.departure_Azone=zone self.departure_Azone=zone
end end
@ -1219,8 +1198,8 @@ function RAT:_InitAircraft(DCSgroup)
-- operational range in NM converted to m -- operational range in NM converted to m
self.aircraft.Rmax = DCSdesc.range*RAT.unit.nm2m self.aircraft.Rmax = DCSdesc.range*RAT.unit.nm2m
-- effective range taking fuel into accound and a 10% reserve -- effective range taking fuel into accound and a 5% reserve
self.aircraft.Reff = self.aircraft.Rmax*self.aircraft.fuel*0.9 self.aircraft.Reff = self.aircraft.Rmax*self.aircraft.fuel*0.95
-- max airspeed from group -- max airspeed from group
self.aircraft.Vmax = DCSdesc.speedMax self.aircraft.Vmax = DCSdesc.speedMax
@ -1251,7 +1230,7 @@ function RAT:_InitAircraft(DCSgroup)
text=text..string.format("Max climb speed = %6.1f m/s\n", self.aircraft.Vymax) text=text..string.format("Max climb speed = %6.1f m/s\n", self.aircraft.Vymax)
text=text..string.format("Initial Fuel = %6.1f\n", self.aircraft.fuel*100) text=text..string.format("Initial Fuel = %6.1f\n", self.aircraft.fuel*100)
text=text..string.format("Max range = %6.1f km\n", self.aircraft.Rmax/1000) text=text..string.format("Max range = %6.1f km\n", self.aircraft.Rmax/1000)
text=text..string.format("Eff range = %6.1f km\n", self.aircraft.Reff/1000) text=text..string.format("Eff range = %6.1f km (including initial fuel amount)\n", self.aircraft.Reff/1000)
text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n", self.aircraft.ceiling/1000, self.aircraft.ceiling/RAT.unit.FL2m) text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n", self.aircraft.ceiling/1000, self.aircraft.ceiling/RAT.unit.FL2m)
text=text..string.format("FL cruise = %6.1f km = FL%3.0f\n", self.aircraft.FLcruise/1000, self.aircraft.FLcruise/RAT.unit.FL2m) text=text..string.format("FL cruise = %6.1f km = FL%3.0f\n", self.aircraft.FLcruise/1000, self.aircraft.FLcruise/RAT.unit.FL2m)
text=text..string.format("******************************************************\n") text=text..string.format("******************************************************\n")
@ -1270,7 +1249,7 @@ end
-- @param #string _departure (Optional) Name of departure airbase. -- @param #string _departure (Optional) Name of departure airbase.
-- @param #string _destination (Optional) Name of destination airbase. -- @param #string _destination (Optional) Name of destination airbase.
-- @param #number _takeoff Takeoff type id. -- @param #number _takeoff Takeoff type id.
function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing) function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint)
-- Set takeoff type. -- Set takeoff type.
local takeoff=self.takeoff local takeoff=self.takeoff
@ -1291,18 +1270,40 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing)
end end
-- Set flight plan. -- Set flight plan.
local departure, destination, waypoints = self:_SetRoute(takeoff, landing, _departure, _destination) local departure, destination, waypoints = self:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
-- Return nil if we could not find a departure destination or waypoints -- Return nil if we could not find a departure destination or waypoints
if not (departure and destination and waypoints) then if not (departure and destination and waypoints) then
return nil return nil
end end
-- Set (another) livery.
local livery
if _livery then
-- Take livery from previous flight (continue journey).
livery=_livery
elseif self.livery then
-- Choose random livery.
livery=self.livery[math.random(#self.livery)]
local text=string.format("Chosen livery for group %s: %s", self:_AnticipatedGroupName(), livery)
env.info(RAT.id..text)
else
livery=nil
end
-- Use last waypoint of previous flight as initial wp for this one.
if _waypoint and takeoff==RAT.wp.air and (self.continuejourney or self.commute) then
-- If the other way does not work, we can still try this.
--waypoints[1]=_waypoint
end
-- Modify the spawn template to follow the flight plan. -- Modify the spawn template to follow the flight plan.
self:_ModifySpawnTemplate(waypoints) self:_ModifySpawnTemplate(waypoints, livery)
-- Actually spawn the group. -- Actually spawn the group.
local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP
-- Increase group counter.
self.alive=self.alive+1 self.alive=self.alive+1
-- ATC is monitoring this flight (if it supposed to land). -- ATC is monitoring this flight (if it supposed to land).
@ -1347,6 +1348,9 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing)
self.ratcraft[self.SpawnIndex].takeoff=takeoff self.ratcraft[self.SpawnIndex].takeoff=takeoff
self.ratcraft[self.SpawnIndex].landing=landing self.ratcraft[self.SpawnIndex].landing=landing
-- Livery
self.ratcraft[self.SpawnIndex].livery=livery
-- If this switch is set to true, the aircraft will be despawned the next time the status function is called. -- If this switch is set to true, the aircraft will be despawned the next time the status function is called.
self.ratcraft[self.SpawnIndex].despawnme=false self.ratcraft[self.SpawnIndex].despawnme=false
@ -1399,36 +1403,49 @@ function RAT:_Respawn(group)
local destination=self.ratcraft[index].destination local destination=self.ratcraft[index].destination
local takeoff=self.ratcraft[index].takeoff local takeoff=self.ratcraft[index].takeoff
local landing=self.ratcraft[index].landing local landing=self.ratcraft[index].landing
local livery=self.ratcraft[index].livery
local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints]
local _departure=nil local _departure=nil
local _destination=nil local _destination=nil
local _takeoff=nil local _takeoff=nil
local _landing=nil local _landing=nil
local _livery=nil
local _lastwp=nil
if self.continuejourney then if self.continuejourney then
-- We continue our journey from the old departure airport. -- We continue our journey from the old departure airport.
_departure=destination:GetName() _departure=destination:GetName()
-- Use the same livery for next aircraft.
_livery=livery
if self.destinationzone then if self.destinationzone then
-- Case: X --> Zone --> Zone --> Zone -- Case: X --> Zone --> Zone --> Zone
_takeoff=RAT.wp.air _takeoff=RAT.wp.air
_landing=RAT.wp.air
-- We should also take case that the destination is set correctly
elseif self.returnzone then elseif self.returnzone then
-- Case: X --> Zone --> X, X --> Zone --> X -- Case: X --> Zone --> X, X --> Zone --> X
-- We flew to a zone and back. Takeoff type does not -- We flew to a zone and back. Takeoff type does not change.
_takeoff=self.takeoff _takeoff=self.takeoff
_landing=self.takeoff
-- If we took of in air we also want to land "in air".
if self.takeoff==RAT.wp.air then
_landing=RAT.wp.air
else
_landing=RAT.wp.landing
end
-- Departure stays the same. -- Departure stays the same. (The destination is the zone here.)
_departure=departure:GetName() _departure=departure:GetName()
else else
-- Default case. Takeoff and landing type does not change.
_takeoff=self.takeoff _takeoff=self.takeoff
_landing=self.landing _landing=self.landing
@ -1440,6 +1457,9 @@ function RAT:_Respawn(group)
_departure=destination:GetName() _departure=destination:GetName()
_destination=departure:GetName() _destination=departure:GetName()
-- Use the same livery for next aircraft.
_livery=livery
-- Handle takeoff type. -- Handle takeoff type.
if self.destinationzone then if self.destinationzone then
-- self.takeoff is either RAT.wp.air or RAT.wp.cold -- self.takeoff is either RAT.wp.air or RAT.wp.cold
@ -1448,8 +1468,8 @@ function RAT:_Respawn(group)
if self.takeoff==RAT.wp.air then if self.takeoff==RAT.wp.air then
-- Case: Zone <--> Zone (both have takeoff air) -- Case: Zone <--> Zone (both have takeoff air)
_takeoff=RAT.wp.air -- = self.takeoff _takeoff=RAT.wp.air -- = self.takeoff (because we just checked)
_landing=RAT.wp.air -- = self.landing _landing=RAT.wp.air -- = self.landing (because destinationzone)
else else
@ -1457,9 +1477,9 @@ function RAT:_Respawn(group)
if takeoff==RAT.wp.air then if takeoff==RAT.wp.air then
-- Last takeoff was air so we are at the airport now, takeoff is from ground. -- Last takeoff was air so we are at the airport now, takeoff is from ground.
_takeoff=self.takeoff -- must be either hot/cold/runway/hotcold _takeoff=self.takeoff -- must be either hot/cold/runway/hotcold
_landing=self.landing -- must be air _landing=RAT.wp.air -- must be air = self.landing (because destinationzone)
else else
-- Last takeoff was on ground so we are at a zone now ==> takeoff in air, landing at zone. -- Last takeoff was on ground so we are at a zone now ==> takeoff in air, landing at airport.
_takeoff=RAT.wp.air _takeoff=RAT.wp.air
_landing=RAT.wp.landing _landing=RAT.wp.landing
end end
@ -1480,14 +1500,19 @@ function RAT:_Respawn(group)
end end
env.info(RAT.id..string.format("self.takeoff, takeoff, _takeoff = %d, %d, %d", self.takeoff, takeoff, _takeoff)) -- Take the last waypoint as initial waypoint for next plane.
env.info(RAT.id..string.format("self.landing, landing, _landing = %d, %d, %d", self.landing, landing, _landing)) if _takeoff==RAT.wp.air and (self.continuejourney or self.commute) then
_lastwp=lastwp
end
env.info(RAT.id..string.format("self.takeoff, takeoff, _takeoff = %s, %s, %s", tostring(self.takeoff), tostring(takeoff), tostring(_takeoff)))
env.info(RAT.id..string.format("self.landing, landing, _landing = %s, %s, %s", tostring(self.landing), tostring(landing), tostring(_landing)))
-- Spawn new group. -- Spawn new group.
if self.respawn_delay then if self.respawn_delay then
SCHEDULER:New(nil, self._SpawnWithRoute, {self, _departure, _destination, _takeoff, _landing}, self.respawn_delay) SCHEDULER:New(nil, self._SpawnWithRoute, {self, _departure, _destination, _takeoff, _landing, _livery, _lastwp}, self.respawn_delay)
else else
self:_SpawnWithRoute(_departure, _destination, _takeoff, _landing) self:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _lastwp)
end end
end end
@ -1504,7 +1529,7 @@ end
-- @return Wrapper.Airport#AIRBASE Destination airbase. -- @return Wrapper.Airport#AIRBASE Destination airbase.
-- @return #table Table of flight plan waypoints. -- @return #table Table of flight plan waypoints.
-- @return #nil If no valid departure or destination airport could be found. -- @return #nil If no valid departure or destination airport could be found.
function RAT:_SetRoute(takeoff, landing, _departure, _destination) function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
-- Max cruise speed. -- Max cruise speed.
local VxCruiseMax local VxCruiseMax
@ -1543,9 +1568,6 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-- Descent angle in rad. -- Descent angle in rad.
local AlphaDescent=math.rad(self.AlphaDescent) local AlphaDescent=math.rad(self.AlphaDescent)
local returnzone=self.returnzone
local destinationzone=self.destinationzone
-- DEPARTURE AIRPORT -- DEPARTURE AIRPORT
-- Departure airport or zone. -- Departure airport or zone.
@ -1582,9 +1604,14 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-- Coordinates of departure point. -- Coordinates of departure point.
local Pdeparture local Pdeparture
if takeoff==RAT.wp.air then if takeoff==RAT.wp.air then
-- For an air start, we take a random point within the spawn zone. if _waypoint then
local vec2=departure:GetRandomVec2() -- Use coordinates of previous flight (commute or journey).
Pdeparture=COORDINATE:NewFromVec2(vec2) Pdeparture=COORDINATE:New(_waypoint.x, _waypoint.alt, _waypoint.y)
else
-- For an air start, we take a random point within the spawn zone.
local vec2=departure:GetRandomVec2()
Pdeparture=COORDINATE:NewFromVec2(vec2)
end
else else
Pdeparture=departure:GetCoordinate() Pdeparture=departure:GetCoordinate()
end end
@ -1600,14 +1627,39 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
Hmin=50 Hmin=50
end end
H_departure=self:_Randomize(self.aircraft.FLcruise*0.7, 0.3, Pdeparture.y+Hmin, self.aircraft.FLcruise) H_departure=self:_Randomize(self.aircraft.FLcruise*0.7, 0.3, Pdeparture.y+Hmin, self.aircraft.FLcruise)
-- Use alt of last flight.
if _waypoint then
H_departure=_waypoint.alt
end
else else
H_departure=Pdeparture.y H_departure=Pdeparture.y
end end
-- Adjust min distance between departure and destination for user set min flight level. -- Adjust min distance between departure and destination for user set min flight level.
local mindist=self.mindist
if self.FLminuser then if self.FLminuser then
self.mindist=self:_MinDistance(AlphaClimb, AlphaDescent, self.FLminuser-H_departure)
local text=string.format("Adjusting min distance to %d km (for given min FL%03d)", self.mindist/1000, self.FLminuser/RAT.unit.FL2m) -- We can conly consider the symmetric case, because no destination selected yet.
local hclimb=self.FLminuser-H_departure
local hdescent=self.FLminuser-H_departure
-- Minimum distance for l
local Dclimb, Ddescent, Dtot=self:_MinDistance(AlphaClimb, AlphaDescent, hclimb, hdescent)
if takeoff==RAT.wp.air and landing==RAT.wpair then
mindist=0 -- Takeoff and landing are in air. No mindist required.
elseif takeoff==RAT.wp.air then
mindist=Ddescent -- Takeoff in air. Need only space to descent.
elseif landing==RAT.wp.air then
mindist=Dclimb -- Landing "in air". Need only space to climb.
else
mindist=Dtot -- Takeoff and landing on ground. Need both space to climb and descent.
end
-- Mindist is at least self.mindist.
mindist=math.max(self.mindist, mindist)
local text=string.format("Adjusting min distance to %d km (for given min FL%03d)", mindist/1000, self.FLminuser/RAT.unit.FL2m)
env.info(RAT.id..text) env.info(RAT.id..text)
end end
@ -1640,18 +1692,15 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
end end
-- In case of a returnzone the destination (i.e. return point) is always a zone. -- In case of a returnzone the destination (i.e. return point) is always a zone.
local mylanding local mylanding=landing
local acrange=self.aircraft.Reff
if self.returnzone then if self.returnzone then
mylanding=RAT.wp.air mylanding=RAT.wp.air
else acrange=self.aircraft.Reff/2 -- Aircraft needs to go to zone and back home.
mylanding=landing
end end
-- Get all destination airports within reach.
local destinations=self:_GetDestinations(departure, Pdeparture, self.mindist, math.min(self.aircraft.Reff, self.maxdist), random, mylanding)
-- Pick a destination airport. -- Pick a destination airport.
destination=self:_PickDestination(destinations) destination=self:_PickDestination(departure, Pdeparture, mindist, math.min(acrange, self.maxdist), random, mylanding)
end end
-- Return nil if no departure could be found. -- Return nil if no departure could be found.
@ -1669,27 +1718,6 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
env.error(RAT.id..text) env.error(RAT.id..text)
end end
--[[
-- Coordinates of destination airport.
local Pdestination
local Preturn
if self.destinationzone then
-- Destination is a zone and we pick a random point within the zone.
local vec2=destination:GetRandomVec2()
Pdestination=COORDINATE:NewFromVec2(vec2)
elseif self.returnzone then
-- We fly to a random point within a zone and back to the departure airport.
Pdestination=departure:GetCoordinate()
-- Get a random point inside zone return zone.
local vec2=destination:GetRandomVec2()
Preturn=COORDINATE:NewFromVec2(vec2)
-- Set departure to destination.
destination=departure
else
Pdestination=destination:GetCoordinate()
end
]]
-- Get a random point inside zone return zone. -- Get a random point inside zone return zone.
local Preturn local Preturn
local destination_returnzone local destination_returnzone
@ -1764,12 +1792,11 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
d_total=Pdeparture:Get2DDistance(Pholding) d_total=Pdeparture:Get2DDistance(Pholding)
end end
-- Max height if we only would descent to holding point for the given distance. -- Max height in case of air start, i.e. if we only would descent to holding point for the given distance.
-- TODO: Add case for destination zone. We could allow a higher max because no descent is necessary.
if takeoff==RAT.wp.air then if takeoff==RAT.wp.air then
local H_departure_max local H_departure_max
if landing==RAT.wp.air then if landing==RAT.wp.air then
H_departure_max = H_departure H_departure_max = H_departure -- If we fly to a zone, there is no descent necessary.
else else
H_departure_max = d_total * math.tan(AlphaDescent) + H_holding + h_holding H_departure_max = d_total * math.tan(AlphaDescent) + H_holding + h_holding
end end
@ -1779,7 +1806,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-------------------------------------------- --------------------------------------------
-- Height difference between departure and destination. -- Height difference between departure and destination.
local deltaH=math.abs(H_departure-h_holding-H_holding) local deltaH=math.abs(H_departure-(h_holding+H_holding))
-- Slope between departure and destination. -- Slope between departure and destination.
local phi = math.atan(deltaH/d_total) local phi = math.atan(deltaH/d_total)
@ -1839,7 +1866,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-- If the route is very short we set FLmin a bit lower than FLmax. -- If the route is very short we set FLmin a bit lower than FLmax.
if FLmin>FLmax then if FLmin>FLmax then
FLmin=FLmax*0.75 FLmin=FLmax*0.90
end end
-- For helicopters we take cruise alt between 50 to 1000 meters above ground. Default cruise alt is ~150 m. -- For helicopters we take cruise alt between 50 to 1000 meters above ground. Default cruise alt is ~150 m.
@ -1853,26 +1880,30 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-- Overrule setting if user specified min/max flight level explicitly. -- Overrule setting if user specified min/max flight level explicitly.
if self.FLminuser then if self.FLminuser then
FLmin=self.FLminuser FLmin=math.max(self.FLminuser, FLmin) -- Still take care that we dont fly too high.
end end
if self.FLmaxuser then if self.FLmaxuser then
FLmax=self.FLmaxuser FLmax=math.min(self.FLmaxuser, FLmax) -- Still take care that we dont fly too low.
end end
-- Adjust FLcruise to be at leat FLmin and at most FLmax -- Expected cruise altitude - peak of gaussian distribution.
if self.aircraft.FLcruise<FLmin then local FLcruise_expect=self.aircraft.FLcruise
self.aircraft.FLcruise=FLmin if FLcruise_expect<FLmin then
FLcruise_expect=FLmin
end end
if self.aircraft.FLcruise>FLmax then if FLcruise_expect>FLmax then
self.aircraft.FLcruise=FLmax FLcruise_expect=FLmax
end end
-- Set cruise altitude. Selected from Gaussian distribution but limited to FLmin and FLmax. -- Set cruise altitude. Selected from Gaussian distribution but limited to FLmin and FLmax.
local FLcruise=self:_Random_Gaussian(self.aircraft.FLcruise, (FLmax-FLmin)/4, FLmin, FLmax) local FLcruise=self:_Random_Gaussian(FLcruise_expect, (FLmax-FLmin)/4, FLmin, FLmax)
-- Overrule setting if user specified a flight level explicitly. -- Overrule setting if user specified a flight level explicitly.
if self.FLuser then if self.FLuser then
FLcruise=self.FLuser FLcruise=self.FLuser
-- Still cruise alt should be with parameters!
FLcruise=math.max(FLcruise, FLmin)
FLcruise=math.min(FLcruise, FLmax)
end end
-- Climb and descent heights. -- Climb and descent heights.
@ -1884,7 +1915,6 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
local d_descent = h_descent/math.tan(AlphaDescent) local d_descent = h_descent/math.tan(AlphaDescent)
local d_cruise = d_total-d_climb-d_descent local d_cruise = d_total-d_climb-d_descent
local d_total_cruise = d_climb + d_cruise + d_descent local d_total_cruise = d_climb + d_cruise + d_descent
--------------------------------------------
-- debug message -- debug message
local text=string.format("\n******************************************************\n") local text=string.format("\n******************************************************\n")
@ -2079,7 +2109,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination)
-- Some info on the route. -- Some info on the route.
self:_Routeinfo(waypoints, "Waypoint info in set_route:") self:_Routeinfo(waypoints, "Waypoint info in set_route:")
-- return departure, destination and waypoints -- Return departure, destination and waypoints.
if self.returnzone then if self.returnzone then
-- We return the actual zone here because returning the departure leads to problems with commute. -- We return the actual zone here because returning the departure leads to problems with commute.
return departure, destination_returnzone, waypoints return departure, destination_returnzone, waypoints
@ -2101,58 +2131,57 @@ function RAT:_PickDeparture(takeoff)
-- Array of possible departure airports or zones. -- Array of possible departure airports or zones.
local departures={} local departures={}
if self.random_departure then
if takeoff==RAT.wp.air then -- Airports of friendly coalitions.
for _,airport in pairs(self.airports) do
if self.random_departure then
-- Air start above a random airport. local name=airport:GetName()
for _,airport in pairs(self.airports) do if not self:_Excluded(name) then
if not self:_Excluded(airport:GetName()) then if takeoff==RAT.wp.air then
table.insert(departures, airport:GetZone()) table.insert(departures, airport:GetZone()) -- insert zone object.
end else
end table.insert(departures, airport) -- insert airport object.
else
-- Put all specified zones in table.
for _,name in pairs(self.departure_zones) do
if not self:_Excluded(name) then
table.insert(departures, ZONE:New(name))
end
end
-- Put all specified airport zones in table.
for _,name in pairs(self.departure_ports) do
if not self:_Excluded(name) then
table.insert(departures, AIRBASE:FindByName(name):GetZone())
end end
end end
end end
else else
if self.random_departure then -- Destination airports or zones specified by user.
for _,name in pairs(self.departure_ports) do
-- All friendly departure airports.
for _,airport in pairs(self.airports) do local dep=nil
if not self:_Excluded(airport:GetName()) then if self:_AirportExists(name) then
table.insert(departures, airport) if takeoff==RAT.wp.air then
dep=AIRBASE:FindByName(name):GetZone()
else
dep=AIRBASE:FindByName(name)
end end
elseif self:_ZoneExists(name) then
if takeoff==RAT.wp.air then
dep=ZONE:New(name)
else
env.error(RAT.id.."Takeoff is not in air. Cannot use "..name.." as departure!")
end
else
env.error(RAT.id.."No airport or zone found with name "..name)
end end
else -- Add to departures table.
if dep then
-- All airports specified by user table.insert(departures, dep)
for _,name in pairs(self.departure_ports) do
if self:_IsFriendly(name) and not self:_Excluded(name) then
table.insert(departures, AIRBASE:FindByName(name))
end
end end
end end
end end
-- Info message.
env.info(RAT.id.."Number of possible departures = "..#departures)
-- Select departure airport or zone. -- Select departure airport or zone.
local departure=departures[math.random(#departures)] local departure=departures[math.random(#departures)]
@ -2168,63 +2197,31 @@ function RAT:_PickDeparture(takeoff)
MESSAGE:New(text, 30):ToAll() MESSAGE:New(text, 30):ToAll()
end end
else else
env.error(RAT.id.."No departure airport or zone found.")
departure=nil departure=nil
end end
return departure return departure
end end
--- Pick destination airport or zone depending on departure position.
--- Pick destination airport. If no airport name is given an airport from the coalition is chosen randomly.
-- @param #RAT self
-- @param #table destinations Table with destination airports.
-- @return Wrapper.Airbase#AIRBASE Destination airport.
function RAT:_PickDestination(destinations)
-- Randomly select one possible destination.
local destination=nil
if destinations and #destinations>0 then
-- Random selection.
destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE
-- Debug message.
local text
if self.destinationzone or self.returnzone then
text="Chosen destination zone: "..destination:GetName()
else
text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")"
end
env.info(RAT.id..text)
if self.debug then
MESSAGE:New(text, 30):ToAll()
end
else
env.error(RAT.id.."No destination airport found.")
end
return destination
end
--- Get all possible destination airports depending on departure position.
-- The list is sorted w.r.t. distance to departure position.
-- @param #RAT self -- @param #RAT self
-- @param Wrapper.Airbase#AIRBASE departure Departure airport or zone. -- @param Wrapper.Airbase#AIRBASE departure Departure airport or zone.
-- @param Core.Point#COORDINATE q Coordinate of the departure point. -- @param Core.Point#COORDINATE q Coordinate of the departure point.
-- @param #number minrange Minimum range to q in meters. -- @param #number minrange Minimum range to q in meters.
-- @param #number maxrange Maximum range to q in meters. -- @param #number maxrange Maximum range to q in meters.
-- @param #booleean random Destination is randomly selected from friendly airport (true) or from destinations specified by user input (false). -- @param #boolean random Destination is randomly selected from friendly airport (true) or from destinations specified by user input (false).
-- @param #number Number indicating whether we land at a destination or return a zone object. -- @param #number landing Number indicating whether we land at a destination airport or fly to a zone object.
-- @return #table Table with possible destination airports or zones. -- @return Wrapper.Airbase#AIRBASE destination Destination airport or zone.
function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing) function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing)
-- Min/max range to destination. -- Min/max range to destination.
minrange=minrange or self.mindist minrange=minrange or self.mindist
maxrange=maxrange or self.maxdist maxrange=maxrange or self.maxdist
local possible_destinations={} -- All possible destinations.
local destinations={}
if random then if random then
-- Airports of friendly coalitions. -- Airports of friendly coalitions.
@ -2238,9 +2235,9 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing)
-- Check if distance form departure to destination is within min/max range. -- Check if distance form departure to destination is within min/max range.
if distance>=minrange and distance<=maxrange then if distance>=minrange and distance<=maxrange then
if landing==RAT.wp.air then if landing==RAT.wp.air then
table.insert(possible_destinations, airport:GetZone()) -- insert zone object. table.insert(destinations, airport:GetZone()) -- insert zone object.
else else
table.insert(possible_destinations, airport) -- insert airport object. table.insert(destinations, airport) -- insert airport object.
end end
end end
end end
@ -2262,7 +2259,11 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing)
dest=AIRBASE:FindByName(name) dest=AIRBASE:FindByName(name)
end end
elseif self:_ZoneExists(name) then elseif self:_ZoneExists(name) then
dest=ZONE:New(name) if landing==RAT.wp.air then
dest=ZONE:New(name)
else
env.error(RAT.id.."Landing is not in air. Cannot use zone "..name.." as destination!")
end
else else
env.error(RAT.id.."No airport or zone found with name "..name) env.error(RAT.id.."No airport or zone found with name "..name)
end end
@ -2272,7 +2273,7 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing)
-- Add as possible destination if zone is within range. -- Add as possible destination if zone is within range.
if distance>=minrange and distance<=maxrange then if distance>=minrange and distance<=maxrange then
table.insert(possible_destinations, dest) table.insert(destinations, dest)
end end
end end
@ -2282,9 +2283,9 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing)
end end
-- Info message. -- Info message.
env.info(RAT.id.."Number of possible destination airports = "..#possible_destinations) env.info(RAT.id.."Number of possible destinations = "..#destinations)
if #possible_destinations > 0 then if #destinations > 0 then
--- Compare distance of destination airports. --- Compare distance of destination airports.
-- @param Core.Point#COORDINATE a Coordinate of point a. -- @param Core.Point#COORDINATE a Coordinate of point a.
-- @param Core.Point#COORDINATE b Coordinate of point b. -- @param Core.Point#COORDINATE b Coordinate of point b.
@ -2294,14 +2295,38 @@ function RAT:_GetDestinations(departure, q, minrange, maxrange, random, landing)
local qb=q:Get2DDistance(b:GetCoordinate()) local qb=q:Get2DDistance(b:GetCoordinate())
return qa < qb return qa < qb
end end
table.sort(possible_destinations, compare) table.sort(destinations, compare)
else else
env.error(RAT.id.."No possible destinations found!") destinations=nil
possible_destinations=nil
end end
-- Return table with destination airports.
return possible_destinations -- Randomly select one possible destination.
local destination
if destinations and #destinations>0 then
-- Random selection.
destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE
-- Debug message.
local text
if landing==RAT.wp.air then
text=string.format("Chosen destination zone: %s.", destination:GetName())
else
text=string.format("Chosen destination airport: %s (ID %d).", destination:GetName(), destination:GetID())
end
env.info(RAT.id..text)
if self.debug then
MESSAGE:New(text, 30):ToAll()
end
else
env.error(RAT.id.."No destination airport or zone found.")
destination=nil
end
-- Return the chosen destination.
return destination
end end
@ -2530,7 +2555,7 @@ function RAT:Status(message, forID)
end end
text=text..string.format("Fuel = %3.0f %%\n", fuel) text=text..string.format("Fuel = %3.0f %%\n", fuel)
text=text..string.format("Life = %3.0f %%\n", life) text=text..string.format("Life = %3.0f %%\n", life)
text=text..string.format("FL%03d = %i m\n", alt/RAT.unit.FL2m, alt) text=text..string.format("FL%03d = %i m ASL\n", alt/RAT.unit.FL2m, alt)
--text=text..string.format("Speed = %i km/h\n", vel) --text=text..string.format("Speed = %i km/h\n", vel)
text=text..string.format("Distance travelled = %6.1f km\n", self.ratcraft[i]["Distance"]/1000) text=text..string.format("Distance travelled = %6.1f km\n", self.ratcraft[i]["Distance"]/1000)
text=text..string.format("Distance to destination = %6.1f km", Ddestination/1000) text=text..string.format("Distance to destination = %6.1f km", Ddestination/1000)
@ -3206,9 +3231,9 @@ function RAT:_TaskHolding(P1, Altitude, Speed, Duration)
DCSTask.params.task=Task DCSTask.params.task=Task
if self.ATCswitch then if self.ATCswitch then
-- Set stop condition for holding. Either flag=1 or after max. 30 min holding. -- Set stop condition for holding. Either flag=1 or after max. 50 min holding.
local userflagname=string.format("%s#%03d", self.alias, self.SpawnIndex+1) local userflagname=string.format("%s#%03d", self.alias, self.SpawnIndex+1)
DCSTask.params.stopCondition={userFlag=userflagname, userFlagValue=1, duration=1800} DCSTask.params.stopCondition={userFlag=userflagname, userFlagValue=1, duration=3000}
else else
DCSTask.params.stopCondition={duration=Duration} DCSTask.params.stopCondition={duration=Duration}
end end
@ -3348,16 +3373,19 @@ function RAT:_FLmax(alpha, beta, d, phi, h0)
return h3+h0 return h3+h0
end end
--- Calculate min distance between departure and destination for given minimum flight level and climb/decent rates --- Calculate minimum distance between departure and destination for given minimum flight level and climb/decent rates.
-- @param #RAT self -- @param #RAT self
-- @param #number alpha Angle of climb [rad]. -- @param #number alpha Angle of climb [rad].
-- @param #number beta Angle of descent [rad]. -- @param #number beta Angle of descent [rad].
-- @param #number h min height AGL. -- @param #number ha Height difference between departure and cruise altiude.
-- @return #number Minimum distance between departure and destiantion. -- @param #number hb Height difference between cruise altitude and destination.
function RAT:_MinDistance(alpha, beta, h) -- @return #number d1 Minimum distance for climb phase to reach cruise altitude.
local d1=h/math.tan(alpha) -- @return #number d2 Minimum distance for descent phase to reach destination height.
local d2=h/math.tan(beta) -- @return #number dtot Minimum total distance to climb and descent.
return d1+d2 function RAT:_MinDistance(alpha, beta, ha, hb)
local d1=ha/math.tan(alpha)
local d2=hb/math.tan(beta)
return d1, d2, d1+d2
end end
@ -3576,25 +3604,14 @@ end
-- This allows to spawn at airports and also land at other airports, i.e. circumventing the DCS "landing bug". -- This allows to spawn at airports and also land at other airports, i.e. circumventing the DCS "landing bug".
-- @param #RAT self -- @param #RAT self
-- @param #table waypoints The waypoints of the AI flight plan. -- @param #table waypoints The waypoints of the AI flight plan.
function RAT:_ModifySpawnTemplate(waypoints) -- @param #string livery (Optional) Livery of the aircraft. All members of a flight will get the same livery.
function RAT:_ModifySpawnTemplate(waypoints, livery)
-- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group.
local PointVec3 = {x=waypoints[1].x, y=waypoints[1].alt, z=waypoints[1].y} local PointVec3 = {x=waypoints[1].x, y=waypoints[1].alt, z=waypoints[1].y}
-- Heading from first to seconds waypoints to align units in case of air start. -- Heading from first to seconds waypoints to align units in case of air start.
local heading = self:_Course(waypoints[1], waypoints[2]) local heading = self:_Course(waypoints[1], waypoints[2])
-- Set (another) livery.
local skin=nil
if self.livery then
if self.debug then
for _, skin in pairs(self.livery) do
env.info(RAT.id.."Possible livery: "..skin.." for group "..self:_AnticipatedGroupName())
end
end
skin=self.livery[math.random(#self.livery)]
env.info(RAT.id.."Chosen livery: "..skin.." for group "..self:_AnticipatedGroupName())
end
if self:_GetSpawnIndex(self.SpawnIndex+1) then if self:_GetSpawnIndex(self.SpawnIndex+1) then
@ -3621,8 +3638,8 @@ function RAT:_ModifySpawnTemplate(waypoints)
SpawnTemplate.units[UnitID].heading = math.rad(heading) SpawnTemplate.units[UnitID].heading = math.rad(heading)
-- Set livery (will be the same for all units of the group). -- Set livery (will be the same for all units of the group).
if skin then if livery then
SpawnTemplate.units[UnitID].livery_id = skin SpawnTemplate.units[UnitID].livery_id = livery
end end
-- Set type of aircraft. -- Set type of aircraft.
@ -3975,4 +3992,3 @@ function RAT:_ATCQueue()
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------