Navigation

This commit is contained in:
Frank 2023-10-08 21:49:15 +02:00
parent 1ece7238dc
commit 4e24a7bf80
9 changed files with 524 additions and 32 deletions

View File

@ -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

View File

@ -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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -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, "<Title>") then
flightplan.title=getXMLelement(line)
end
-- Departure ICAO
if string.find(line, "<DepartureID>") then
flightplan.departureICAO=getXMLelement(line)
end
-- Destination ICAO
if string.find(line, "<DestinationID>") then
flightplan.destinationICAO=getXMLelement(line)
end
-- FPType
if string.find(line, "<FPType>") then
flightplan.plantype=getXMLelement(line)
end
-- Route type
if string.find(line, "<RouteType>") then
flightplan.routetype=getXMLelement(line)
end
-- Cruise alt in feet
if string.find(line, "<CruisingAlt>") then
flightplan.altCruise=getXMLelement(line)
end
-- Departure LLA
if string.find(line, "<DepartureLLA>") then
local lat,long=getLatLong(line)
end
-- Destination LLA
if string.find(line, "<DestinationLLA>") then
local lat,long=getLatLong(line)
end
-- Departure Name
if string.find(line, "<DepartureName>") then
local DepartureName=getXMLelement(line)
end
-- DestinationName
if string.find(line, "<DestinationName>") 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, "</ATCWaypoint>") 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, "<ATCWaypointType>") then
local wptype=getXMLelement(line)
wp.type=wptype
end
-- Waypoint position.
if string.find(line, "<WorldPosition>") 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, "<DepartureFP>") then
wp.segment="Departure"
end
-- Segment is Arrival
if string.find(line, "<ArrivalFP>") then
wp.segment="Arrival"
end
-- Segment is Enroute
if string.find(line, "<ATCAirway>") 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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -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

View File

@ -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:**
--

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.