This commit is contained in:
funkyfranky
2019-05-09 16:27:54 +02:00
parent a73faca149
commit b795d2fd09
2 changed files with 195 additions and 40 deletions
@@ -1,14 +1,20 @@
--- **Functional** - (R2.5) - Monitor and export flight model data. --- **Functional** - (R2.5) - Monitor and export flight model data.
-- --
-- Record flight data and export it to a csv file.
-- --
-- ## Recorded Data:
-- --
-- RAT2 creates random air traffic on the map. -- * Time,
-- -- * Altitude,
-- -- * Temperature,
-- -- * Pressure,
-- **Main Features:** -- * Velocity (total and x,y,z components),
-- -- * Acceleration (total and x,y,z components),
-- * It's very random. -- * AoA,
-- * Pitch and pitch rate,
-- * Roll and roll rate,
-- * Yaw and yaw rate,
-- * Turn rate.
-- --
-- === -- ===
-- --
@@ -27,15 +33,35 @@
-- @field #string savepath Path to save data files. -- @field #string savepath Path to save data files.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Be surprised! --- Be sure!
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\RAT2\RAT2_Main.png) -- ![Banner Image](..\Presentations\FMD\FMD_Main.png)
-- --
-- # The RAT2 Concept -- # The FMD Concept
-- --
-- This class can be used to record flight data such as velocity, acceleration, pitch, roll, yaw and turn rates. The output is written to a csv file for later analysis.
-- --
-- # Usage
--
-- The script is very easy to use. It only requires the line
--
-- fmd=FMD:New()
--
-- This will automatically start the FMD script.
--
-- Each player will get an entry "FMD" in the F-10 radio menu. There he can start or stop the data recording.
--
-- **IMPORTANT**
-- Due to a DCS bug, it is necessary that (in single player mode), player/clients have to **hit ESC twice** before entering an aircraft client slot.
-- Otherwise, the script will not load and now menus will be created for the player.
--
-- # Output
--
-- After the data recording is completed, the data is written to a csv file. The file name starts with **FMD** and contains the employed airframe plus a running number.
--
-- However, one must desanitize the **io** and **lfs** lines the DCS root directory. Otherwise, DCS will not allow data to be written to file.
-- --
-- @field #FMD -- @field #FMD
FMD = { FMD = {
@@ -52,9 +78,12 @@ FMD = {
-- @field Core.Scheduler#SCHEDULER scheduler Scheduler -- @field Core.Scheduler#SCHEDULER scheduler Scheduler
-- @field Wrapper.Unit#UNIT unit Player unit. -- @field Wrapper.Unit#UNIT unit Player unit.
-- @field #string unitname Name of the unit. -- @field #string unitname Name of the unit.
-- @field Wrapper.Client#CLIENT client Player client.
-- @field #string actype Aircraft type. -- @field #string actype Aircraft type.
-- @field #string name Player name. -- @field #string name Player name.
-- @field #number dt Time step for data recording in seconds. Default 0.01 sec ==> 100 data points per second! -- @field #number dt Time step for data recording in seconds. Default 0.01 sec ==> 100 data points per second!
-- @field #number rd Recording duration in seconds.
-- @field #boolean recording If true, recording started.
-- @field #table data Data table. -- @field #table data Data table.
-- @field #number SID Scheduler ID. -- @field #number SID Scheduler ID.
@@ -73,14 +102,14 @@ FMD = {
-- @field DCS#Vec3 v Velocity vector. Components x,y,z in m/s. -- @field DCS#Vec3 v Velocity vector. Components x,y,z in m/s.
-- @field DCS#Vec3 o Orientation vector. Components x,y,z in meters. -- @field DCS#Vec3 o Orientation vector. Components x,y,z in meters.
-- @field #number omega Angle velocity in m/s. -- @field #number omega Angle velocity in m/s.
-- @field #number hdg Heading in degrees. -- @field #number Hdg Heading in degrees.
-- @field #number Atot Total acceleration m/s^2. -- @field #number Atot Total acceleration m/s^2.
-- @field DCS#Vec3 a Accelleration vector. -- @field DCS#Vec3 a Acceleration vector.
-- @field #number DRoll Roll speed in degrees/second. -- @field #number DRoll Roll speed in degrees/second.
-- @field #number DPitch Pitch speed in degrees/second. -- @field #number DPitch Pitch speed in degrees/second.
-- @field #number DYaw Yaw speed in degrees/second. -- @field #number DYaw Yaw speed in degrees/second.
--- Main group level radio menu: F10 Other/Airboss. --- Main group level radio menu: F10 Other/FMD.
-- @field #table MenuF10 -- @field #table MenuF10
FMD.MenuF10={} FMD.MenuF10={}
@@ -88,9 +117,9 @@ FMD.MenuF10={}
-- @field #table MenuF10Root -- @field #table MenuF10Root
FMD.MenuF10Root=nil FMD.MenuF10Root=nil
--- FMD class version. --- FMD script version.
-- @field #string version -- @field #string version
FMD.version="0.0.1" FMD.version="0.1.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
@@ -115,6 +144,9 @@ function FMD:New()
self:AddTransition("Stopped", "Start", "Running") -- Start FMD script. self:AddTransition("Stopped", "Start", "Running") -- Start FMD script.
self:AddTransition("*", "Status", "*") -- Start FMD script. self:AddTransition("*", "Status", "*") -- Start FMD script.
-- Start FMD.
FMD:Start()
return self return self
end end
@@ -125,9 +157,27 @@ end
-- @param #string To To state. -- @param #string To To state.
function FMD:onafterStart(From, Event, To) function FMD:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting Flight Model Data script version %s", FMD.version)
self:I(self.lid..text)
-- Handle events. -- Handle events.
self:HandleEvent(EVENTS.Birth) self:HandleEvent(EVENTS.Birth)
end
--- On after Stop event.
-- @param #FMD self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function FMD:onafterStop(From, Event, To)
-- Short info.
local text=string.format("Stopping Flight Model Data script version %s", FMD.version)
self:I(self.lid..text)
-- Handle events.
self:UnHandleEvent(EVENTS.Birth)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -158,10 +208,10 @@ function FMD:OnEventBirth(EventData)
self:T(self.lid..text) self:T(self.lid..text)
MESSAGE:New(text, 5):ToAllIf(self.Debug) MESSAGE:New(text, 5):ToAllIf(self.Debug)
-- Add Menu commands. -- Add Menu commands.
self:_AddF10Commands(_unitName) self:_AddF10Commands(_unitName)
-- Player data.
self.players[_playername]={} self.players[_playername]={}
local player=self.players[_playername] --#FMD.PlayerData local player=self.players[_playername] --#FMD.PlayerData
@@ -170,8 +220,11 @@ function FMD:OnEventBirth(EventData)
player.unit=_unit player.unit=_unit
player.unitname=_unitName player.unitname=_unitName
player.name=_playername player.name=_playername
player.client=CLIENT:FindByName(_unitName, nil, true)
player.actype=_unit:GetTypeName() player.actype=_unit:GetTypeName()
player.dt=0.01 player.dt=0.01
player.rd=30
player.recording=false
player.data={} player.data={}
end end
@@ -220,8 +273,6 @@ function FMD:_Derivative(playerData)
return 0.5*(fpp-fpm)/h return 0.5*(fpp-fpm)/h
end end
self:E("derivative #"..#playerData.data)
local datapoints=playerData.data local datapoints=playerData.data
--local dt=playerData.dt --local dt=playerData.dt
@@ -231,6 +282,7 @@ function FMD:_Derivative(playerData)
local dpp=datapoints[i+1] --#FMD.DataPoint local dpp=datapoints[i+1] --#FMD.DataPoint
local dpi=datapoints[i] --#FMD.DataPoint local dpi=datapoints[i] --#FMD.DataPoint
-- Time step.
local dt=0.5*(dpp.time-dpm.time) local dt=0.5*(dpp.time-dpm.time)
dpi.Atot=numderiv(dpm.Vtot, dpp.Vtot, dt) dpi.Atot=numderiv(dpm.Vtot, dpp.Vtot, dt)
@@ -255,8 +307,21 @@ end
-- @param #FMD.PlayerData playerData Player data table. -- @param #FMD.PlayerData playerData Player data table.
function FMD:_RecordData(playerData) function FMD:_RecordData(playerData)
-- Check if we are recording already.
if not playerData.recording then
-- Inform player.
local text=string.format("Data recording starts for %.1f sec.", playerData.rd)
MESSAGE:New(text, 3, "FMD"):ToClient(playerData.client)
-- Activate recording switch.
playerData.recording=true
end
-- Get data point.
local dp=self:_GetDataPoint(playerData.unit) local dp=self:_GetDataPoint(playerData.unit)
-- Add data point to player table.
table.insert(playerData.data, dp) table.insert(playerData.data, dp)
end end
@@ -284,8 +349,6 @@ function FMD:_SaveData(playerData)
path=path or lfs.writedir() path=path or lfs.writedir()
end end
self:E("FF save data")
-- Create unused file name. -- Create unused file name.
local filename=nil local filename=nil
for i=1,9999 do for i=1,9999 do
@@ -347,6 +410,7 @@ function FMD:_SaveData(playerData)
local r=dp.DYaw or 0 local r=dp.DYaw or 0
local s=dp.omega or 0 local s=dp.omega or 0
-- Debug output.
self:T3(t) self:T3(t)
self:T3(a) self:T3(a)
self:T3(b) self:T3(b)
@@ -369,7 +433,7 @@ function FMD:_SaveData(playerData)
self:T3(s) self:T3(s)
-- t a b c d e f g h i j k l m n o p q r s -- t a b c d e f g h i j k l m n o p q r s
data=data..string.format("%.2f,%.2f,%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f \n", data=data..string.format("%.2f,%.2f,%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n",
t, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) t, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)
end end
@@ -435,7 +499,27 @@ function FMD:_AddF10Commands(_unitName)
end end
-------------------------------- --------------------------------
-- F10/FMD/<Carrier>/F1 Help -- F10/F<X> FMD/F1 Time Interval
--------------------------------
local _timePath=missionCommands.addSubMenuForGroup(gid, "Time Interval", _rootPath)
-- F10/FMD/F1 Time Interval/
missionCommands.addCommandForGroup(gid, "Delta t=0.01 s", _timePath, self._SetTimeInterval, self, _unitName, 0.01)
missionCommands.addCommandForGroup(gid, "Delta t=0.1 s", _timePath, self._SetTimeInterval, self, _unitName, 0.1)
missionCommands.addCommandForGroup(gid, "Delta t=1.0 s", _timePath, self._SetTimeInterval, self, _unitName, 1.0)
missionCommands.addCommandForGroup(gid, "Delta t=10 s", _timePath, self._SetTimeInterval, self, _unitName, 10.0)
missionCommands.addCommandForGroup(gid, "Delta t=30 s", _timePath, self._SetTimeInterval, self, _unitName, 30.0)
missionCommands.addCommandForGroup(gid, "Delta t=60 s", _timePath, self._SetTimeInterval, self, _unitName, 60.0)
local _durPath=missionCommands.addSubMenuForGroup(gid, "Rec Duration", _rootPath)
-- F10/FMD/F1 Rec Duration/
missionCommands.addCommandForGroup(gid, "T=10 s", _timePath, self._SetRecDuration, self, _unitName, 10)
missionCommands.addCommandForGroup(gid, "T=30 s", _timePath, self._SetRecDuration, self, _unitName, 30)
missionCommands.addCommandForGroup(gid, "T=60 s", _timePath, self._SetRecDuration, self, _unitName, 60)
missionCommands.addCommandForGroup(gid, "T=5 min", _timePath, self._SetRecDuration, self, _unitName, 5*60)
missionCommands.addCommandForGroup(gid, "T=10 min", _timePath, self._SetRecDuration, self, _unitName, 10*60)
--------------------------------
-- F10/F<X> FMD/
-------------------------------- --------------------------------
-- F10/FMD/Start Recording -- F10/FMD/Start Recording
@@ -447,6 +531,60 @@ function FMD:_AddF10Commands(_unitName)
end end
end end
--- Set recording duration.
-- @param #FMD self
-- @param #string _unitName Name of player unit.
-- @param #number rd Recording duration in sec.
function FMD:_SetTimeInterval(_unitName, rd)
-- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
-- Check if we have a unit which is a player.
if _unit and _playername then
local playerData=self.players[_playername] --#FMD.PlayerData
if playerData then
-- Set dt.
playerData.rd=rd
-- Inform player.
local text=string.format("Data recording duration set to %.1f sec.", playerData.rd)
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
end
end
end
--- Set time interval for data recording.
-- @param #FMD self
-- @param #string _unitName Name of player unit.
-- @param #number dt Time interval in seconds.
function FMD:_SetTimeInterval(_unitName, dt)
-- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName(_unitName)
-- Check if we have a unit which is a player.
if _unit and _playername then
local playerData=self.players[_playername] --#FMD.PlayerData
if playerData then
-- Set dt.
playerData.dt=dt
-- Inform player.
local text=string.format("Data recording time interval set to %.3f sec.", playerData.dt)
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
end
end
end
--- Start data recording. --- Start data recording.
-- @param #FMD self -- @param #FMD self
-- @param #string _unitName Name of player unit. -- @param #string _unitName Name of player unit.
@@ -461,11 +599,18 @@ function FMD:_StartRecording(_unitName)
if playerData then if playerData then
-- Delay before recording starts.
local delay=3 local delay=3
MESSAGE:New(string.format("Flight data recording will be started in %d seconds. dt=%.3f sec", delay, playerData.dt)):ToAll() -- Inform player.
local text=string.format("Data recording will be started in %d seconds with %.3f sec timestep for %.1f sec.", delay, playerData.dt, playerData.rd)
MESSAGE:New(text, 3, "FMD"):ToClient(playerData.client)
playerData.SID=playerData.scheduler:Schedule(nil, self._RecordData, {self, playerData}, delay, playerData.dt) -- Start scheduler.
playerData.SID=playerData.scheduler:Schedule(nil, self._RecordData, {self, playerData}, delay, playerData.dt, 0.0)
-- Stop scheduler once.
playerData.scheduler:ScheduleOnce(playerData.rd+delay, self._StopRecording, {self,_unitName})
end end
end end
@@ -485,6 +630,15 @@ function FMD:_StopRecording(_unitName)
if playerData then if playerData then
local ndata=#playerData.data
-- Inform player.
local text=string.format("Data recording stopped. Data points recorded: %d", ndata)
MESSAGE:New(text, 10, "FMD"):ToClient(playerData.client)
-- No recording switch.
playerData.recording=false
-- Stop scheduler. -- Stop scheduler.
playerData.scheduler:Stop(playerData.SID) playerData.scheduler:Stop(playerData.SID)
@@ -527,6 +681,7 @@ function FMD:_GetPlayerUnitAndName(_unitName)
return nil,nil return nil,nil
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+11 -11
View File
@@ -4,16 +4,16 @@
-- Train to evade missiles without being destroyed. -- Train to evade missiles without being destroyed.
-- --
-- --
-- **Main Features:** -- ## Main Features:
-- --
-- * Adaptive update of missile-to-player distance. -- * Adaptive update of missile-to-player distance.
-- * Define your own training zones on the map. Player in this zone will be protected. -- * Define your own training zones on the map. Player in this zone will be protected.
-- * Define launch zones. Only -- * Define launch zones. Only
-- * F10 radio menu to adjust settings for each player. -- * F10 radio menu to adjust settings for each player.
-- * Easy to use. -- * Easy to use.
-- * Handles air-to-air and surface-to-air missiles. -- * Handles air-to-air and surface-to-air missiles.
-- * Alert on missile launch (optional). -- * Alert on missile launch (optional).
-- * Marker of missile launch position (optional). -- * Marker of missile launch position (optional).
-- --
-- === -- ===
-- --
@@ -1039,8 +1039,8 @@ end
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned. --- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
-- @param #FOX2 self -- @param #FOX2 self
-- @param #DCS.Weapon weapon The weapon. -- @param #DCS.Weapon weapon The weapon.
-- @return #number Notching heading right, i.e. missile heading +90° -- @return #number Notching heading right, i.e. missile heading +90
-- @return #number Notching heading left, i.e. missile heading -90°. -- @return #number Notching heading left, i.e. missile heading -90.
function FOX2:_GetNotchingHeadings(weapon) function FOX2:_GetNotchingHeadings(weapon)
if weapon then if weapon then