From c7494da901890748ce34e789631bb8716068bce6 Mon Sep 17 00:00:00 2001 From: Valmo Trindade Date: Tue, 5 May 2026 08:03:01 -0300 Subject: [PATCH] added mavlink cot parsing functions --- .../main/functions/api/fn_send_drone_cot.sqf | 31 +--- .../api/fn_send_uas_platform_cot.sqf | 72 +++++++++ .../functions/api/fn_send_uas_sensor_cot.sqf | 22 +++ .../functions/api/fn_send_uas_video_cot.sqf | 20 +++ .../api/fn_set_uas_camera_override.sqf | 9 ++ .../fn_extract_uas_camera_data.sqf | 141 ++++++++++++++++++ 6 files changed, 267 insertions(+), 28 deletions(-) create mode 100644 addons/main/functions/api/fn_send_uas_platform_cot.sqf create mode 100644 addons/main/functions/api/fn_send_uas_sensor_cot.sqf create mode 100644 addons/main/functions/api/fn_send_uas_video_cot.sqf create mode 100644 addons/main/functions/api/fn_set_uas_camera_override.sqf create mode 100644 addons/main/functions/extract_data/fn_extract_uas_camera_data.sqf diff --git a/addons/main/functions/api/fn_send_drone_cot.sqf b/addons/main/functions/api/fn_send_drone_cot.sqf index e8c09a5..535785e 100644 --- a/addons/main/functions/api/fn_send_drone_cot.sqf +++ b/addons/main/functions/api/fn_send_drone_cot.sqf @@ -4,31 +4,6 @@ params["_drone"]; -private _atak_role = "a-f-A"; -private _atak_callsign = [_drone] call armatak_fnc_extract_marker_callsign; - -switch (side _drone) do { - case "WEST": { - _atak_role = "a-f-A-M-F-Q" - }; - case "EAST": { - _atak_role = "a-h-A-M-F-Q" - }; - case "INDEPENDENT": { - _atak_role = "a-n-A-M-F-Q" - }; - case "CIVILIAN": { - _atak_role = "a-f-A-C" - }; - default { - _atak_role = "a-f-A-M-F-Q" - }; -}; - -_pre_defined_role = _drone getVariable "_atak_group_role"; - -if (!isNil "_pre_defined_role") then { - _callsign = _pre_defined_role; -}; - -_cot = [_drone, _atak_role, _atak_callsign] call armatak_fnc_send_marker_cot; +[_drone] call armatak_fnc_send_uas_platform_cot; +[_drone] call armatak_fnc_send_uas_video_cot; +[_drone] call armatak_fnc_send_uas_sensor_cot; diff --git a/addons/main/functions/api/fn_send_uas_platform_cot.sqf b/addons/main/functions/api/fn_send_uas_platform_cot.sqf new file mode 100644 index 0000000..62069e0 --- /dev/null +++ b/addons/main/functions/api/fn_send_uas_platform_cot.sqf @@ -0,0 +1,72 @@ +params ["_drone"]; + +private _uuid = _drone call armatak_fnc_extract_uuid; +private _uavControl = UAVControl _drone; +private _controller = _uavControl param [0, objNull]; +private _controller_uid = if (!isNull _controller) then { [_controller] call armatak_fnc_extract_uuid } else { _drone getVariable ["armatak_uas_controller_uid", _uuid] }; +private _callsign = [_drone] call armatak_fnc_extract_marker_callsign; + +private _atak_role = "a-f-A-M-H-Q"; +switch (side _drone) do { + case west: { + _atak_role = "a-f-A-M-H-Q"; + }; + case east: { + _atak_role = "a-h-A-M-H-Q"; + }; + case independent: { + _atak_role = "a-n-A-M-H-Q"; + }; + case civilian: { + _atak_role = "a-f-A-C"; + }; + default { + _atak_role = "a-f-A-M-H-Q"; + }; +}; + +private _position = _drone call armatak_client_fnc_extractClientPosition; +private _lat = _position select 1; +private _lon = _position select 2; +private _hae = _position select 3; +private _course = _position select 5; +private _speed = _position select 6; + +private _cameraData = [_drone] call armatak_fnc_extract_uas_camera_data; +private _azimuth = _cameraData select 0; +private _elevation = _cameraData select 1; +private _fov = _cameraData select 2; +private _range = _cameraData select 3; +private _vfov = _drone getVariable ["armatak_uas_vfov", _fov]; + +private _yaw = round (getDir _drone); +private _pitch = (vectorDir _drone) select 2; +private _roll = (vectorUp _drone) select 0; +private _isFlying = parseNumber (isEngineOn _drone); +private _hal = ((getPosATL _drone) select 2) max 0; +private _vehicleType = typeOf _drone; + +private _payload = [ + _uuid, + _atak_role, + _callsign, + _lat, + _lon, + _hae, + _course, + _speed, + _azimuth, + _elevation, + _fov, + _vfov, + _range, + _yaw, + _pitch, + _roll, + _hal, + _vehicleType, + _isFlying, + _controller_uid +]; + +"armatak" callExtension ["tcp_socket:cot:uas_platform", [_payload]]; diff --git a/addons/main/functions/api/fn_send_uas_sensor_cot.sqf b/addons/main/functions/api/fn_send_uas_sensor_cot.sqf new file mode 100644 index 0000000..e9fa6dc --- /dev/null +++ b/addons/main/functions/api/fn_send_uas_sensor_cot.sqf @@ -0,0 +1,22 @@ +params ["_drone"]; + +private _video_url = [_drone] call armatak_fnc_extract_marker_video_url; +if (_video_url == "") exitWith {}; + +private _uuid = _drone call armatak_fnc_extract_uuid; +private _video_uid = _uuid + "-video"; +private _sensor_uid = _uuid + "-sensor"; +private _callsign = [_drone] call armatak_fnc_extract_marker_callsign; + +private _position = _drone call armatak_client_fnc_extractClientPosition; +private _lat = _position select 1; +private _lon = _position select 2; +private _hae = _position select 3; + +private _cameraData = [_drone] call armatak_fnc_extract_uas_camera_data; +private _azimuth = _cameraData select 0; +private _fov = _cameraData select 2; +private _range = _cameraData select 3; + +private _payload = [_sensor_uid, _video_uid, _callsign, _lat, _lon, _hae, _azimuth, _fov, _range]; +"armatak" callExtension ["tcp_socket:cot:uas_sensor", [_payload]]; diff --git a/addons/main/functions/api/fn_send_uas_video_cot.sqf b/addons/main/functions/api/fn_send_uas_video_cot.sqf new file mode 100644 index 0000000..c3cef9e --- /dev/null +++ b/addons/main/functions/api/fn_send_uas_video_cot.sqf @@ -0,0 +1,20 @@ +params ["_drone"]; + +private _video_url = [_drone] call armatak_fnc_extract_marker_video_url; +if (_video_url == "") exitWith {}; + +private _uuid = _drone call armatak_fnc_extract_uuid; +private _video_uid = _uuid + "-video"; +private _callsign = [_drone] call armatak_fnc_extract_marker_callsign; + +private _signature = format ["%1|%2|%3", _video_uid, _callsign, _video_url]; +private _nextRefreshAt = _drone getVariable ["armatak_next_uas_video_refresh_at", 0]; +private _lastSignature = _drone getVariable ["armatak_last_uas_video_signature", ""]; + +if (_signature == _lastSignature && {diag_tickTime < _nextRefreshAt}) exitWith {}; + +_drone setVariable ["armatak_last_uas_video_signature", _signature, false]; +_drone setVariable ["armatak_next_uas_video_refresh_at", diag_tickTime + 300, false]; + +private _payload = [_video_uid, _callsign, _video_url]; +"armatak" callExtension ["tcp_socket:cot:uas_video", [_payload]]; diff --git a/addons/main/functions/api/fn_set_uas_camera_override.sqf b/addons/main/functions/api/fn_set_uas_camera_override.sqf new file mode 100644 index 0000000..518b117 --- /dev/null +++ b/addons/main/functions/api/fn_set_uas_camera_override.sqf @@ -0,0 +1,9 @@ +params ["_drone", ["_cameraData", []]]; + +if (isNull _drone) exitWith {}; + +if ((_cameraData isEqualType []) && {(count _cameraData) >= 6}) then { + _drone setVariable ["armatak_uas_camera_data_override", _cameraData + [serverTime], false]; +} else { + _drone setVariable ["armatak_uas_camera_data_override", nil, false]; +}; diff --git a/addons/main/functions/extract_data/fn_extract_uas_camera_data.sqf b/addons/main/functions/extract_data/fn_extract_uas_camera_data.sqf new file mode 100644 index 0000000..d4347d1 --- /dev/null +++ b/addons/main/functions/extract_data/fn_extract_uas_camera_data.sqf @@ -0,0 +1,141 @@ +params ["_drone"]; + +private _override = _drone getVariable ["armatak_uas_camera_data_override", []]; +private _isLocalController = hasInterface && {!isNull player} && {(getConnectedUAV player) isEqualTo _drone}; + +if (!_isLocalController && {_override isEqualType []} && {(count _override) >= 7}) then { + private _updatedAt = _override param [6, -1000]; + if ((time - _updatedAt) <= 5) exitWith { + private _overrideSpiAsl = _override param [4, []]; + private _overrideSpiGeo = _override param [5, []]; + _drone setVariable ["armatak_uas_spi_asl", _overrideSpiAsl, false]; + _drone setVariable ["armatak_uas_spi_geo", _overrideSpiGeo, false]; + _override select [0, 6] + }; +}; + +private _defaultFov = _drone getVariable ["armatak_uas_fov", 60]; +private _maxRange = _drone getVariable ["armatak_uas_max_range", 15000]; +private _originASL = getPosASL _drone; +private _originAGL = ASLToAGL _originASL; +private _cameraDir = []; +private _spiASL = []; +private _slantRange = 0; + +private _laserTarget = laserTarget _drone; +if (!isNull _laserTarget) then { + private _laserTargetWorld = getPosWorld _laserTarget; + private _laserTargetAslZ = (getPosASL _laserTarget) select 2; + _spiASL = [_laserTargetWorld select 0, _laserTargetWorld select 1, _laserTargetAslZ]; + _cameraDir = _spiASL vectorDiff _originASL; + _slantRange = _originASL vectorDistance _spiASL; +}; + +if (_cameraDir isEqualTo []) then { + private _uavControl = UAVControl _drone; + private _controlledTurretPath = _uavControl param [1, []]; + private _candidateTurrets = []; + + if ((_controlledTurretPath isEqualType []) && {_controlledTurretPath isNotEqualTo []}) then { + _candidateTurrets pushBack _controlledTurretPath; + }; + + { + if !(_x in _candidateTurrets) then { + _candidateTurrets pushBack _x; + }; + } forEach (allTurrets _drone); + + { + private _turretWeapons = _drone weaponsTurret _x; + if (_turretWeapons isNotEqualTo []) exitWith { + private _weapon = _turretWeapons select 0; + private _weaponDirection = _drone weaponDirection _weapon; + if (_weaponDirection isNotEqualTo [0, 0, 0]) then { + _cameraDir = _weaponDirection; + }; + }; + } forEach _candidateTurrets; +}; + +if (_cameraDir isEqualTo []) then { + _cameraDir = vectorDirVisual _drone; +}; + +private _dirMagnitude = vectorMagnitude _cameraDir; +if (_dirMagnitude <= 0) then { + private _fallbackAzimuth = getDir _drone; + _cameraDir = [sin _fallbackAzimuth, cos _fallbackAzimuth, -1]; + _dirMagnitude = vectorMagnitude _cameraDir; +}; + +_cameraDir = _cameraDir vectorMultiply (1 / _dirMagnitude); + +private _dirX = _cameraDir select 0; +private _dirY = _cameraDir select 1; +private _dirZ = _cameraDir select 2; +private _horizontalMagnitude = sqrt ((_dirX * _dirX) + (_dirY * _dirY)); + +private _azimuth = (((_dirX atan2 _dirY) + 360) mod 360); +private _elevation = (_dirZ atan2 (_horizontalMagnitude max 0.001)); + +if (_spiASL isEqualTo []) then { + private _altitudeAGL = (_originAGL select 2) max 0.1; + private _probeASL = _originASL vectorAdd (_cameraDir vectorMultiply _maxRange); + + if (_dirZ < -0.01 && {terrainIntersectASL [_originASL, _probeASL]}) then { + private _near = _originASL; + private _far = _probeASL; + + for "_i" from 0 to 24 do { + private _mid = [ + ((_near select 0) + (_far select 0)) / 2, + ((_near select 1) + (_far select 1)) / 2, + ((_near select 2) + (_far select 2)) / 2 + ]; + + if (terrainIntersectASL [_originASL, _mid]) then { + _far = _mid; + } else { + _near = _mid; + }; + }; + + _spiASL = _far; + _slantRange = _originASL vectorDistance _spiASL; + } else { + private _verticalComponent = abs _dirZ; + + if (_verticalComponent > 0.01) then { + _slantRange = (_altitudeAGL / _verticalComponent) min _maxRange; + } else { + _slantRange = _maxRange; + }; + + _slantRange = _slantRange max 1; + _spiASL = _originASL vectorAdd (_cameraDir vectorMultiply _slantRange); + _spiASL set [2, getTerrainHeightASL [_spiASL select 0, _spiASL select 1]]; + }; +}; + +if (_slantRange <= 0) then { + _slantRange = (_originASL vectorDistance _spiASL) max 1; +}; + +private _spiAgl = ASLToAGL _spiASL; +private _spiWorld = [_spiAgl select 0, _spiAgl select 1, (_spiAgl select 2) max 0]; +private _spiGeo = _spiWorld call armatak_client_fnc_convertClientLocation; + +_drone setVariable ["armatak_uas_spi_asl", _spiASL, false]; +_drone setVariable ["armatak_uas_spi_geo", _spiGeo, false]; + +[ + round _azimuth, + round _elevation, + round _defaultFov, + round (_slantRange max 1), + _spiASL, + _spiGeo +] + +