From 7453a6c55d49e6c8231af2599438a7c9ee03a9e9 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 26 Sep 2023 17:02:13 +0200 Subject: [PATCH] navygroup - into wind --- Moose Development/Moose/Navigation/Point.lua | 183 +++++++++++-------- Moose Development/Moose/Ops/NavyGroup.lua | 109 +++++++++-- 2 files changed, 201 insertions(+), 91 deletions(-) diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua index d0d06ace7..770ef0a15 100644 --- a/Moose Development/Moose/Navigation/Point.lua +++ b/Moose Development/Moose/Navigation/Point.lua @@ -180,7 +180,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- NAVFIX class. -- @type NAVFIX -- @field #string ClassName Name of the class. @@ -283,59 +282,6 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set minimum altitude. --- @param #NAVFIX self --- @param #number Altitude Min altitude in feet. --- @return #NAVFIX self -function NAVFIX:SetAltMin(Altitude) - - self.altMin=Altitude - - return self -end - ---- Set maximum altitude. --- @param #NAVFIX self --- @param #number Altitude Max altitude in feet. --- @return #NAVFIX self -function NAVFIX:SetAltMax(Altitude) - - self.altMax=Altitude - - return self -end - - ---- Set mandatory altitude (min alt = max alt). --- @param #NAVFIX self --- @param #number Altitude Altitude in feet. --- @return #NAVFIX self -function NAVFIX:SetAltMandatory(Altitude) - - self.altMin=Altitude - self.altMax=Altitude - - return self -end - ---- Set whether this fix is compulsory. --- @param #NAVFIX self --- @param #boolean Compulsory If `true`, this is a compusory fix. If `false` or nil, it is non-compulsory. --- @return #NAVFIX self -function NAVFIX:SetCompulsory(Compulsory) - self.isCompulsory=Compulsory - return self -end - ---- Set whether this is a fly-over fix fix. --- @param #NAVFIX self --- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not. --- @return #NAVFIX self -function NAVFIX:SetFlyOver(FlyOver) - self.isFlyover=FlyOver - return self -end - --- Set whether this is the intermediate fix (IF). -- @param #NAVFIX self -- @param #boolean IntermediateFix If `true`, this is an intermediate fix. @@ -378,24 +324,6 @@ function NAVFIX:SetMissedApproachFix(MissedApproachFix) return self end - ---- Get the altitude in feet MSL. --- @param #NAVFIX self --- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set. -function NAVFIX:GetAltitude() - - local alt=nil - if self.altMin and self.altMax then - alt=math.random(self.altMin, self.altMax) - elseif self.altMin then - alt=self.altMin - elseif self.altMax then - alt=self.altMax - end - - return alt -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Private Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -438,6 +366,9 @@ end -- @field #string name Name of the point. -- @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. +-- @field #number altMax Maximum altitude in meters. +-- -- @field #boolean isCompulsory Is this a compulsory fix. -- -- @extends Core.Base#BASE @@ -489,11 +420,16 @@ function NAVPOINT:NewFromVector(Name, Vector) -- Inherit everything from BASE class. self=BASE:Inherit(self, BASE:New()) -- #NAVFIX + -- Vector of point. self.vector=Vector + -- Name of point. self.name=Name - --self.marker=MARKER:New(Coordinate, self:_GetMarkerText()) + -- Marker on F10. + self.marker=MARKER:New(Vector:GetCoordinate(true), self:_GetMarkerText()) + + --self.marker:ToAll() return self @@ -529,9 +465,9 @@ function NAVPOINT:NewFromNavPoint(Name, NavPoint, Distance, Bearing, Reciprocal) end -- Translate. - local coord=NavPoint.coordinate:Translate(UTILS.NMToMeters(Distance), Bearing) + local Vector=NavPoint.vector:Translate(UTILS.NMToMeters(Distance), Bearing) - local self=NavPoint:NewFromCoordinate(Name, coord) + self=NavPoint:NewFromVector(Name, Vector) return self end @@ -540,13 +476,106 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- No user functions yet. +--- Set minimum altitude. +-- @param #NAVPOINT self +-- @param #number Altitude Min altitude in feet. +-- @return #NAVPOINT self +function NAVPOINT:SetAltMin(Altitude) + + self.altMin=Altitude + + return self +end + +--- Set maximum altitude. +-- @param #NAVPOINT self +-- @param #number Altitude Max altitude in feet. +-- @return #NAVPOINT self +function NAVPOINT:SetAltMax(Altitude) + + self.altMax=Altitude + + return self +end + + +--- Set mandatory altitude (min alt = max alt). +-- @param #NAVPOINT self +-- @param #number Altitude Altitude in feet. +-- @return #NAVPOINT self +function NAVPOINT:SetAltMandatory(Altitude) + + self.altMin=Altitude + self.altMax=Altitude + + return self +end + +--- Set whether this fix is compulsory. +-- @param #NAVPOINT self +-- @param #boolean Compulsory If `true`, this is a compusory fix. If `false` or nil, it is non-compulsory. +-- @return #NAVPOINT self +function NAVPOINT:SetCompulsory(Compulsory) + self.isCompulsory=Compulsory + return self +end + +--- Set whether this is a fly-over fix fix. +-- @param #NAVPOINT self +-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not. +-- @return #NAVPOINT self +function NAVPOINT:SetFlyOver(FlyOver) + self.isFlyover=FlyOver + return self +end + + +--- Get the altitude in feet MSL. +-- @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 + alt=math.random(self.altMin, self.altMax) + elseif self.altMin then + alt=self.altMin + elseif self.altMax then + alt=self.altMax + end + + return alt +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Private Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- No private functions yet. +--- Get text displayed in the F10 marker. +-- @param #NAVPOINT self +-- @return #string Marker text. +function NAVPOINT:_GetMarkerText() + + local altmin=self.altMin and tostring(self.altMin) or "" + local altmax=self.altMax and tostring(self.altMax) or "" + local speedmin=self.speedMin and tostring(self.speedMin) or "" + local speedmax=self.speedMax and tostring(self.speedMax) or "" + + + local text=string.format("NAVPOINT %s", self.name) + if self.isIAF then + text=text..string.format(" (IAF)") + end + if self.isIF then + text=text..string.format(" (IF)") + end + text=text..string.format("\nAltitude: %s - %s", altmin, altmax) + text=text..string.format("\nSpeed: %s - %s", speedmin, speedmax) + text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory)) + text=text..string.format("\nFly Over: %s", tostring(self.isFlyover)) + + return text +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index a69e5f627..41f29f188 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1277,25 +1277,20 @@ end -- @param #NAVYGROUP.IntoWind Into wind parameters. function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind) - IntoWind.Heading=self:GetHeadingIntoWind(IntoWind.Offset) + -- Calculate heading and speed of ship. + local heading, speed=self:GetHeadingIntoWind(IntoWind.Offset, IntoWind.Speed) + IntoWind.Heading=heading IntoWind.Open=true + -- Get coordinate. IntoWind.Coordinate=self:GetCoordinate(true) + -- Set current into wind parameters. self.intowind=IntoWind - - -- Wind speed in m/s. - local _,vwind=self:GetWind() - - -- Convert to knots. - vwind=UTILS.MpsToKnots(vwind) - - -- Speed of carrier relative to wind but at least 4 knots. - local speed=math.max(IntoWind.Speed-vwind, 4) -- Debug info. - self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop)) + self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f, Tstart=%d Tstop=%d", IntoWind.Heading, speed, IntoWind.Tstart, IntoWind.Tstop)) local distance=UTILS.NMToMeters(1000) @@ -2072,7 +2067,7 @@ end -- @param #NAVYGROUP self -- @param #number Offset Offset angle in degrees, e.g. to account for an angled runway. -- @return #number Carrier heading in degrees. -function NAVYGROUP:GetHeadingIntoWind(Offset) +function NAVYGROUP:GetHeadingIntoWind_old(Offset) Offset=Offset or 0 @@ -2086,15 +2081,101 @@ function NAVYGROUP:GetHeadingIntoWind(Offset) if vwind<0.1 then intowind=self:GetHeading() end - + -- Adjust negative values. if intowind<0 then intowind=intowind+360 end - + return intowind end + +--- Get heading of group into the wind. This minimizes the cross wind for an angled runway. +-- Implementation based on [Mags & Bami](https://magwo.github.io/carrier-cruise/) work. +-- @param #NAVYGROUP self +-- @param #number Offset Offset angle in degrees, e.g. to account for an angled runway. +-- @param #number vdeck Desired wind speed on deck in Knots. +-- @return #number Carrier heading in degrees. +function NAVYGROUP:GetHeadingIntoWind(Offset, vdeck) + + -- Default offset angle. + Offset=Offset or 0 + + -- Get direction the wind is blowing from. + local windfrom, vwind=self:GetWind() + + -- Convert wind speed to knots. + vwind=UTILS.MpsToKnots(vwind) + + -- Wind to in knots. + local windto=(windfrom+180)%360 + + -- Offset angle in rad. + local alpha=math.rad(Offset) + + -- Ships min/max speed. + local Vmin=4 + local Vmax=UTILS.KmphToKnots(self.speedMax) + + -- Constant. + local C = math.sqrt(math.cos(alpha)^2 / math.sin(alpha)^2 + 1) + + + -- Upper limit of desired speed due to max boat speed. + local vdeckMax=vwind + math.cos(alpha) * Vmax + + -- Lower limit of desired speed due to min boat speed. + local vdeckMin=vwind + math.cos(alpha) * Vmin + + + -- Speed of ship so it matches the desired speed. + local v + + -- Angle wrt. to wind TO-direction + local theta + + if vdeck>vdeckMax then + -- Boat cannot go fast enough + + -- Set max speed. + v=Vmax + + -- Calculate theta. + theta = math.asin(v/(vwind*C)) - math.asin(-1/C) + + elseif vdeckvwind then + -- Too little wind + + -- Set theta to 90° + theta=math.pi/2 + + -- Set speed. + v = math.sqrt(vdeck^2 - vwind^2) + + else + -- Normal case + theta = math.asin(vdeck * math.sin(alpha) / vwind) + v = vdeck * math.cos(alpha) - vwind * math.cos(theta) + end + + + -- Ship heading so cross wind is min for the given wind. + local intowind = (540 + (windto + math.deg(theta) )) % 360 + + return intowind, v +end + + --- Find free path to next waypoint. -- @param #NAVYGROUP self -- @return #boolean If true, a path was found.