From 77a39364f4c42d0304ea77cafa993ae7771b815a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 14 Feb 2023 12:58:23 +0100 Subject: [PATCH 1/7] #PLAYERTASKCONTROLLER * Added ship detail types --- Moose Development/Moose/Ops/PlayerTask.lua | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 94901b479..e4afe4b7a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1369,6 +1369,13 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Airdefense", SAM = "SAM", GROUP = "Group", + UNARMEDSHIP = "Merchant", + LIGHTARMEDSHIP = "Light Boat", + CORVETTE = "Corvette", + FRIGATE = "Frigate", + CRUISER = "Cruiser", + DESTROYER = "Destroyer", + CARRIER = "Aircraft Carrier", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1441,12 +1448,19 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Flak", SAM = "Luftabwehr", GROUP = "Einheit", + UNARMEDSHIP = "Handelsschiff", + LIGHTARMEDSHIP = "Tender", + CORVETTE = "Korvette", + FRIGATE = "Fregatte", + CRUISER = "Kreuzer", + DESTROYER = "Zerstörer", + CARRIER = "Flugzeugträger", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.56" +PLAYERTASKCONTROLLER.version="0.1.57" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2262,6 +2276,27 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() --self:T(self.lid.."Target TypeName = "..target.TypeName) end + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + self:_AddTask(target) end return self From 5d4c0f3c87dc4745dcc3bf0082e3715961c5b5d0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 10:31:52 +0100 Subject: [PATCH 2/7] Bugfix --- Moose Development/Moose/Wrapper/Group.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 19c2b9fd6..99125e66a 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,4 +1,4 @@ ---- **Wrapper** - GROUP wraps the DCS Class Group objects. +----- **Wrapper** - GROUP wraps the DCS Class Group objects. -- -- === -- @@ -2810,7 +2810,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) if self:IsAlive() then local IsPlayer = self:IsPlayer() local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1 - local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi + local callsignroot = string.match(shortcallsign, '(%a+)') or "Ghost" -- Uzi --self:I("CallSign = " .. callsignroot) local groupname = self:GetName() local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91 From ef0ddddb46b9602ac35d3686c2f71e25456a21fc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 10:32:30 +0100 Subject: [PATCH 3/7] #PLAYERTASK * added group/unit check for UseTypeNames --- Moose Development/Moose/Ops/PlayerTask.lua | 102 +++++++++++---------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e4afe4b7a..b5ee9e527 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1460,7 +1460,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.57" +PLAYERTASKCONTROLLER.version="0.1.58" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2245,56 +2245,60 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() end end - if self.UseTypeNames and object:IsGround() then - -- * Threat level 0: Unit is unarmed. - -- * Threat level 1: Unit is infantry. - -- * Threat level 2: Unit is an infantry vehicle. - -- * Threat level 3: Unit is ground artillery. - -- * Threat level 4: Unit is a tank. - -- * Threat level 5: Unit is a modern tank or ifv with ATGM. - -- * Threat level 6: Unit is a AAA. - -- * Threat level 7: Unit is a SAM or manpad, IR guided. - -- * Threat level 8: Unit is a Short Range SAM, radar guided. - -- * Threat level 9: Unit is a Medium Range SAM, radar guided. - -- * Threat level 10: Unit is a Long Range SAM, radar guided. - local threat = object:GetThreatLevel() - local typekey = "INFANTRY" - if threat == 0 or threat == 2 then - typekey = "TECHNICAL" - elseif threat == 3 then - typekey = "ARTILLERY" - elseif threat == 4 or threat == 5 then - typekey = "TANKS" - elseif threat == 6 or threat == 7 then - typekey = "AIRDEFENSE" - elseif threat >= 8 then - typekey = "SAM" - end - local typename = self.gettext:GetEntry(typekey,self.locale) - local gname = self.gettext:GetEntry("GROUP",self.locale) - target.TypeName = string.format("%s %s",typename,gname) - --self:T(self.lid.."Target TypeName = "..target.TypeName) - end + if object:IsInstanceOf("UNIT") or object:IsInstanceOf("GROUP") then - if self.UseTypeNames and object:IsShip() then - local threat = object:GetThreatLevel() - local typekey = "UNARMEDSHIP" - if threat == 1 then - typekey = "LIGHTARMEDSHIP" - elseif threat == 2 then - typekey = "CORVETTE" - elseif threat == 3 or threat == 4 then - typekey = "FRIGATE" - elseif threat == 5 or threat == 6 then - typekey = "CRUISER" - elseif threat == 7 or threat == 8 then - typekey = "DESTROYER" - elseif threat >= 9 then - typekey = "CARRIER" + if self.UseTypeNames and object:IsGround() then + -- * Threat level 0: Unit is unarmed. + -- * Threat level 1: Unit is infantry. + -- * Threat level 2: Unit is an infantry vehicle. + -- * Threat level 3: Unit is ground artillery. + -- * Threat level 4: Unit is a tank. + -- * Threat level 5: Unit is a modern tank or ifv with ATGM. + -- * Threat level 6: Unit is a AAA. + -- * Threat level 7: Unit is a SAM or manpad, IR guided. + -- * Threat level 8: Unit is a Short Range SAM, radar guided. + -- * Threat level 9: Unit is a Medium Range SAM, radar guided. + -- * Threat level 10: Unit is a Long Range SAM, radar guided. + local threat = object:GetThreatLevel() + local typekey = "INFANTRY" + if threat == 0 or threat == 2 then + typekey = "TECHNICAL" + elseif threat == 3 then + typekey = "ARTILLERY" + elseif threat == 4 or threat == 5 then + typekey = "TANKS" + elseif threat == 6 or threat == 7 then + typekey = "AIRDEFENSE" + elseif threat >= 8 then + typekey = "SAM" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + local gname = self.gettext:GetEntry("GROUP",self.locale) + target.TypeName = string.format("%s %s",typename,gname) + --self:T(self.lid.."Target TypeName = "..target.TypeName) end - local typename = self.gettext:GetEntry(typekey,self.locale) - target.TypeName = typename - --self:T(self.lid.."Target TypeName = "..target.TypeName) + + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + end self:_AddTask(target) From 35322399e9ae78a844459508f2375009a70095b3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 12:23:31 +0100 Subject: [PATCH 4/7] #SPOT - minor fixes --- Moose Development/Moose/Core/Spot.lua | 35 ++++++++++++++------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index f4dc1f967..d87606937 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -5,7 +5,7 @@ -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: -- -- * Spot for a defined duration. --- * Updates of laer spot position every 0.2 seconds for moving targets. +-- * Updates of laser spot position every 0.2 seconds for moving targets. -- * Wiggle the spot at the target. -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. @@ -13,18 +13,8 @@ -- === -- -- # Demo Missions --- --- ### [SPOT Demo Missions source code]() --- --- ### [SPOT Demo Missions, only for beta testers]() -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) --- --- === --- --- # YouTube Channel --- --- ### [SPOT YouTube Channel]() +-- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- === -- @@ -50,14 +40,14 @@ do --- Implements the target spotting or marking functionality, but adds additional luxury to be able to: -- -- * Mark targets for a defined duration. - -- * Updates of laer spot position every 0.2 seconds for moving targets. + -- * Updates of laser spot position every 0.25 seconds for moving targets. -- * Wiggle the spot at the target. -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. -- -- ## 1. SPOT constructor -- - -- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object. + -- * @{#SPOT.New}(): Creates a new SPOT object. -- -- ## 2. SPOT is a FSM -- @@ -217,6 +207,8 @@ do self.Recce = Recce + + self.RecceName = self.Recce:GetName() self.LaseScheduler = SCHEDULER:New( self ) @@ -243,6 +235,9 @@ do end self.Target = Target + + self.TargetName = Target:GetName() + self.LaserCode = LaserCode self.Lasing = true @@ -302,12 +297,18 @@ do function SPOT:OnEventDead(EventData) self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) if self.Target then - if EventData.IniDCSUnitName == self.Target:GetName() then - self:F( {"Target dead ", self.Target:GetName() } ) + if EventData.IniDCSUnitName == self.TargetName then + self:F( {"Target dead ", self.TargetName } ) self:Destroyed() self:LaseOff() end end + if self.Recce then + if EventData.IniDCSUnitName == self.RecceName then + self:F( {"Recce dead ", self.RecceName } ) + self:LaseOff() + end + end end --- @param #SPOT self @@ -382,4 +383,4 @@ do return self end -end +end \ No newline at end of file From bae7edb9149dedb30fdf5b5ba80d5b9b50e312e1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Feb 2023 11:41:45 +0100 Subject: [PATCH 5/7] #NET * Added event management for clients and block/unblock functions --- Moose Development/Moose/Wrapper/Net.lua | 230 +++++++++++++++++++++++- 1 file changed, 227 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index e822a176b..d2ac28285 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -16,14 +16,27 @@ do -- @type NET -- @field #string ClassName -- @field #string Version --- @extends Core.Base#BASE +-- @field #string lid +-- @field #number BlockTime +-- @field #table BlockedPilots +-- @field #table KnownPilots +-- @field #string BlockMessage +-- @field #string UnblockMessage +-- @extends Core.Fsm#FSM --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- with some added FSM functions and options to block/unblock players in MP environments. -- -- @field #NET NET = { ClassName = "NET", - Version = "0.0.2" + Version = "0.0.3", + BlockTime = 600, + BlockedPilots = {}, + KnownPilots = {}, + BlockMessage = nil, + UnblockMessage = nil, + lid = nil, } --- Instantiate a new NET object. @@ -31,7 +44,218 @@ NET = { -- @return #NET self function NET:New() -- Inherit base. - local self = BASE:Inherit(self, BASE:New()) -- #NET + local self = BASE:Inherit(self, FSM:New()) -- #NET + + self.BlockTime = 600 + self.BlockedPilots = {} + self.KnownPilots = {} + self:SetBlockMessage() + self:SetUnblockMessage() + + self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) + self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler) + self:HandleEvent(EVENTS.PilotDead,self._EventHandler) + self:HandleEvent(EVENTS.Ejection,self._EventHandler) + self:HandleEvent(EVENTS.Crash,self._EventHandler) + self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler) + + -- Start State. + self:SetStartState("Running") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("*", "Run", "Running") -- Start FSM. + self:AddTransition("*", "PlayerJoined", "*") + self:AddTransition("*", "PlayerLeft", "*") + self:AddTransition("*", "PlayerDied", "*") + self:AddTransition("*", "PlayerEjected", "*") + self:AddTransition("*", "PlayerBlocked", "*") + self:AddTransition("*", "PlayerUnblocked", "*") + + self.lid = string.format("NET %s | ",self.Version) + + --- FSM Function OnAfterPlayerJoined. + -- @function [parent=#NET] OnAfterPlayerJoined + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of joining Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerLeft. + -- @function [parent=#NET] OnAfterPlayerLeft + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of leaving Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerEjected. + -- @function [parent=#NET] OnAfterPlayerEjected + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of leaving Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerDied. + -- @function [parent=#NET] OnAfterPlayerDied + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of dead Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerBlocked. + -- @function [parent=#NET] OnAfterPlayerBlocked + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param #string Name Name of blocked Pilot. + -- @param #number Seconds Blocked for this number of seconds + -- @return #NET self + + --- FSM Function OnAfterPlayerUnblocked. + -- @function [parent=#NET] OnAfterPlayerUnblocked + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param #string Name Name of unblocked Pilot. + -- @return #NET self + + return self +end + +--- [Internal] Event Handler +-- @param #NET self +-- @param Core.Event#EVENTDATA EventData +-- @return #NET self +function NET:_EventHandler(EventData) + self:T(self.lid .. " _EventHandler") + self:T2({Event = EventData.id}) + local data = EventData -- Core.Event#EVENTDATA EventData + if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then + -- Get PlayerName + local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() + self:T(self.lid.."Event for: "..name) + -- Joining + if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then + -- Check for known pilots + local TNow = timer.getTime() + if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then + -- block pilot + self:ReturnToSpectators(data.IniUnit) + else + self.KnownPilots[name] = true + self.BlockedPilots[name] = nil + self:__PlayerJoined(1,data.IniUnit,name) + return self + end + end + -- Leaving + if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then + self:__PlayerLeft(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + -- Ejected + if data.id == EVENTS.Ejection and self.KnownPilots[name] then + self:__PlayerEjected(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + -- Dead, Crash, Suicide + if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then + self:__PlayerDied(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + end + return self +end + +--- Block a player. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client CLIENT object. +-- @param #string PlayerName (optional) Name of the player. +-- @param #number Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:BlockPlayer(Client,PlayerName,Seconds,Message) + local name + if Client then + name = CLIENT:GetPlayerName() + elseif PlayerName then + name = PlayerName + else + self:F(self.lid.."Block: No PlayerName given or not found!") + return self + end + local addon = Seconds or self.BlockTime + self.BlockedPilots[name] = timer.getTime()+addon + local message = Message or self.BlockMessage + if Client then + self:SendChatToPlayer(message,Client) + else + self:SendChat(name..": "..message) + end + self:__PlayerBlocked(1,Client,name,Seconds) + self:ReturnToSpectators(Client) + return self +end + +--- Unblock a player. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client CLIENT object +-- @param #string PlayerName (optional) Name of the player. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:UnblockPlayer(Client,PlayerName,Message) + local name + if Client then + name = CLIENT:GetPlayerName() + elseif PlayerName then + name = PlayerName + else + self:F(self.lid.."Unblock: No PlayerName given or not found!") + return self + end + self.BlockedPilots[name] = nil + local message = Message or self.UnblockMessage + if Client then + self:SendChatToPlayer(message,Client) + else + self:SendChat(name..": "..message) + end + self:__PlayerUnblocked(1,Client,name) + return self +end + +function NET:SetBlockMessage(Text) + self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!" + return self +end + +function NET:SetBlockTime(Seconds) + self.BlockTime = Seconds or 600 + return self +end + +function NET:SetUnblockMessage(Text) + self.UnblockMessage = Text or "You are unblocked now and can join again." return self end From f85c0320ec5284941018d6d22f2143e86263cc8b Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 16 Feb 2023 17:09:12 +0100 Subject: [PATCH 6/7] Airspeed **POSITIONABLE** - Added function `GetAirspeedIndicated` to return IAS - Added function `GetAirspeedTrue` to return TAS **UTILS** - Added function `UTILS.IasToTas` to convert IAS to TAS - Added function `TasToIas` to convert TAS to IAS. **POINT** - Added function `COORDINATE:GetWindVec3` --- Moose Development/Moose/Core/Point.lua | 57 ++++++++++++++----- Moose Development/Moose/Utilities/Utils.lua | 25 ++++++++ .../Moose/Wrapper/Positionable.lua | 52 +++++++++++++++++ 3 files changed, 119 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 0a9d28e25..23172a347 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1054,28 +1054,55 @@ do -- COORDINATE return heading end + --- Returns the 3D wind direction vector. Note that vector points into the direction the wind in blowing to. + -- @param #COORDINATE self + -- @param #number height (Optional) parameter specifying the height ASL in meters. The minimum height will be always be the land height since the wind is zero below the ground. + -- @param #boolean turbulence (Optional) If `true`, include turbulence. + -- @return DCS#Vec3 Wind 3D vector. Components in m/s. + function COORDINATE:GetWindVec3(height, turbulence) + + -- We at 0.1 meters to be sure to be above ground since wind is zero below ground level. + local landheight=self:GetLandHeight()+0.1 + + local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z} + + -- Get wind velocity vector. + local wind = nil --DCS#Vec3 + + if turbulence then + wind = atmosphere.getWindWithTurbulence(point) + else + wind = atmosphere.getWind(point) + end + + return wind + end + --- Returns the wind direction (from) and strength. -- @param #COORDINATE self - -- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. - -- @return Direction the wind is blowing from in degrees. - -- @return Wind strength in m/s. - function COORDINATE:GetWind(height) - local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level. - local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z} - -- get wind velocity vector - local wind = atmosphere.getWind(point) - local direction = math.deg(math.atan2(wind.z, wind.x)) - if direction < 0 then - direction = 360 + direction - end - -- Convert to direction to from direction + -- @param #number height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. + -- @param #boolean turbulence If `true`, include turbulence. If `false` or `nil`, wind without turbulence. + -- @return #number Direction the wind is blowing from in degrees. + -- @return #number Wind strength in m/s. + function COORDINATE:GetWind(height, turbulence) + + -- Get wind velocity vector + local wind = self:GetWindVec3(height, turbulence) + + -- Calculate the direction of the vector. + local direction=UTILS.VecHdg(wind) + + -- Invert "to" direction to "from" direction. if direction > 180 then direction = direction-180 else direction = direction+180 end - local strength=math.sqrt((wind.x)^2+(wind.z)^2) - -- Return wind direction and strength km/h. + + -- Wind strength in m/s. + local strength=UTILS.VecNorm(wind) -- math.sqrt((wind.x)^2+(wind.z)^2) + + -- Return wind direction and strength. return direction, strength end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index d81856f3b..7d3efe12f 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -489,6 +489,31 @@ UTILS.hPa2inHg = function( hPa ) return hPa * 0.0295299830714 end +--- Convert indicated airspeed (IAS) to true airspeed (TAS) for a given altitude above main sea level. +-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level. +-- @param #number ias Indicated air speed in any unit (m/s, km/h, knots, ...) +-- @param #number altitude Altitude above main sea level in meters. +-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017. +-- @return #number True airspeed in the same unit the IAS has been given. +UTILS.IasToTas = function( ias, altitude, oatcorr ) + oatcorr=oatcorr or 0.017 + local tas=ias + (ias * oatcorr * UTILS.MetersToFeet(altitude) / 1000) + return tas +end + +--- Convert true airspeed (TAS) to indicated airspeed (IAS) for a given altitude above main sea level. +-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level. +-- @param #number tas True air speed in any unit (m/s, km/h, knots, ...) +-- @param #number altitude Altitude above main sea level in meters. +-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017. +-- @return #number Indicated airspeed in the same unit the TAS has been given. +UTILS.TasToIas = function( tas, altitude, oatcorr ) + oatcorr=oatcorr or 0.017 + local ias=tas/(1+oatcorr*UTILS.MetersToFeet(altitude)/1000) + return ias +end + + --- Convert knots to altitude corrected KIAS, e.g. for tankers. -- @param #number knots Speed in knots. -- @param #number altitude Altitude in feet diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 72f9a9fce..f47d888d8 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -845,6 +845,58 @@ function POSITIONABLE:GetVelocityKNOTS() return UTILS.MpsToKnots( self:GetVelocityMPS() ) end +--- Returns the true airspeed (TAS). This is calculated from the current velocity minus wind in 3D. +-- @param #POSITIONABLE self +-- @return #number TAS in m/s. Returns 0 if the POSITIONABLE does not exist. +function POSITIONABLE:GetAirspeedTrue() + + -- TAS + local tas=0 + + -- Get current coordinate. + local coord=self:GetCoord() + + if coord then + + -- Altitude in meters. + local alt=coord.y + + -- Wind velocity vector. + local wvec3=coord:GetWindVec3(alt, false) + + -- Velocity vector. + local vvec3=self:GetVelocityVec3() + + --GS=TAS+WIND ==> TAS=GS-WIND + local tasvec3=UTILS.VecSubstract(vvec3, wvec3) + + -- True airspeed in m/s + tas=UTILS.VecNorm(tasvec3) + + end + + return tas +end + +--- Returns the indicated airspeed (IAS). +-- The IAS is calculated from the TAS under the approximation that TAS increases by ~2% with every 1000 feet altitude ASL. +-- @param #POSITIONABLE self +-- @param #number oatcorr (Optional) Outside air temperature (OAT) correction factor. Default 0.017 (=1.7%). +-- @return #number IAS in m/s. Returns 0 if the POSITIONABLE does not exist. +function POSITIONABLE:GetAirspeedIndicated(oatcorr) + + -- Get true airspeed. + local tas=self:GetAirspeedTrue() + + -- Get altitude. + local altitude=self:GetAltitude() + + -- Convert TAS to IAS. + local ias=UTILS.TasToIas(tas, altitude, oatcorr) + + return ias +end + --- Returns the Angle of Attack of a POSITIONABLE. -- @param #POSITIONABLE self -- @return #number Angle of attack in degrees. From 3b1b57a33e128975a9cf6946a6bf9dbd33d148cf Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 16 Feb 2023 17:20:53 +0100 Subject: [PATCH 7/7] Update Positionable.lua - Added GetGroundSpeed --- .../Moose/Wrapper/Positionable.lua | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index f47d888d8..082977785 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -897,6 +897,26 @@ function POSITIONABLE:GetAirspeedIndicated(oatcorr) return ias end +--- Returns the horizonal speed relative to eath's surface. The vertical component of the velocity vector is projected out (set to zero). +-- @param #POSITIONABLE self +-- @return #number Ground speed in m/s. Returns 0 if the POSITIONABLE does not exist. +function POSITIONABLE:GetGroundSpeed() + + local gs=0 + + local vel=self:GetVelocityVec3() + + if vel then + + local vec2={x=vel.x, y=vel.z} + + gs=UTILS.Vec2Norm(vel) + + end + + return gs +end + --- Returns the Angle of Attack of a POSITIONABLE. -- @param #POSITIONABLE self -- @return #number Angle of attack in degrees.