diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua
index 97730f0ac..fdb30a528 100644
--- a/Moose Development/Moose/Core/Point.lua
+++ b/Moose Development/Moose/Core/Point.lua
@@ -468,6 +468,23 @@ do -- COORDINATE
end
+ --- Returns the coordinate from the latitude and longitude given in degrees, minutes and seconds (DMS).
+ -- @param #COORDINATE self
+ -- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
+ -- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
+ -- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate.
+ -- @return #COORDINATE
+ function COORDINATE:NewFromLLDMS(Latitude, Longitude, Altitude)
+
+ local lat=UTILS.LLDMSstringToDD(Latitude)
+ local lon=UTILS.LLDMSstringToDD(Longitude)
+
+ self=COORDINATE:NewFromLLDD(lat, lon, Altitude)
+
+ return self
+ end
+
+
--- Returns if the 2 coordinates are at the same 2D position.
-- @param #COORDINATE self
-- @param #COORDINATE Coordinate
diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua
index c12fe2ef6..9a9aeef61 100644
--- a/Moose Development/Moose/Core/Vector.lua
+++ b/Moose Development/Moose/Core/Vector.lua
@@ -270,6 +270,48 @@ function VECTOR:NewDirectionalVector(a, b)
return c
end
+
+--- Creates a new VECTOR instance from given the latitude and longitude in decimal degrees (DD).
+-- @param #VECTOR self
+-- @param #number Latitude Latitude in decimal degrees.
+-- @param #number Longitude Longitude in decimal degrees.
+-- @param #number altitude (Optional) Altitude in meters. Default is the land height at the 2D position.
+-- @return #VECTOR self
+function VECTOR:NewFromLLDD(Latitude, Longitude, Altitude)
+
+ -- Returns a point from latitude and longitude in the vec3 format.
+ local vec3=coord.LLtoLO(Latitude, Longitude)
+
+ -- Convert vec3 to coordinate object.
+ self=VECTOR:NewFromVec(vec3)
+
+-- -- Adjust height
+-- if Altitude==nil then
+-- self.y=self:GetSurfaceHeight()
+-- else
+-- self.y=Altitude
+-- end
+
+ return self
+end
+
+--- Creates a new VECTOR instance from given latitude and longitude in degrees, minutes and seconds (DMS).
+-- **Note** that latitude and longitude are passed as strings and the characters `°`, `'` and `"` are important.
+-- @param #VECTOR self
+-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`".
+-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`".
+-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate.
+-- @return #VECTOR
+function VECTOR:NewFromLLDMS(Latitude, Longitude, Altitude)
+
+ local lat=UTILS.LLDMSstringToDD(Latitude)
+ local lon=UTILS.LLDMSstringToDD(Longitude)
+
+ self=VECTOR:NewFromLLDD(lat, lon, Altitude)
+
+ return self
+end
+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/Moose Development/Moose/Navigation/FlightPlan.lua b/Moose Development/Moose/Navigation/FlightPlan.lua
index fb1520337..e8960b27f 100644
--- a/Moose Development/Moose/Navigation/FlightPlan.lua
+++ b/Moose Development/Moose/Navigation/FlightPlan.lua
@@ -25,11 +25,12 @@
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table fixes Navigation fixes.
+-- @field Core.Pathline#PATHLINE pathline Pathline of the plan.
-- @field Wrapper.Airbase#AIRBASE departureAirbase Departure airbase.
-- @field Wrapper.Airbase#AIRBASE destinationAirbase Destination airbase.
-- @field #number altitudeCruiseMin Minimum cruise altitude in feet MSL.
-- @field #number altitudeCruiseMax Maximum cruise altitude in feet MSL.
--- @extends Core.Base#BASE
+-- @extends Core.Pathline#PATHLINE
--- *Life is what happens to us while we are making other plans.* -- Allen Saunders
--
@@ -37,12 +38,11 @@
--
-- # The FLIGHTPLAN Concept
--
--- A FLIGHTPLAN consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
+-- This class has a great concept!
--
-- # Basic Setup
--
--- A new `FLIGHTPLAN` object can be created with the @{#FLIGHTPLAN.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet
--- and `FleetName` is the name you want to give the fleet. This must be *unique*!
+-- A new `FLIGHTPLAN` object can be created with the @{#FLIGHTPLAN.New}() function.
--
-- myFlightplan=FLIGHTPLAN:New("Plan A")
-- myFleet:SetPortZone(ZonePort1stFleet)
@@ -81,8 +81,10 @@ FLIGHTPLAN.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: How to connect SID, STAR, ENROUTE, TRANSITION, APPROACH. Typical flightplan SID --> ENROUTE --> STAR --> APPROACH
--- TODO: How to handle the FLIGHTGROUP:_LandAtAirBase
+-- TODO: Add approach.
+-- DONE: How to handle the FLIGHTGROUP:_LandAtAirBase
-- TODO: Do we always need a holding pattern? https://www.faa.gov/air_traffic/publications/atpubs/aip_html/part2_enr_section_1.5.html#:~:text=If%20no%20holding%20pattern%20is,than%20that%20desired%20by%20ATC.
+-- DOEN: Read from MSFS file.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -94,8 +96,8 @@ FLIGHTPLAN.version="0.0.1"
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:New(Name)
- -- Inherit everything from SCENERY class.
- self=BASE:Inherit(self, BASE:New()) -- #FLIGHTPLAN
+ -- Inherit everything from BASE class.
+ self=BASE:Inherit(self, PATHLINE:New(Name)) -- #FLIGHTPLAN
-- Set alias.
self.alias=tostring(Name)
@@ -103,6 +105,8 @@ function FLIGHTPLAN:New(Name)
-- Set some string id for output to DCS.log file.
self.lid=string.format("FLIGHTPLAN %s | ", self.alias)
+ --self.pathline=PATHLINE:New(Name)
+
-- Debug info.
self:I(self.lid..string.format("Created FLIGHTPLAN!"))
@@ -119,30 +123,55 @@ function FLIGHTPLAN:NewFromFlightPlan(FlightPlan)
return self
end
+
+--- Create a new FLIGHTPLAN instance from a given file.
+-- Currently, the file has to be an MSFS 2020 .pln file as, *e.g.*, exported from [Navigraph](https://navigraph.com/).
+--
+-- **Note** that the flight plan does only cover the departure, enroute and arrival portions but **not the approach** part!
+-- @param #FLIGHTPLAN self
+-- @param #string FileName Full path to file.
+-- @return #FLIGHTPLAN self
+function FLIGHTPLAN:NewFromFile(FileName)
+
+ if UTILS.FileExists(FileName) then
+
+ self=FLIGHTPLAN._ReadFileMSFS(FileName)
+
+ else
+ error(string.format("ERROR: File not found! File name=%s", tostring(FileName)))
+ end
+
+ return self
+end
+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add navigation fix to the flight plan.
-- @param #FLIGHTPLAN self
--- @param Navigation.NavFix#NAVFIX NavFix The nav fix.
+-- @param Navigation.Point#NAVPOINT NavFix The nav fix.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:AddNavFix(NavFix)
table.insert(self.fixes, NavFix)
+
+ local point=self:AddPointFromVec3(NavFix.vector:GetVec3(true))
+
+ point.navpoint=NavFix
return self
end
---- Set depature airbase.
+--- Set departure airbase.
-- @param #FLIGHTPLAN self
-- @param #string AirbaseName Name of the airbase or AIRBASE object.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:SetDepartureAirbase(AirbaseName)
self.departureAirbase=AIRBASE:FindByName(AirbaseName)
-
+
return self
end
@@ -171,6 +200,19 @@ function FLIGHTPLAN:SetCruiseAltitude(AltMin, AltMax)
return self
end
+--- Set cruise speed.
+-- @param #FLIGHTPLAN self
+-- @param #number SpeedMin Minimum speed in knots.
+-- @param #number SpeedMax Maximum speed in knots. Default is `SpeedMin`.
+-- @return #FLIGHTPLAN self
+function FLIGHTPLAN:SetCruiseSpeed(SpeedMin, SpeedMax)
+
+ self.speedCruiseMin=SpeedMin
+ self.speedCruiseMax=SpeedMax or self.speedCruiseMin
+
+ return self
+end
+
--- Get the name of this flight plan.
-- @param #FLIGHTPLAN self
@@ -197,11 +239,242 @@ function FLIGHTPLAN:GetCruiseAltitude()
return alt
end
+--- Get cruise speed. This returns a random speed between the set min/max cruise speeds.
+-- @param #FLIGHTPLAN self
+-- @return #number Cruise speed in knots.
+function FLIGHTPLAN:GetCruiseSpeed()
+
+ local speed=250
+
+ if self.speedCruiseMin and self.speedCruiseMax then
+ speed=math.random(self.speedCruiseMin, self.speedCruiseMax)
+ elseif self.speedCruiseMin then
+ speed=self.speedCruiseMin
+ elseif self.altitudeCruiseMax then
+ speed=self.speedCruiseMax
+ end
+
+ return speed
+end
+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- No private functions yet.
+--- Read flight plan from a given MSFS 2020 .plt file.
+-- @param #string FileName Name of the file.
+-- @return #FLIGHTPLAN The flight plan.
+function FLIGHTPLAN._ReadFileMSFS(FileName)
+
+ local function readfile(filename)
+
+ local lines = {}
+
+ -- Open file in read binary mode.
+ local file=assert(io.open(filename, "rb"), string.format("File not found! File name = %s", tostring(filename)))
+
+ for line in file:lines() do
+ lines[#lines+1] = line
+ end
+
+ -- Close file.
+ file:close()
+
+ -- Return data
+ return lines
+ end
+
+
+ --- This function returns an XML element, i.e. the string between <...> and .
+ local function getXMLelement(line)
+ local element=string.match(line, ">(.+)<")
+ return element
+ end
+
+ --- This function returns Latitude and Longitude
+ local function getLatLong(line)
+ local latlong=getXMLelement(line)
+ -- The format is "N41° 38' 20.00",E41° 33' 19.00",+000000.00" so we still need to process that.
+ local lat,long=string.match(latlong, "(.+),(.+),")
+ return lat,long
+ end
+
+ -- Read data from file.
+ local data=readfile(FileName)
+
+ local flightplan={}
+ local waypoints={}
+ local wp=nil
+
+ local gotwaypoint=false
+ for i,line in pairs(data) do
+
+
+ --print(line)
+
+ -- Title
+ if string.find(line, "
") then
+ flightplan.title=getXMLelement(line)
+ end
+
+ -- Departure ICAO
+ if string.find(line, "") then
+ flightplan.departureICAO=getXMLelement(line)
+ end
+
+ -- Destination ICAO
+ if string.find(line, "") then
+ flightplan.destinationICAO=getXMLelement(line)
+ end
+
+ -- FPType
+ if string.find(line, "") then
+ flightplan.plantype=getXMLelement(line)
+ end
+
+ -- Route type
+ if string.find(line, "") then
+ flightplan.routetype=getXMLelement(line)
+ end
+
+ -- Cruise alt in feet
+ if string.find(line, "") then
+ flightplan.altCruise=getXMLelement(line)
+ end
+
+ -- Departure LLA
+ if string.find(line, "") then
+ local lat,long=getLatLong(line)
+ end
+
+ -- Destination LLA
+ if string.find(line, "") then
+ local lat,long=getLatLong(line)
+ end
+
+ -- Departure Name
+ if string.find(line, "") then
+ local DepartureName=getXMLelement(line)
+ end
+
+ -- DestinationName
+ if string.find(line, "") then
+ local DestinationName=getXMLelement(line)
+ end
+
+ ---
+ -- Waypoint stuff
+ ---
+
+ -- New waypoint starts.
+ if string.find(line, "ATCWaypoint id") then
+
+ --Get string inside quotes " and ".
+ local wpid=string.match(line, [["(.+)"]])
+
+ -- Create a new wp table.
+ wp={}
+
+ -- Set waypoint name.
+ wp.name=wpid
+ end
+
+ -- Waypoint info ends.
+ if string.find(line, "") then
+ -- This is the end of the waypoint.
+
+ -- Add info to waypoints table.
+ table.insert(waypoints, wp)
+
+ -- Set waypoint to nil. We create an empty table if the next wp starts.
+ wp=nil
+ end
+
+ -- Waypoint type (Airport, Intersection, NDB, VORTAC)
+ if string.find(line, "") then
+ local wptype=getXMLelement(line)
+ wp.type=wptype
+ end
+
+ -- Waypoint position.
+ if string.find(line, "") then
+ wp.lat, wp.long=getLatLong(line)
+ end
+
+ -- Runway should exist for initial and final WP if it is an airport.
+ if string.find(line, "RunwayNumberFP") then
+ wp.runway=getXMLelement(line)
+ end
+
+ -- Runway designator: LEFT, RIGHT, CENTER
+ if string.find(line, "RunwayDesignatorFP") then
+ wp.runwayDesignator=getXMLelement(line)
+ end
+
+ -- Segment is Departure
+ if string.find(line, "") then
+ wp.segment="Departure"
+ end
+
+ -- Segment is Arrival
+ if string.find(line, "") then
+ wp.segment="Arrival"
+ end
+
+ -- Segment is Enroute
+ if string.find(line, "") then
+ wp.segment="Enroute"
+ end
+
+ -- Approach type: VORDME, LOCALIZER
+ if string.find(line, "ApproachTypeFP") then
+ flightplan.approachtype=getXMLelement(line)
+ end
+
+ -- Approach type suffic: Z
+ if string.find(line, "SuffixFP") then
+ local SuffixFP=getXMLelement(line)
+ end
+
+ end
+
+ for key, value in pairs(flightplan) do
+ env.info(string.format("Flightplan %s=%s", key, tostring(value)))
+ end
+
+ env.info(string.format("Number of waypoints=%d", #waypoints))
+ for i,wp in pairs(waypoints) do
+ env.info(string.format("Waypoint name=%s type=%s segment=%s runway=%s lat=%s long=%s", wp.name, wp.type, tostring(wp.segment), tostring(wp.runway)..tostring(wp.runwayDesignator or ""), wp.lat, wp.long))
+ end
+
+ -- Create a new flightplan.
+ local fp=FLIGHTPLAN:New(flightplan.title)
+
+ -- Set cruise altitude.
+ fp:SetCruiseAltitude(flightplan.altCruise)
+
+ -- Set departure and destination airports.
+ fp:SetDepartureAirbase(flightplan.departureICAO)
+ fp:SetDestinationAirbase(flightplan.destinationICAO)
+
+ --TODO: Remove first and last waypoint if they are identical to the departure/destination airport!
+
+ for i,wp in pairs(waypoints) do
+
+ -- Create a navpoint.
+ local navpoint=NAVPOINT:NewFromLLDMS(wp.name, wp.type, wp.lat, wp.long)
+
+ navpoint:SetAltMin(flightplan.altCruise)
+
+ -- Add point to flightplan.
+ -- TODO: section departure, enroute, arrival.
+ fp:AddNavFix(navpoint)
+ end
+
+
+
+ return fp
+end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua
index 2ccfbf2dc..5bf6f5d4c 100644
--- a/Moose Development/Moose/Navigation/Point.lua
+++ b/Moose Development/Moose/Navigation/Point.lua
@@ -97,7 +97,7 @@ function NAVAID:New(ZoneName, SceneryName, Type)
self.zone=ZONE:FindByName(ZoneName)
- self=self.zone:GetCoordinate()
+ self.coordinate=self.zone:GetCoordinate()
if SceneryName then
self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName)
@@ -364,6 +364,7 @@ end
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #string name Name of the point.
+-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport".
-- @field Core.Vector#VECTOR vector Position vector of the fix.
-- @field Wrapper.Marker#MARKER marker Marker on F10 map.
-- @field #number altMin Minimum altitude in meters.
@@ -377,13 +378,16 @@ end
--
-- ===
--
--- # The NAVFIX Concept
+-- # The NAVPOINT Concept
--
--- The NAVFIX class has a great concept!
+-- The NAVPOINT class has a great concept!
+--
+-- A NAVPOINT describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc.
+-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point.
--
-- # Basic Setup
--
--- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function.
+-- A new `NAVPOINT` object can be created with the @{#NAVPOINT.New}() function.
--
-- myNavPoint=NAVPOINT:New()
-- myTemplate:SetXYZ(X, Y, Z)
@@ -396,6 +400,21 @@ NAVPOINT = {
verbose = 0,
}
+--- Type of point.
+-- @type NAVPOINT.Type
+-- @field #string VOR VOR
+-- @field #string NDB NDB
+NAVPOINT.Type={
+ POINT="Point",
+ INTERSECTION="Intersection",
+ AIRPORT="Airport",
+ VOR="VOR",
+ DME="DME",
+ VORDME="VOR/DME",
+ LOC="Localizer",
+ NDB="NDB",
+}
+
--- NAVPOINT class version.
-- @field #string version
NAVPOINT.version="0.0.1"
@@ -412,10 +431,11 @@ NAVPOINT.version="0.0.1"
--- Create a new NAVPOINT class instance from a given VECTOR.
-- @param #NAVPOINT self
--- @param #string Name Name of the fix. Should be unique!
+-- @param #string Name Name of the point. Should be unique!
+-- @param #string Type Type of the point. Default `NAVPOINT.Type.POINT`.
-- @param Core.Vector#VECTOR Vector Position vector of the navpoint.
-- @return #NAVPOINT self
-function NAVPOINT:NewFromVector(Name, Vector)
+function NAVPOINT:NewFromVector(Name, Type, Vector)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #NAVFIX
@@ -426,6 +446,8 @@ function NAVPOINT:NewFromVector(Name, Vector)
-- Name of point.
self.name=Name
+ self.typePoint=Type or NAVPOINT.Type.POINT
+
-- Marker on F10.
self.marker=MARKER:New(Vector:GetCoordinate(true), self:_GetMarkerText())
@@ -439,15 +461,59 @@ end
--- Create a new NAVPOINT class instance from a given COORDINATE.
-- @param #NAVPOINT self
-- @param #string Name Name of the fix. Should be unique!
+-- @param #string Type Type of the point. Default `NAVPOINT.Type.POINT`.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the point.
-- @return #NAVPOINT self
-function NAVPOINT:NewFromCoordinate(Name, Coordinate)
+function NAVPOINT:NewFromCoordinate(Name, Type, Coordinate)
+
+ -- Create a VECTOR from the coordinate.
local Vector=VECTOR:NewFromVec(Coordinate)
- self=NAVPOINT:NewFromVector(Name, Vector)
+
+ -- Create NAVPOINT.
+ self=NAVPOINT:NewFromVector(Name, Type, Vector)
+
return self
end
---- Create a new NAVPOINT class instance from a given NAVPOINT.
+
+--- Create a new NAVPOINT instance from given latitude and longitude in degrees, minutes and seconds (DMS).
+-- @param #NAVPOINT self
+-- @param #string Name Name of the fix. Should be unique!
+-- @param #string Type Type of the point. Default `NAVPOINT.Type.POINT`.
+-- @param #string Latitude Latitude in DMS as string.
+-- @param #string Longitude Longitude in DMS as string.
+-- @return #NAVPOINT self
+function NAVPOINT:NewFromLLDMS(Name, Type, Latitude, Longitude)
+
+ -- Create a VECTOR from the coordinate.
+ local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude)
+
+ -- Create NAVPOINT.
+ self=NAVPOINT:NewFromVector(Name, Type, Vector)
+
+ return self
+end
+
+--- Create a new NAVPOINT instance from given latitude and longitude in decimal degrees (DD).
+-- @param #NAVPOINT self
+-- @param #string Name Name of the fix. Should be unique!
+-- @param #string Type Type of the point. Default `NAVPOINT.Type.POINT`.
+-- @param #number Latitude Latitude in DD.
+-- @param #number Longitude Longitude in DD.
+-- @return #NAVPOINT self
+function NAVPOINT:NewFromLLDD(Name, Type, Latitude, Longitude)
+
+ -- Create a VECTOR from the coordinate.
+ local Vector=VECTOR:NewFromLLDD(Latitude, Longitude)
+
+ -- Create NAVPOINT.
+ self=NAVPOINT:NewFromVector(Name, Type, Vector)
+
+ return self
+end
+
+
+--- Create a new NAVPOINT class instance relative to a given other NAVPOINT.
-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the
-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps.
-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify.
@@ -517,7 +583,19 @@ end
-- @return #NAVPOINT self
function NAVPOINT:SetSpeedMin(Speed)
- self.speedMin=Altitude
+ self.speedMin=Speed
+
+ return self
+end
+
+
+--- Set maximum speed.
+-- @param #NAVPOINT self
+-- @param #number Speed Max speed in knots.
+-- @return #NAVPOINT self
+function NAVPOINT:SetSpeedMax(Speed)
+
+ self.speedMax=Speed
return self
end
@@ -541,13 +619,13 @@ function NAVPOINT:SetFlyOver(FlyOver)
end
---- Get the altitude in feet MSL.
+--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max.
-- @param #NAVPOINT self
-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set.
function NAVPOINT:GetAltitude()
local alt=nil
- if self.altMin and self.altMax then
+ if self.altMin and self.altMax and self.altMin~=self.altMax then
alt=math.random(self.altMin, self.altMax)
elseif self.altMin then
alt=self.altMin
diff --git a/Moose Development/Moose/Navigation/Procedure.lua b/Moose Development/Moose/Navigation/Procedure.lua
index 3038e4214..2a0cca784 100644
--- a/Moose Development/Moose/Navigation/Procedure.lua
+++ b/Moose Development/Moose/Navigation/Procedure.lua
@@ -1,4 +1,4 @@
---- **NAVIGATION** - Prodedures for Departure (SID), Arrival (STAR) and Approach.
+--- **NAVIGATION** - Prodedures for Departure (*e.g.* SID), Enroute, Arrival (*e.g.* STAR) and Approach.
--
-- **Main Features:**
--
diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua
index 38262026c..5bb3ea9da 100644
--- a/Moose Development/Moose/Ops/FlightGroup.lua
+++ b/Moose Development/Moose/Ops/FlightGroup.lua
@@ -3697,13 +3697,17 @@ end
-- Flightplan Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Check flightplans.
+--- Set flightplan and add waypoints.
-- @param #FLIGHTGROUP self
-- @param Navigation.FlightPlan#FLIGHTPLAN FlightPlan Flight plan.
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:_SetFlightPlan(FlightPlan)
+ self:I(self.lid..string.format("Setting flightplan %s", FlightPlan.alias))
+
self.flightplan=FlightPlan
+
+ self:SetDestinationbase(self.flightplan.destinationAirbase)
for i,_fix in pairs(FlightPlan.fixes) do
local fix=_fix --Navigation.NavFix#NAVFIX
@@ -3714,7 +3718,7 @@ function FLIGHTGROUP:_SetFlightPlan(FlightPlan)
local altitude=(fix.altMin or fix.altMax)~=nil and fix:GetAltitude() or FlightPlan:GetCruiseAltitude()
- local wp=self:AddWaypoint(fix.coordinate, Speed, AfterWaypointWithID, altitude or 10000, false)
+ local wp=self:AddWaypoint(fix.vector, Speed, AfterWaypointWithID, altitude or 10000, false)
wp.flightplan=FlightPlan
end
diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua
index 122b7cd8e..135571e8d 100644
--- a/Moose Development/Moose/Ops/OpsGroup.lua
+++ b/Moose Development/Moose/Ops/OpsGroup.lua
@@ -13493,20 +13493,24 @@ end
-- @return Core.Point#COORDINATE The coordinate of the object.
function OPSGROUP:_CoordinateFromObject(Object)
+ env.info("FF coordfrom object")
if Object then
- if Object:IsInstanceOf("COORDINATE") then
+ if VECTOR._IsVector(Object) then
+ env.info("FF VECTOR")
+ return Object:GetCoordinate()
+ elseif Object:IsInstanceOf("COORDINATE") then
return Object
else
if Object:IsInstanceOf("POSITIONABLE") or Object:IsInstanceOf("ZONE_BASE") then
- self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
+ self:E(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
local coord=Object:GetCoordinate()
return coord
else
- self:T(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
+ self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
end
end
else
- self:T(self.lid.."ERROR: Object passed is nil!")
+ self:E(self.lid.."ERROR: Object passed is nil!")
end
return nil
diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua
index b7461eb37..76a51bc04 100644
--- a/Moose Development/Moose/Utilities/Utils.lua
+++ b/Moose Development/Moose/Utilities/Utils.lua
@@ -721,7 +721,22 @@ end
-- @return #number Latitude or Longitude in decimal degrees.
UTILS.LLDMSToDD = function(Degrees, Minutes, Seconds)
- local dd=(Degrees or 0) + (Minutes or 0)/60 + (Seconds or 0)/3600
+ local dd=tonumber(Degrees or 0) + tonumber(Minutes or 0)/60 + tonumber(Seconds or 0)/3600
+
+ return dd
+end
+
+--- Convert latitude or longitude from degrees, minutes, seconds (DMS) given in text form to decimal degrees (DD).
+-- @param #string LatOrLongString Latitude or longitude passed as ttring in format `DD°MM'SS.SS"`.
+-- @return #number Latitude or Longitude in decimal degrees.
+UTILS.LLDMSstringToDD = function(LatOrLongString)
+
+ local hem=string.match(LatOrLongString, "(%a)")
+ local Degrees=string.match(LatOrLongString, "(%d+)°")
+ local Minutes=string.match(LatOrLongString, "(%d+)'")
+ local Seconds=string.match(LatOrLongString, "([%d\.]+)\"")
+
+ local dd=UTILS.LLDMSToDD(Degrees, Minutes, Seconds)
return dd
end
diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua
index 7510a5be1..4cdf88092 100644
--- a/Moose Development/Moose/Wrapper/Airbase.lua
+++ b/Moose Development/Moose/Wrapper/Airbase.lua
@@ -753,6 +753,32 @@ AIRBASE.SpotStatus = {
RESERVED="Reserved",
}
+--- ICAO Codes.
+-- @type AIRBASE.ICAO
+AIRBASE.ICAO = {
+ UGTB=AIRBASE.Caucasus.Tbilisi_Lochini,
+ UGKO=AIRBASE.Caucasus.Kutaisi,
+ UG5X=AIRBASE.Caucasus.Kobuleti,
+ UG24=AIRBASE.Caucasus.Soganlug,
+ UG27=AIRBASE.Caucasus.Vaziani,
+ UGSS=AIRBASE.Caucasus.Sukhumi_Babushara,
+ UG23=AIRBASE.Caucasus.Gudauta,
+ URSS=AIRBASE.Caucasus.Sochi_Adler,
+ URMO=AIRBASE.Caucasus.Beslan,
+ URMN=AIRBASE.Caucasus.Nalchik,
+ XRMF=AIRBASE.Caucasus.Mozdok,
+ URMM=AIRBASE.Caucasus.Mineralnye_Vody,
+ URKH=AIRBASE.Caucasus.Maykop_Khanskaya,
+ URKK=AIRBASE.Caucasus.Krasnodar_Pashkovsky,
+ URKL=AIRBASE.Caucasus.Krasnodar_Center,
+ URKG=AIRBASE.Caucasus.Gelendzhik,
+ URKN=AIRBASE.Caucasus.Novorossiysk,
+ URKW=AIRBASE.Caucasus.Krymsk,
+ URKA=AIRBASE.Caucasus.Anapa_Vityazevo,
+}
+
+
+
--- Runway data.
-- @type AIRBASE.Runway
-- @field #string name Runway name.
@@ -875,14 +901,47 @@ end
--- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase.
-- @param #AIRBASE self
--- @param #string AirbaseName The Airbase Name.
+-- @param #string AirbaseName The name of the airbase. Can also be the ICAO code or the airbase ID.
-- @return #AIRBASE self
function AIRBASE:FindByName( AirbaseName )
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
+
+ if not AirbaseFound then
+ AirbaseFound=self:FindByICAO(AirbaseName)
+ end
+
+ if not AirbaseFound then
+ AirbaseFound=self:FindByID(AirbaseName)
+ end
+
return AirbaseFound
end
+
+--- Find an AIRBASE in the _DATABASE using the [International Civil Aviation Organization](https://en.wikipedia.org/wiki/ICAO_airport_code) (ICAO) airport code.
+-- The code consists of four characters. Typically, the first one or two letters of the ICAO code indicate the country and the remaining letters identify the airport.
+--
+-- **NOTE** that the ICAO code cannot be retrieved via the DCS API and has to be hard coded into the MOOSE code. Therefore, it is a rare occasion where
+-- @param #AIRBASE self
+-- @param #string AirbaseICAO The Airbase ICAO code.
+-- @return #AIRBASE self
+function AIRBASE:FindByICAO( AirbaseICAO )
+
+ if AirbaseICAO then
+
+ local name=AIRBASE.ICAO[AirbaseICAO]
+
+ env.info(string.format("FF ICAO=%s, Name=%s", tostring(AirbaseICAO), tostring(name)))
+
+ local Airbase=self:FindByName(name)
+ return Airbase
+
+ end
+
+ return nil
+end
+
--- Find a AIRBASE in the _DATABASE by its ID.
-- @param #AIRBASE self
-- @param #number id Airbase ID.