Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Acoustic gunshot detector #37

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions @cTab/addons/acousticdetector/$PBOPREFIX$
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
z\ctab\addons\acousticdetector
15 changes: 15 additions & 0 deletions @cTab/addons/acousticdetector/CfgEventHandlers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
clientInit = QUOTE(call COMPILE_FILE(XEH_postInitClient));
};
};
5 changes: 5 additions & 0 deletions @cTab/addons/acousticdetector/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PREP(computeAmmoCaliberType);
PREP(firedManEH);
PREP(simulatePFH);
PREP(updateActiveState);
PREP(applySettings);
62 changes: 62 additions & 0 deletions @cTab/addons/acousticdetector/XEH_postInitClient.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "script_component.hpp"

if (!hasInterface) exitWith {};

GVAR(caliberTypeCache) = createHashMap;
GVAR(shotsToProcess) = [];
GVAR(detectedShots) = [];
GVAR(distanceLimitPerCaliber) = [250, 750, 350, 550, 1000, 1350, 1650, 2400];
GVAR(caliberLabel) = ["Rocket", "Missile", "5.56mm", "7.62mm", "12.7mm", "14.5mm", "20-40mm", "90mm+"];
GVAR(nextShotId) = 1;
GVAR(isActive) = false;
GVAR(pfhHandle) = -1;
GVAR(isInitialized) = false;

["CBA_settingsInitialized", FUNC(applySettings)] call CBA_fnc_addEventHandler;

#ifdef DEBUG_MODE_FULL

// GVAR(drawn) = [];

// [QGVAR(update), {
// private _toKeep = [];
// {
// _x params ['','_shotId','_point','_radius'];
// if ( !(_shotId in GVAR(drawn)) ) then {
// GVAR(drawn) pushBack _shotId;
// private _marker = createMarker [format ['_USER_DEFINED #0/shot%1C/0', _shotId], _point];
// _marker setMarkerShape "ELLIPSE";
// _marker setMarkerColor "ColorRed";
// _marker setMarkerSize [_radius, _radius];
// };
// _toKeep pushBack _shotId;
// } forEach GVAR(detectedShots);

// {
// deleteMarker format ['_USER_DEFINED #0/shot%1C/0', _x];
// } forEach (GVAR(drawn) - _toKeep);

// /*
// private _marker1 = createMarker [ format ['_USER_DEFINED #0/shot%1A/0', _shotId], _pointA];
// _marker1 setMarkerShape 'polyline';
// _marker1 setMarkerPolyline ((_pointA select [0,2]) + (_pointB select [0,2]) + (_pointC select [0,2]) +(_pointD select [0,2]) +(_pointA select [0,2]));
// _marker1 setMarkerColor "ColorRed";

// private _marker2 = createMarker [format ['_USER_DEFINED #0/shot%1C/0', _shotId], _point];
// _marker2 setMarkerShape "ELLIPSE";
// _marker2 setMarkerColor "ColorRed";
// _marker2 setMarkerSize [_radius, _radius];

// private _marker3 = createMarker [format ['_USER_DEFINED #0/shot%1R/0', _shotId], _source];
// _marker3 setMarkerType "mil_dot";
// _marker3 setMarkerColor "ColorBlack";
// */

// }] call CBA_fnc_addEventHandler;






#endif
9 changes: 9 additions & 0 deletions @cTab/addons/acousticdetector/XEH_preInit.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
#include "XEH_PREP.hpp"
ADDON = true;

