Merge pull request #1125 from FlightControl-Master/FF/Develop

AIRBOSS v0.9.9.1
This commit is contained in:
Frank 2019-02-28 23:15:12 +01:00 committed by GitHub
commit d4510af249
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 190 additions and 56 deletions

View File

@ -436,6 +436,11 @@
-- This command toggles the display of radio message subtitles if no radio relay unit is used. By default subtitles are on.
-- Note that subtitles for radio messages which do not have a complete voice over are always displayed.
--
-- ### Trapsheet On/Off
--
-- Each player can activated or deactivate the recording of his flight data (AoA, glideslope, lineup, etc.) during his landing approaches.
-- Note that this feature also has to be enabled by the mission designer.
--
-- ## Kneeboard Menu
--
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_MenuKneeboard.png)
@ -589,7 +594,7 @@
-- Furthermore, we have the cases:
--
-- * 2.5 Points **B**: "Bolder", when the player landed but did not catch a wire.
-- * 2.0 Points **PWO**: "Pattern Wave-Off", when pilot was far away from where he should be in the pattern.
-- * 2.0 Points **WOP**: "Pattern Wave-Off", when pilot was far away from where he should be in the pattern.
-- * 2.0 Points **OWO**: "Own Wave-Off**, when pilot flies past the deck without touching it.
-- * 1.0 Points **WO**: "Technique Wave-Off": Player got waved off in the final parts of the groove.
-- * 1.0 Points **LIG**: "Long In the Groove", when pilot extents the downwind leg too far and screws up the timing for the following aircraft.
@ -819,13 +824,31 @@
--
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_TrapSheetLUE.png)
--
-- The graph displayes the lineup error as a function of the distance to the carrier.
-- The graph displays the lineup error (LUE) as a function of the distance to the carrier.
--
-- The pilot approaches the carrier from the port side, LUE>0°, at a distance of ~1 NM.
-- At the beginning of the groove (X), he significantly overshoots to the starboard side (LUE<5°).
-- In the middle (IM), he performs good corrections and smoothly reduces the lineup error.
-- Finally, at a distance of ~0.3 NM (IC) he has corrected his lineup with the runway to a reasonable level, |LUE|<0.5°.
--
-- ## Glideslope Error
--
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_TrapSheetGSE.png)
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_TrapSheetGLE.png)
--
-- -- The graph displayes the glideslope error as a function of the distance to the carrier.
-- The graph displays the glideslope error (GSE) as a function of the distance to the carrier.
--
-- In this case the pilot already enters the groove (X) below the optimal glideslope. He is not able to correct his height in the IM part and
-- stays significantly too low. In close, he performs a harsh correction to gain altitude and ends up even slightly too high (GSE>0.5°).
-- At his point further corrections are necessary.
--
-- ## Angle of Attack
--
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_TrapSheetAoA.png)
--
-- The graph displays the angle of attack (AoA) as a function of the distance to the carrier.
--
-- The pilot starts off being on speed after the ball call. Then he get way to fast troughout the most part of the groove. He manages to correct
-- this somewhat short before touchdown.
--
-- ===
--
@ -1453,6 +1476,9 @@ AIRBOSS.Difficulty={
-- @field #number Vel Total velocity in m/s.
-- @field #number Vy Vertical velocity in m/s.
-- @field #number Gamma Relative heading player to carrier's runway. 0=parallel, +-90=perpendicular.
-- @field #string Grade LSO grade.
-- @field #number GradePoints LSO grade points
-- @field #string GradeDetail LSO grade details.
-- @field #string FlyThrough Fly through up "/" or fly through down "\\".
--- LSO grade data.
@ -1560,7 +1586,7 @@ AIRBOSS.MenuF10Root=nil
--- Airboss class version.
-- @field #string version
AIRBOSS.version="0.9.9"
AIRBOSS.version="0.9.9.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1802,7 +1828,7 @@ function AIRBOSS:New(carriername, alias)
-- Debug trace.
if false then
--self.Debug=true
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
@ -2524,8 +2550,12 @@ end
-- @param #string path (Optional) Path where to save the trap sheets.
-- @return #AIRBOSS self
function AIRBOSS:SetTrapSheet(path)
self.trapsheet=true
self.trappath=path
if io then
self.trapsheet=true
self.trappath=path
else
self:E(self.lid.."ERROR: io is not desanitized. Cannot save trap sheet.")
end
return self
end
@ -6782,8 +6812,14 @@ end
-- @param Wrapper.Unit#UNIT unit The aircraft unit that was recovered.
-- @return #AIRBOSS.FlightGroup Flight group of element.
function AIRBOSS:_RecoveredElement(unit)
local element, idx, flight=self:_GetFlightElement(unit:GetName()) --#AIRBOSS.FlightElement
element.recovered=true
-- Get element of flight.
local element, idx, flight=self:_GetFlightElement(unit:GetName()) --#AIRBOSS.FlightElement
-- Nil check. Could be if a helo landed or something else we dont know!
if element then
element.recovered=true
end
return flight
end
@ -8598,7 +8634,10 @@ function AIRBOSS:_GetGrooveData(playerData)
end
-- Velocity vector.
local vel=playerData.unit:GetVelocityVec3()
local vel=playerData.unit:GetVelocityVec3()
-- Grade, points, details
local Gg,Gp,Gd=self:_LSOgrade(playerData)
-- Gather pilot data.
local groovedata={} --#AIRBOSS.GrooveData
@ -8617,6 +8656,9 @@ function AIRBOSS:_GetGrooveData(playerData)
groovedata.Vel=UTILS.VecNorm(vel)
groovedata.Vy=vel.y
groovedata.Gamma=self:_GetRelativeHeading(playerData.unit, true)
groovedata.Grade=Gg
groovedata.GradePoints=Gp
groovedata.GradeDetail=Gd
return groovedata
end
@ -8860,6 +8902,7 @@ function AIRBOSS:_Groove(playerData)
-- Get current groove data.
local gd=playerData.groove[gs] --#AIRBOSS.GrooveData
if gd then
self:T3(gd)
@ -10282,12 +10325,13 @@ end
-- @param #AIRBOSS self
-- @param #number alt Altitude ASL in meters. Default 50 m.
-- @param #boolean magnetic Direction including magnetic declination.
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
-- @return #number Direction the wind is blowing **from** in degrees.
-- @return #number Wind speed in m/s.
function AIRBOSS:GetWind(alt, magnetic)
function AIRBOSS:GetWind(alt, magnetic, coord)
-- Current position of the carrier
local cv=self:GetCoordinate()
-- Current position of the carrier or input.
local cv=coord or self:GetCoordinate()
-- Wind direction and speed. By default at 50 meters ASL.
local Wdir, Wspeed=cv:GetWind(alt or 50)
@ -10304,48 +10348,61 @@ function AIRBOSS:GetWind(alt, magnetic)
return Wdir, Wspeed
end
--- Get wind direction and speed on carrier deck.
--- Get wind speed on carrier deck parallel and perpendicular to runway.
-- @param #AIRBOSS self
-- @param #number alt Altitude in meters. Default 50 m.
-- @return #number Direction the wind is blowing **from** in degrees.
-- @return #number Wind speed in m/s.
-- @return #number Wind component parallel to runway im m/s.
-- @return #number Wind component perpendicular to runway in m/s.
-- @return #number Total wind strength in m/s.
function AIRBOSS:GetWindOnDeck(alt)
-- Position of carrier
-- Position of carrier.
local cv=self:GetCoordinate()
-- Velocity vector of carrier.
local vc=self.carrier:GetVelocityVec3()
-- Rotate to angled deck.
vc=UTILS.Rotate2D(vc, self.carrierparam.rwyangle)
-- Carrier orientation X.
local xc=self.carrier:GetOrientationX()
-- Carrier orientation Z.
local zc=self.carrier:GetOrientationZ()
-- Rotate back so that angled deck points to wind.
xc=UTILS.Rotate2D(xc, -self.carrierparam.rwyangle)
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
-- Wind (from) vector
local vw=cv:GetWindWithTurbulenceVec3(alt or 50)
-- Total wind velocity vector.
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
local vd=UTILS.VecSubstract(vw, vc)
local vh=math.deg(math.atan2(vd.z, vd.x))
if vh<0 then
vh=vh+360
end
local vT=UTILS.VecSubstract(vw, vc)
-- Strength.
local vabs=UTILS.VecNorm(vd)
-- || Parallel component.
local vpa=UTILS.VecDot(vT,xc)
return vh, vabs
-- == Perpendicular component.
local vpp=UTILS.VecDot(vT,zc)
-- Strength.
local vabs=UTILS.VecNorm(vT)
-- We return positive values as head wind and negative values as tail wind.
--TODO: Check minus sign.
return -vpa, vpp, vabs
end
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
-- @param #AIRBOSS self
-- @param #boolean magnetic If true, calculate magnetic heading. By default true heading is returned.
-- @param Core.Point#COORDINATE coord (Optional) Coodinate from which heading is calculated. Default is current carrier position.
-- @return #number Carrier heading in degrees.
function AIRBOSS:GetHeadingIntoWind(magnetic)
function AIRBOSS:GetHeadingIntoWind(magnetic, coord)
-- Get direction the wind is blowing from. This is where we want to go.
local windfrom, vwind=self:GetWind()
local windfrom, vwind=self:GetWind(nil, nil, coord)
-- Actually, we want the runway in the wind.
local intowind=windfrom-self.carrierparam.rwyangle
@ -11686,7 +11743,7 @@ function AIRBOSS:_Debrief(playerData)
mygrade.finalscore=Points
end
mygrade.case=playerData.case
local _,windondeck=self:GetWindOnDeck()
local windondeck=self:GetWindOnDeck()
mygrade.wind=tostring(UTILS.Round(UTILS.MpsToKnots(windondeck), 1))
mygrade.modex=playerData.onboard
mygrade.airframe=playerData.actype
@ -12082,8 +12139,9 @@ end
-- @param #number speed Speed in knots. Default is current carrier velocity.
-- @param #boolean uturn (Optional) If true, carrier will go back to where it came from before it resumes its route to the next waypoint.
-- @param #number uspeed Speed in knots after U-turn. Default is same as before.
-- @param Core.Point#COORDINATE tcoord Additional coordinate to make turn smoother.
-- @return #AIRBOSS self
function AIRBOSS:CarrierDetour(coord, speed, uturn, uspeed)
function AIRBOSS:CarrierDetour(coord, speed, uturn, uspeed, tcoord)
-- Current coordinate of the carrier.
local pos0=self:GetCoordinate()
@ -12094,17 +12152,16 @@ function AIRBOSS:CarrierDetour(coord, speed, uturn, uspeed)
-- Speed in km/h.
local speedkmh=UTILS.KnotsToKmph(speed)
local cspeedkmh=self.carrier:GetVelocityKMH()
local uspeedkmh=UTILS.KnotsToKmph(uspeed)
local uspeedkmh=UTILS.KnotsToKmph(uspeed or speed)
-- Waypoint table.
local wp={}
-- Pos1 is a bit into.
local pos1=pos0:Translate(500, pos0:HeadingTo(coord))
-- Create from/to waypoints.
table.insert(wp, pos0:WaypointGround(cspeedkmh))
table.insert(wp, pos1:WaypointGround(cspeedkmh))
if tcoord then
table.insert(wp, tcoord:WaypointGround(cspeedkmh))
end
table.insert(wp, coord:WaypointGround(speedkmh))
-- If enabled, go back to where you came from.
@ -12123,6 +12180,9 @@ function AIRBOSS:CarrierDetour(coord, speed, uturn, uspeed)
-- Debug mark.
if self.Debug then
if tcoord then
tcoord:MarkToAll("Detour Tcoord")
end
coord:MarkToAll("Detour Point")
end
@ -12158,15 +12218,72 @@ function AIRBOSS:CarrierTurnIntoWind(time, vdeck, uturn)
self:I(self.lid..string.format("Carrier steaming into the wind (%.1f kts). Distance=%.1f NM, Speed=%.1f knots, Time=%d sec.", UTILS.MpsToKnots(vwind), distNM, speedknots, time))
-- Get heading into the wind accounting for angled runway.
local intowind=self:GetHeadingIntoWind(false)
-- Translate current position.
local pos1=self:GetCoordinate():Translate(dist, intowind)
local hiw=self:GetHeadingIntoWind()
-- Current heading.
local hdg=self:GetHeading()
-- Heading difference.
local deltaH=self:_GetDeltaHeading(hdg, hiw)
local Cv=self:GetCoordinate()
local Ctiw=nil --Core.Point#COORDINATE
local Csoo=nil --Core.Point#COORDINATE
-- Define path depending on turn angle.
if deltaH<45 then
-- Small turn.
-- Point in the right direction to help turning.
Csoo=Cv:Translate(750, hdg):Translate(750, hiw)
-- Heading into wind from Csoo.
local hsw=self:GetHeadingIntoWind(false, Csoo)
-- Into the wind coord.
Ctiw=Csoo:Translate(dist, hsw)
elseif deltaH<90 then
-- Medium turn.
-- Point in the right direction to help turning.
Csoo=Cv:Translate(900, hdg):Translate(900, hiw)
-- Heading into wind from Csoo.
local hsw=self:GetHeadingIntoWind(false, Csoo)
-- Into the wind coord.
Ctiw=Csoo:Translate(dist, hsw)
elseif deltaH<135 then
-- Large turn backwards.
-- Point in the right direction to help turning.
-- TODO turn left or right?
Csoo=Cv:Translate(1200, hdg-90):Translate(1200, hiw)
-- Heading into wind from Csoo.
local hsw=self:GetHeadingIntoWind(false, Csoo)
-- Into the wind coord.
Ctiw=Csoo:Translate(dist, hsw)
else
-- Huge turn backwards.
-- Point in the right direction to help turning.
-- TODO turn left or right?
Csoo=Cv:Translate(1200, hdg-90):Translate(1200, hiw)
-- Heading into wind from Csoo.
local hsw=self:GetHeadingIntoWind(false, Csoo)
-- Into the wind coord.
Ctiw=Csoo:Translate(dist, hsw)
-- Debug mark.
if self.Debug then
pos1:MarkToAll("Into the wind point")
end
-- Return to coordinate if collision is detected.
self.Creturnto=self:GetCoordinate()
@ -12178,7 +12295,7 @@ function AIRBOSS:CarrierTurnIntoWind(time, vdeck, uturn)
local vdownwind=UTILS.MpsToKnots(nextwp:GetVelocity())
-- Let the carrier make a detour from its route but return to its current position.
self:CarrierDetour(pos1, speedknots, uturn, vdownwind)
self:CarrierDetour(Ctiw, speedknots, uturn, vdownwind, Csoo)
-- Set switch that we are currently turning into the wind.
self.turnintowind=true
@ -15190,7 +15307,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
end
-- Wind on flight deck
local wind=UTILS.MpsToKnots(select(2, self:GetWindOnDeck()))
local wind=UTILS.MpsToKnots(select(1, self:GetWindOnDeck()))
-- Get groups, units in queues.
local Nmarshal,nmarshal = self:_GetQueueInfo(self.Qmarshal, playerData.case)
@ -15313,8 +15430,10 @@ function AIRBOSS:_DisplayCarrierWeather(_unitname)
-- Get Beaufort wind scale.
local Bn,Bd=UTILS.BeaufortScale(Ws)
-- Wind on flight deck
local Wod=UTILS.MpsToKnots(select(2, self:GetWindOnDeck()))
-- Wind on flight deck.
local WodPA,WodPP=self:GetWindOnDeck()
local WodPA=UTILS.MpsToKnots(WodPA)
local WodPP=UTILS.MpsToKnots(WodPP)
local WD=string.format('%03d°', Wd)
local Ts=string.format("%d°C",T)
@ -15322,13 +15441,13 @@ function AIRBOSS:_DisplayCarrierWeather(_unitname)
local tT=string.format("%d°C",T)
local tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws))
local tP=string.format("%.2f inHg", UTILS.hPa2inHg(P))
-- Report text.
text=text..string.format("Weather Report at Carrier %s:\n", self.alias)
text=text..string.format("================================\n")
text=text..string.format("================================\n")
text=text..string.format("Temperature %s\n", tT)
text=text..string.format("Wind from %s at %s (%s)\n", WD, tW, Bd)
text=text..string.format("Wind on deck %.1f knots\n", Wod)
text=text..string.format("Wind on deck || %.1f kts, == %.1f kts\n", WodPA, WodPP)
text=text..string.format("QFE %.1f hPa = %s", P, tP)
-- More info only reliable if Mission uses static weather.
@ -15935,7 +16054,7 @@ function AIRBOSS:_SaveTrapSheet(playerData, grade)
self:I(self.lid..text)
-- Header line
local data="#Time,Rho,X,Z,Alt,AoA,GSE,LUE,Vtot,Vy,Gamma,Pitch,Roll,Yaw,Step\n"
local data="#Time,Rho,X,Z,Alt,AoA,GSE,LUE,Vtot,Vy,Gamma,Pitch,Roll,Yaw,Step,Grade,Points,Details\n"
local g0=playerData.trapsheet[1] --#AIRBOSS.GrooveData
local T0=g0.Time
@ -15956,9 +16075,12 @@ function AIRBOSS:_SaveTrapSheet(playerData, grade)
local k=groove.Pitch
local l=groove.Roll
local m=groove.Yaw
local n=groove.Step
-- t a b c d e f g h i j k l m n
data=data..string.format("%.2f,%.3f,%.1f,%.1f,%.1f,%.2f,%.2f,%.2f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%s\n",t,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
local n=self:_GS(groove.Step, -1)
local o=groove.Grade
local p=groove.GradePoints
local q=groove.GradeDetail
-- t a b c d e f g h i j k l m n o p q
data=data..string.format("%.2f,%.3f,%.1f,%.1f,%.1f,%.2f,%.2f,%.2f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%s,%s,%.1f,%s\n",t,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q)
end
-- Save file.

View File

@ -799,6 +799,18 @@ function UTILS.VecAngle(a, b)
return math.deg(alpha)
end
--- Calculate "heading" of a 3D vector in the X-Z plane.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @return #number Heading in degrees in [0,360).
function UTILS.VecHdg(a)
local h=math.deg(math.atan2(a.z, a.x))
if h<0 then
h=h+360
end
return h
end
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number angle Rotation angle in degrees.