[QGVAR(enable), "CHECKBOX", [LLSTRING(enable), LLSTRING(enableDetails)], ["cTab", LLSTRING(acousticdetector)], false, 0, FUNC(applySettings)] call CBA_fnc_addSetting;
[QGVAR(shotsTrackTimeLimit), "TIME", [LLSTRING(timeLimit), LLSTRING(timeLimitDetails)], ["cTab", LLSTRING(acousticdetector)], [10, 60, 20]] call CBA_fnc_addSetting;
[QGVAR(shotsCountLimit), "SLIDER", [LLSTRING(countLimit), LLSTRING(countLimitDetails)], ["cTab", LLSTRING(acousticdetector)], [10, 510, 150, 0]] call CBA_fnc_addSetting;
[QGVAR(filterDistance), "SLIDER", [LLSTRING(filterDistance), LLSTRING(filterDistanceDetails)], ["cTab", LLSTRING(acousticdetector)], [0, 100, 50, 0]] call CBA_fnc_addSetting;
2 changes: 2 additions & 0 deletions @cTab/addons/acousticdetector/XEH_preStart.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"
15 changes: 15 additions & 0 deletions @cTab/addons/acousticdetector/config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "script_component.hpp"

class CfgPatches {
class ADDON {
name = QUOTE(COMPONENT);
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ctab_main","ctab_core"};
author = "GrueArbre";
VERSION_CONFIG;
};
};

#include "CfgEventHandlers.hpp"
28 changes: 28 additions & 0 deletions @cTab/addons/acousticdetector/functions/fnc_applySettings.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "script_component.hpp"

if ( !hasInterface ) exitWith {};

TRACE_2("applySettings", GVAR(enable), GVAR(isInitialized));

if ( GVAR(enable) ) then {
if ( !GVAR(isInitialized) ) then {
LOG("Initialize events for Acoustic gunshot detector");
["CAManBase", "firedMan", FUNC(firedManEH)] call CBA_fnc_addClassEventHandler;
["unit", FUNC(updateActiveState)] call CBA_fnc_addPlayerEventHandler;
["turret", FUNC(updateActiveState)] call CBA_fnc_addPlayerEventHandler;
["vehicle", FUNC(updateActiveState)] call CBA_fnc_addPlayerEventHandler;

EGVAR(core,bftDrawHandlers) pushBack {
if ( !GVAR(isActive) ) exitWith { };
params ['_ctrl'];
{
_x params ['','','_point','_radius'];
_ctrl drawEllipse [_point, _radius, _radius, 0, cTabColorRed, "#(argb,8,8,3)color(1,1,1,0.5)"];
} forEach GVAR(detectedShots);
};

GVAR(isInitialized) = true;
};
};

call FUNC(updateActiveState);
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "script_component.hpp"
params ['_ammo'];
private _cfgAmmo = configFile >> "CfgAmmo";
if (_ammo isKindOf ["BulletBase", _cfgAmmo]) exitWith {
private _ace_caliber = getNumber (_cfgAmmo >> _ammo >> "ace_caliber");
if (_ace_caliber == 0) then {
_ace_caliber = getNumber (_cfgAmmo >> _ammo >> "ace_rearm_caliber");
};
if (_ace_caliber == 0) exitWith {
private _caliber = getNumber (_cfgAmmo >> _ammo >> "caliber");
if ( _caliber <= 1 ) exitWith {
CALIBER_556
};
if ( _caliber <= 2 ) exitWith {
CALIBER_762
};
if ( _caliber <= 3 ) exitWith {
CALIBER_1270
};
private _hit = getNumber (_cfgAmmo >> _ammo >> "hit");
if ( _hit > 20 ) exitWith {
CALIBER_2000
};
CALIBER_1450
};
if ( _ace_caliber < 6 ) exitWith {
CALIBER_556
};
if ( _ace_caliber < 8 ) exitWith {
CALIBER_762
};
if ( _ace_caliber < 13 ) exitWith {
CALIBER_1270
};
if ( _ace_caliber >= 20 ) exitWith {
CALIBER_2000
};
CALIBER_1450
};
if (_ammo isKindOf ["ShellBase", _cfgAmmo]) exitWith {
if (_ammo isKindOf ["ace_explosion_reflection_base", _cfgAmmo]) exitWith {
INFO_1("Ammo %1 is IGNORED", _ammo);
CALIBER_UNSUPPORTED
};
private _ace_caliber = getNumber (_cfgAmmo >> _ammo >> "ace_rearm_caliber");
if (_ace_caliber == 0) exitWith {
private _caliber = getNumber (_cfgAmmo >> _ammo >> "caliber");
if ( _caliber < 10 ) exitWith {
CALIBER_2000
};
CALIBER_9000
};
if (_ace_caliber < 90) exitWith {
CALIBER_2000
};
CALIBER_9000
};
if (_ammo isKindOf ["RocketBase", _cfgAmmo]) exitWith {
CALIBER_ROCKET
};
if (_ammo isKindOf ["MissileBase", _cfgAmmo]) exitWith {
CALIBER_MISSILE
};
INFO_1("Ammo %1 is IGNORED", _ammo);
CALIBER_UNSUPPORTED
17 changes: 17 additions & 0 deletions @cTab/addons/acousticdetector/functions/fnc_firedManEH.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "script_component.hpp"

if ( !GVAR(isActive) ) exitWith { };

params ["_unit", "", "", "", "_ammo", "", "_projectile", "_vehicle"];

if ( _unit == player || { _vehicle == vehicle player } ) exitWith {}; // system is able to ignore shots from vehicle

private _pos1 = if ( isNull _vehicle ) then { getPosASL _unit } else { getPosASL _vehicle };
private _pos2 = getPosASL _projectile;

private _caliber = GVAR(caliberTypeCache) getOrDefaultCall [_ammo, {[_ammo] call FUNC(computeAmmoCaliberType)}, true];

if ( _caliber != -1 ) then {
// Register shot for PFH
GVAR(shotsToProcess) pushBack [diag_tickTime, _pos1, _caliber];
};
85 changes: 85 additions & 0 deletions @cTab/addons/acousticdetector/functions/fnc_simulatePFH.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "script_component.hpp"

if ( !GVAR(isActive) ) exitWith { };

private _now = diag_tickTime;

private _count = count GVAR(detectedShots);
if ( _count > GVAR(shotsCountLimit) ) then {
// Prevent having too much shots stored
GVAR(detectedShots) = GVAR(detectedShots) select [_count - GVAR(shotsCountLimit), GVAR(shotsCountLimit)];
};
// Remove outdated detected shots
GVAR(detectedShots) = GVAR(detectedShots) select { _now - (_x select 0) < GVAR(shotsTrackTimeLimit) };
private _changed = _count != count GVAR(detectedShots);

// Process any pending shots
if ( count GVAR(shotsToProcess) > 0 ) then {

private _shots = GVAR(shotsToProcess);
GVAR(shotsToProcess) = [];

private _position = getPosASL vehicle player;
private _soundSpeed = 340; // meter per seconds

if (!isNil "ace_weather_currentTemperature") then {
// ACE if present, can give temperature
// temperature have an effect on sound speed
// Approximated to 331.5 + (0.607 * tempInCelcius)
// better approximation to consider : 20.05 * sqrt(tempInCelcius+273.15)
_soundSpeed = 331.5 + (0.607 * (ace_weather_currentTemperature - 0.0065 * (_position select 2)));
};

{
_x params ['_time', '_source', '_caliber'];
private _distance = _position vectorDistance _source;
private _limit = GVAR(distanceLimitPerCaliber) select _caliber;
if ( (_now - _time) > (_distance / _soundSpeed) ) then {
// sound had time to travel to our position

if ( _distance < _limit ) then { // gunshot is at a detectable distance, let's detect him

if ( _distance > GVAR(filterDistance) ) then { // if shot is too close, ignore him
private _detectedDir = (_position getDir _source) + (random [-2, 0, +2]); // +/- 2°
private _detectedDistance = _distance * (random [0.9, 1, 1.1]); // +/- 10%
private _point = _position getPos [_detectedDistance, _detectedDir];

// check if an existing shot might have the same source (close distance, to avoid flooding with too much data)
private _existing = GVAR(detectedShots) findIf { (_x select 4) == _caliber && { (_x select 2) vectorDistance _point < ((_x select 3) * 0.3) } };
if ( _existing == -1 ) then {
private _pointA = _position getPos [_detectedDistance + (_distance * 0.1), _detectedDir - 2];
private _pointB = _position getPos [_detectedDistance - (_distance * 0.1), _detectedDir - 2];
private _pointC = _position getPos [_detectedDistance - (_distance * 0.1), _detectedDir + 2];
private _pointD = _position getPos [_detectedDistance + (_distance * 0.1), _detectedDir + 2];
private _radius = _point vectorDistance _pointA;
private _shotId = GVAR(nextShotId);
GVAR(detectedShots) pushBack [ _now, _shotId, _point, _radius, _caliber, [_pointA, _pointB, _pointC, _pointD], _source];
_changed = true;
GVAR(nextShotId) = GVAR(nextShotId) + 1;
} else {
// Assume same source
(GVAR(detectedShots) select _existing) set [0, _now];
// _changed = true; Should we notify this one ?
};

};
};
} else {
// sound did not have time to get to our position
if ( _distance < _limit * 1.2 ) then {
// A ground vehicle speed will always be slower than sound
// Lets consider vehicle max speed 216km/h => 60m/s (really fast for a ground vehicle)
// Slowest sound condition, will be be 300m/s (extremely cold weather => -50°C)
// Even if we drive in the direction of the gunshot, we will never detect him
// if distance is larger than 20% the kimit (=60/300)
GVAR(shotsToProcess) pushBack _x;
};
};
} forEach _shots;
};

if ( _changed ) then {
TRACE_1("Update", count GVAR(detectedShots));
[QGVAR(update)] call CBA_fnc_localEvent;
};

38 changes: 38 additions & 0 deletions @cTab/addons/acousticdetector/functions/fnc_updateActiveState.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "script_component.hpp"

private _isActive = false;

if ( GVAR(enable) ) then {

private _vehicle = vehicle ctab_player;
if ( _vehicle != ctab_player) then {
private _cargoIndex = _vehicle getCargoIndex ctab_player;
if (_cargoIndex == -1) then {
_isActive = true; // TODO : check vehicle has acoustic detector
};
};

};

TRACE_3("updateActiveState", GVAR(enable), _isActive, GVAR(isActive));

if ( _isActive != GVAR(isActive) ) then {
GVAR(isActive) = _isActive;
GVAR(shotsToProcess) = [];
GVAR(detectedShots) = [];
[QGVAR(update)] call CBA_fnc_localEvent;
if ( _isActive ) then {
if ( GVAR(pfhHandle) == -1 ) then {
LOG("Acoustic gunshot detector is started");
GVAR(pfhHandle) = [FUNC(simulatePFH)] call CBA_fnc_addPerFrameHandler;
};
}
else
{
if ( GVAR(pfhHandle) != -1 ) then {
LOG("Acoustic gunshot detector is stopped");
[GVAR(pfhHandle)] call CBA_fnc_removePerFrameHandler;
GVAR(pfhHandle) = -1;
};
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "\z\ctab\addons\acousticdetector\script_component.hpp"
24 changes: 24 additions & 0 deletions @cTab/addons/acousticdetector/script_component.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#define COMPONENT acousticdetector
#include "\z\ctab\addons\main\script_mod.hpp"

// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE

#ifdef DEBUG_ENABLED_CORE
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_OTHER
#define DEBUG_SETTINGS DEBUG_SETTINGS_CORE
#endif

#include "\z\ctab\addons\main\script_macros.hpp"

#define CALIBER_UNSUPPORTED -1
#define CALIBER_ROCKET 0
#define CALIBER_MISSILE 1
#define CALIBER_556 2
#define CALIBER_762 3
#define CALIBER_1270 4
#define CALIBER_1450 5
#define CALIBER_2000 6
#define CALIBER_9000 7
Loading
Loading