Jump to content
hatbat

Help calculating an accurate ETA

Recommended Posts

I've just started learning to script in earnest and so far I've been playing with a "widget" that will report various things to the player about an oncoming wave of jets during an air combat mission. It works just fine but the obvious flaw is that the ETA does not account for the direction the enemy wave might be flying. I need the relative velocity of two objects along the direction between them; so basically how fast they're converging on each other. If, for example, one object was flying in a perfect orbit around the other then the result of this would be 0m/s because the two objects were neither moving closer nor further together. I've done enough research to know this calculation will be the sum of the velocities of the objects along said direction (playerjet getDir enemyjet) and will probably involve the x and y of the velocity command when used on these objects but I just don't have a good enough understanding of maths or scripting to work out how to get there. I'd greatly appreciate anyone's help.

 

	monitor = 0; 
	
	_leaderVel = 999; //These two variables are predefined so that if a player has a poor connection and receives zero then the distance / speed equation will not be a divide by zero.
	_distance = 999;
	
	player addAction ["Monitor Hostiles", {monitor = 1}, [], 10, false, true, "", "monitor == 0 && isEngineOn wingleader", -0, false];
	player addAction ["Stop Monitoring Hostiles", {monitor = 0; hintSilent ""}, [], 10, false, true, "", "monitor == 1 && isEngineOn wingleader", -0, false];
	
	while {isEngineOn wingleader} do {
	
		if (monitor == 1) then {
			_leaderSpeed = round speed wingleader;
			_leaderVel = if (_leaderSpeed != 0) then {round ( _leaderSpeed * 0.277778)}; //Conversion to m/s.
			_distance = if (player distance wingleader != 0) then {player distance wingleader};
			_ETA = round (_distance / _leaderVel);
			hintSilent parseText format ["Hostile now at %1 km/h and %2 m/s! <br />It's %3 km away with a %4 second ETA!", _leaderSpeed, _leaderVel, [(_distance / 1000),1] call BIS_fnc_cutDecimals, _ETA];
			sleep 0.1;
		};

	};

 

Share this post


Link to post
Share on other sites

I ve found the math solution for the problem. I can get the ETA and additionaly the nearest distance of these 2 objects. but i ve no time to script it within the next days.

 

the solution adds the velocity vectors of each object to the position vectors. then it differentiates the resulting vectors from each other.

then u can calculate those vectors magnitude (distance of both objects) at a specific time with this formula:

 

d(t) = SQRT( (V2Y-V1Y)*t+P2y-P1y)² + ((V2x-V1x)*t+P2x-P1x)² + ((V1z-V2z)*t-P2z+P1z)² )

 

After deriving this formula for t and calculating its simple zero you get this formula for the ETA:

 

t_dmin = ( (P2y-P1y)*V2y + (P2x-P1x)*V2x + (P1z-P2z)*V1z + (P1y-P2y)*V1y + (P1x-P2x)*V1x + V2z*P2z - V2z*P1z ) /

( V2y² - 2*V1y*V2y + V2x² - 2*V1x*V2x + V1z² - 2*V2z*V1z + V1y² + V1x² + V2z² )

 

Now u can calculate the ETA with the formula for t_dmin.

If u want to get the nearest distance then u just substitute t in the first formula with the calculated ETA.

 

Symbols:

P1x - x component of position vector of object 1

P1y - y component of position vector of object 1

P1z - z component of position vector of object 1

 

P2x - x component of position vector of object 2

P2y - y component of position vector of object 2

P2z - z component of position vector of object 2

 

V1x - x component of velocity vector of object 1

V1y - y component of velocity vector of object 1

V1z - z component of velocity vector of object 1

 

V2x - x component of velocity vector of object 2

V2y - y component of velocity vector of object 2

V2z - z component of velocity vector of object 2

 

SQRT - square root

 

t - time

d(t) - distance at time t

t_dmin - time at minimum distance

 

Have fun with it! :-)

Cheers

 

 

EDIT: just forgot to say: position vectors should be ASL format for most cases

  • Like 1

Share this post


Link to post
Share on other sites

Found some minutes to create a script for it. Its completly untested and may contain errors and/or bugs.

/*
 Author: Sarogahtyp
 Description:
 Calculates the current estimated Time of arrival (ETA) at a rendevouz point and the distance at this point for two (flying) objects.
 the rendevouz point is the point where the two object will get closest together with their actual velocity vectors.

 Arguments:

 0: object1 or 3D ASL position 1 - one of both objects or its position
 1: object2 or 3D ASL position 2 - the other object or its position
 2: 3D velocity vector for position 1 - only needed if argument 1 is position
 3: 3D velocity vector for position 2 - only needed if argument 2 is position

 return value:

 array with 2 components
 0:  ETA
 1:  Minimum distance (on ETA)
*/

params [
	["_obj1", objNull, [objNull, []], 
	["_obj2", objNull, [objNull, []], 
	["_vel1", objNull, [[]]], 
	["_vel2", objNull, [[]]]
       ];

if(isNull _obj1 || isNull _obj2) exitWith {true};

private _is_pos_obj1 = (typeName _obj1 isEqualTo "ARRAY");
private _is_pos_obj2 = (typeName _obj2 isEqualTo "ARRAY");

if (( _is_pos_obj1 && isNull _vel1 ) || ( _is_pos_obj2 && isNull _vel2 )) exitWith {true}; 

private ["_ETA", "_DIST", "_numerator", "_dominator"];

//get ASL positions and split into coordinates
private _pos1 = if(_is_pos_obj1) then {_obj1} else {getPosASL _obj1};
private _pos2 = if(_is_pos_obj2) then {_obj2} else {getPosASL _obj1};

_pos1 params ["_p1x", "_p1y", "_p1z"];
_pos2 params ["_p2x", "_p2y", "_p2z"];

//get velocities and split into coordinates
_vel1 = if(_is_pos_obj1) then {_vel1} else {velocity _obj1};
_vel2 = if(_is_pos_obj2) then {_vel2} else {velocity _obj2};

_vel1 params ["_v1x", "_v1y", "_v1z"];
_vel2 params ["_v2x", "_v2y", "_v2z"];

//calculate ETA
_numerator = (_p2y-_p1y)*_v2y + (_p2x-_p1x)*_v2x + (_p1z-_p2z)*_v1z + (_p1y-_p2y)*_v1y + (_p1x-_p2x)*_v1x + _v2z*_p2z - _v2z_*p1z;
_dominator = _v2y^2 - 2*_v1y*_v2y + _v2x^2 - 2*_v1x*_v2x + _v1z^2 - 2*_v2z*_v1z + _v1y^2 + _v1x^2 + _v2z^2;
_ETA = _numerator / _dominator;

//calculate distance on ETA
_DIST = sqrt ( ((_v2y-_v1y)*_ETA+_p2y-_p1y )^2 + ((_v2x-_v1x)*_ETA+_p2x-p1x)^2 + ((_v1z-_v2z)*_ETA-_p2z+_p1z)^2 );

[_ETA, _DIST]

I am currently not able to test it. I would be happy if someone could do this and report errors and/or bugs here. I ll look what I ve to change then.

Share this post


Link to post
Share on other sites
5 hours ago, sarogahtyp said:

Found some minutes to create a script for it. Its completly untested and may contain errors and/or bugs.

I am currently not able to test it. I would be happy if someone could do this and report errors and/or bugs here. I ll look what I ve to change then.


Wow, this is rather fortunate timing. I had just written a much more simplistic script based off your post and was about to bring forward some issues. I'll test your script first but I have a feeling there might be the same problem. This is what I wrote.

 

hostile = (nearestObjects [_this, ["Man"], 100]) select 1; //"B_Fighter_Pilot_F"

while {true} do {

P1x = getPosASL _this select 0;
P1y = getPosASL _this select 1;
P1z = getPosASL _this select 2;
 
P2x = getPosASL hostile select 0;
P2y = getPosASL hostile select 1;
P2z = getPosASL hostile select 2;
 
V1x = velocity _this select 0;
V1y = velocity _this select 1;
V1z = velocity _this select 2;
 
V2x = velocity hostile select 0;
V2y = velocity hostile select 1;
V2z = velocity hostile select 2;

positionEquations = ( (P2y-P1y)*V2y + (P2x-P1x)*V2x + (P1z-P2z)*V1z + (P1y-P2y)*V1y + (P1x-P2x)*V1x + V2z*P2z - V2z*P1z );
velocityEquations = ( V2y^2 - 2*V1y*V2y + V2x^2 - 2*V1x*V2x + V1z^2 - 2*V2z*V1z + V1y^2 + V1x^2 + V2z^2 );

if (velocityEquations != 0) then {

t_dmin = positionEquations / velocityEquations;

hintSilent parseText format [" Your speed: %1 m/s <br /> Hostile Speed: %2 m/s <br /> ETA = %3 seconds ", 
round ((velocityModelSpace _this) select 1), round ((velocityModelSpace hostile) select 1), abs (round t_dmin)];

} else {

hintSilent "";

};
sleep 0.1;
};

 

Share this post


Link to post
Share on other sites
5 minutes ago, sarogahtyp said:

What issues?

sent from mobile using Tapatalk
 

 

At the start of a mission the velocity will be zero right? That doesn't sit so well with this part:

t_dmin = positionEquations / velocityEquations;

I also found that the ETA seemed to give a negative number when the two objects were moving towards each other. I wouldn't have a clue why that is and it could possibly be my fault but I found that it was fairly simple just to use abs when fetching the result.

 

abs (round t_dmin);

 

Still, I'm going to test your script if I can figure how to implement a function.

Share this post


Link to post
Share on other sites

ETA should go negative if both objects are moving apart each other because the time of being closest is history then.

I thought bout a check for zero for the dominator as well. Should be better to have it

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites
1 minute ago, sarogahtyp said:

ETA should go negative if both objects are moving apart each other because the time of being closest is history then.

I thought bout a check for zero for the dominator as well. Should be better to have it

sent from mobile using Tapatalk
 

 

What I meant is that it was like the ETA was inverted. Positive when moving away and negative when moving together.


For now I've just dodged the zero divisor by adding this 

 

if (velocityEquations != 0) then {

 

but I think it might be handy to have it return perhaps an infinity symbol if the velocity was zero. Could that be done?

 

Share this post


Link to post
Share on other sites

Could be done but not now because I can't script anything now

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites

First error. I would sort this out but I've never used params before. Where is it missing?

 

bEqX0E6.png

 

 

 

 

Share this post


Link to post
Share on other sites
6 hours ago, sarogahtyp said:

params [ ["_obj1", objNull, [objNull, []], ["_obj2", objNull, [objNull, []], ["_vel1", objNull, [[]]], ["_vel2", objNull, [[]]] ];

The last two param defines, for _vel1 and _vel2 contain an additional closing bracket.

Should be like this:

params [
    ["_obj1", objNull, [objNull, []],
    ["_obj2", objNull, [objNull, []],
    ["_vel1", objNull, [[]], 
    ["_vel2", objNull, [[]] 
    ];

 

Share this post


Link to post
Share on other sites
The last two param defines, for _vel1 and _vel2 contain an additional closing bracket.
Should be like this:
params [["_obj1", objNull, [objNull, []],["_obj2", objNull, [objNull, []],["_vel1", objNull, [[]], ["_vel2", objNull, [[]] ];

 


This is wrong!
My mistake are two missing closing brackets at the end of the lines of _obj1 and _obj2


sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites
1 hour ago, sarogahtyp said:


This is wrong!
My mistake are two missing closing brackets at the end of the lines of _obj1 and _obj2


sent from mobile using Tapatalk
 

 

I fixed that and I'm not getting any more error reports but for reasons unknown _ETA always evaluates to zero, despite _obj1, _obj2, _vel1 and _vel2 all returning what they should be after having checked them. Perhaps you made a mistake when writing the equation? This is what I have right now:

 

/*
 Author: Sarogahtyp
 Description:
 Calculates the current estimated Time of arrival (ETA) at a rendevouz point and the distance at this point for two (flying) objects.
 the rendevouz point is the point where the two object will get closest together with their actual velocity vectors.

 Arguments:

 0: object1 or 3D ASL position 1 - one of both objects or its position
 1: object2 or 3D ASL position 2 - the other object or its position
 2: 3D velocity vector for position 1 - only needed if argument 1 is position
 3: 3D velocity vector for position 2 - only needed if argument 2 is position

 return value:

 array with 2 components
 0:  ETA
 1:  Minimum distance (on ETA)
*/


params [
	["_obj1", objNull, [objNull, []]], 
	["_obj2", objNull, [objNull, []]],
	["_vel1", objNull, [[]]], 
	["_vel2", objNull, [[]]]
       ];
	   
if(isNull _obj1 || isNull _obj2) exitWith {true};

private _is_pos_obj1 = (typeName _obj1 isEqualTo "ARRAY");
private _is_pos_obj2 = (typeName _obj2 isEqualTo "ARRAY");

if (( _is_pos_obj1 && isNull _vel1 ) || ( _is_pos_obj2 && isNull _vel2 )) exitWith {true}; 

private ["_ETA", "_DIST", "_numerator", "_dominator"];

//get ASL positions and split into coordinates
private _pos1 = if(_is_pos_obj1) then {_obj1} else {getPosASL _obj1};
private _pos2 = if(_is_pos_obj2) then {_obj2} else {getPosASL _obj1};

_pos1 params ["_p1x", "_p1y", "_p1z"];
_pos2 params ["_p2x", "_p2y", "_p2z"];

//get velocities and split into coordinates
_vel1 = if(_is_pos_obj1) then {_vel1} else {velocity _obj1};
_vel2 = if(_is_pos_obj2) then {_vel2} else {velocity _obj2};

_vel1 params ["_v1x", "_v1y", "_v1z"];
_vel2 params ["_v2x", "_v2y", "_v2z"];

//calculate ETA
_numerator = (_p2y-_p1y)*_v2y + (_p2x-_p1x)*_v2x + (_p1z-_p2z)*_v1z + (_p1y-_p2y)*_v1y + (_p1x-_p2x)*_v1x + _v2z*_p2z - _v2z*_p1z;
_dominator = _v2y^2 - 2*_v1y*_v2y + _v2x^2 - 2*_v1x*_v2x + _v1z^2 - 2*_v2z*_v1z + _v1y^2 + _v1x^2 + _v2z^2;

if (_dominator != 0) then {

_ETA = _numerator / _dominator;

//calculate distance on ETA
_DIST = sqrt ( ((_v2y-_v1y)*_ETA+_p2y-_p1y )^2 + ((_v2x-_v1x)*_ETA+_p2x-_p1x)^2 + ((_v1z-_v2z)*_ETA-_p2z+_p1z)^2 );

} else {_ETA = '∞'; _DIST = '∞';};
systemChat str _ETA; //DEBUG

[_ETA, _DIST]



 

 

Share this post


Link to post
Share on other sites

Sadly I can't verify anything some days. I be to work work work... Did u check what distance is returned?

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites
19 minutes ago, sarogahtyp said:

Sadly I can't verify anything some days. I be to work work work... Did u check what distance is returned?

sent from mobile using Tapatalk
 

 

Yeah that's zero too. Have you re-read the script? Perhaps it's just something obvious that you missed.

Share this post


Link to post
Share on other sites

I reread the formula for _ETA and it is correct.

I be to go sleep now.

Plz check all position and Velocity components and the numerator and dominator as well.

If that is not helping then calculate an ETA manually with a calculator to verify the formula please.
I ll look at your findings tomorrow in the morning.

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites

Or this little script, for an updated ETA, more exactly, time to go TTG, for an object2 to an object1. Negative timing means that object2 is going away.

Near a CPA (closest point of approach, TTG is obviously important, then becomes negative):


 

  fn_TTG = {
    params [["_obj1",objNull,[objNull]],["_obj2",objNull,[objNull]]];

    [_obj1,_obj2] spawn {
      params ["_obj1","_obj2"];
      private "_dt";
      while {true} do {
        _speedVecAbs = velocity _obj2 vectorDiff velocity _obj1;
        _dist = _obj1 distance _obj2;
        _cosine = (_speedVecAbs vectorCos (getPosASL _obj2 vectorFromTo getPOSASL _obj1));
        _speedVecRel = _speedVecAbs vectorMultiply _cosine;
        _sign = [-1,1] select (_cosine >= 0);
        _velRel = _sign * vectorMagnitude _speedVecRel;
        if (_velRel !=0) then {
          _dt = round (_dist/_velRel);
        } else {
          _dt = "inf."
        };
        hintSilent str(_dt);
        sleep 0.1
      };
    };
};
Example: 0 = [player,car1] call fn_TTG;

 

Share this post


Link to post
Share on other sites
4 hours ago, sarogahtyp said:

I reread the formula for _ETA and it is correct.

I be to go sleep now.

Plz check all position and Velocity components and the numerator and dominator as well.

If that is not helping then calculate an ETA manually with a calculator to verify the formula please.
I ll look at your findings tomorrow in the morning.

sent from mobile using Tapatalk
 


Well...

 

I set up some pretty extensive debugging and found that _numerator was evaluating to 0. I thought that was a little odd so I began to input some example numbers into a calculator and quickly realised the coordinates for the objects were identical. All down to this little typo.


private _pos1 = if(_is_pos_obj1) then {_obj1} else {getPosASL _obj1};
private _pos2 = if(_is_pos_obj2) then {_obj2} else {getPosASL _obj1};

 

Since that's sorted I'd like to get on to the issue of the ETA being inverted. I don't think you quite understood what I meant when I said the ETA was negative so here's an example:

 

 

Note how the ETA in the system chat is negative whilst moving towards the target and positive when away? Are _numerator and _dominator in the wrong order?

 

This is the current script if it helps:

 

/*
 Author: Sarogahtyp
 Description:
 Calculates the current estimated Time of arrival (ETA) at a rendevouz point and the distance at this point for two (flying) objects.
 the rendevouz point is the point where the two object will get closest together with their actual velocity vectors.

 Arguments:

 0: object1 or 3D ASL position 1 - one of both objects or its position
 1: object2 or 3D ASL position 2 - the other object or its position
 2: 3D velocity vector for position 1 - only needed if argument 1 is position
 3: 3D velocity vector for position 2 - only needed if argument 2 is position

 return value:

 array with 2 components
 0:  ETA
 1:  Minimum distance (on ETA)
*/


params [
	["_obj1", objNull, [objNull, []]], 
	["_obj2", objNull, [objNull, []]],
	["_vel1", objNull, [[]]], 
	["_vel2", objNull, [[]]]
       ];
	   
if(isNull _obj1 || isNull _obj2) exitWith {true};

private _is_pos_obj1 = (typeName _obj1 isEqualTo "ARRAY");
private _is_pos_obj2 = (typeName _obj2 isEqualTo "ARRAY");

if (( _is_pos_obj1 && isNull _vel1 ) || ( _is_pos_obj2 && isNull _vel2 )) exitWith {true}; 

private ["_ETA", "_DIST", "_numerator", "_dominator"];

//get ASL positions and split into coordinates
private _pos1 = if(_is_pos_obj1) then {_obj1} else {getPosASL _obj1};
private _pos2 = if(_is_pos_obj2) then {_obj2} else {getPosASL _obj2};

_pos1 params ["_p1x", "_p1y", "_p1z"];
_pos2 params ["_p2x", "_p2y", "_p2z"];

//get velocities and split into coordinates
_vel1 = if(_is_pos_obj1) then {_vel1} else {velocity _obj1};
_vel2 = if(_is_pos_obj2) then {_vel2} else {velocity _obj2};

_vel1 params ["_v1x", "_v1y", "_v1z"];
_vel2 params ["_v2x", "_v2y", "_v2z"];

//calculate ETA
_numerator = (_p2y-_p1y)*_v2y + (_p2x-_p1x)*_v2x + (_p1z-_p2z)*_v1z + (_p1y-_p2y)*_v1y + (_p1x-_p2x)*_v1x + _v2z*_p2z - _v2z*_p1z;
_dominator = _v2y^2 - 2*_v1y*_v2y + _v2x^2 - 2*_v1x*_v2x + _v1z^2 - 2*_v2z*_v1z + _v1y^2 + _v1x^2 + _v2z^2;

if (_dominator != 0) then {

_ETA = _numerator / _dominator;
systemChat format ["Numerator: %1, Dominator: %2, ETA: %3, Obj1: %4, Pos1: %5 %6 %7, Vel1: %8 %9 %10, Obj2: %11, Pos2: %12 %13 %14, Vel2: %15 %16 %17", round _numerator, round _dominator, round _ETA, _obj1, round _p1x, round _p1y, round _p1z, round _v1x, round _v1y, round _v1z, _obj2, round _p2x, round _p2y, round _p2z, round _v2x, round _v2y, round _v2z];

//calculate distance on ETA
_DIST = sqrt ( ((_v2y-_v1y)*_ETA+_p2y-_p1y )^2 + ((_v2x-_v1x)*_ETA+_p2x-_p1x)^2 + ((_v1z-_v2z)*_ETA-_p2z+_p1z)^2 );

} else {_ETA = '∞'; _DIST = '∞';};

[_ETA, _DIST]

 

 

 

Share this post


Link to post
Share on other sites

okay, thx for debugging those typo.

 

negative time:

I ve calculated it manually and it seems to be a point of view problem.

I think if you change obj1 and obj2 each other then the time is positive. Actually idk why but it resides in the formula and is no bug in the script.

 

EDIT:

I recalculated the complete formulas now and they are correct...

Share this post


Link to post
Share on other sites

If it is not the case that changing obj1 with obj2 gives a positive time then just negate the ETA before returning it.
As I said the formulas seem to be correct.

It would be cool if u check the distance value as well because it should be positive in any case.

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites
7 hours ago, sarogahtyp said:

okay, thx for debugging those typo.

 

negative time:

I ve calculated it manually and it seems to be a point of view problem.

I think if you change obj1 and obj2 each other then the time is positive. Actually idk why but it resides in the formula and is no bug in the script.

 

EDIT:

I recalculated the complete formulas now and they are correct...

 

6 hours ago, sarogahtyp said:

If it is not the case that changing obj1 with obj2 gives a positive time then just negate the ETA before returning it.
As I said the formulas seem to be correct.

It would be cool if u check the distance value as well because it should be positive in any case.

sent from mobile using Tapatalk
 


Nah. Swapping object 1 and 2 had no effect, at least when calling the function. I've negated the result of _ETA as you suggested and that's provided a suitable workaround. It's also had the effect of fixing _DIST, which previously didn't work.

 

I don't see how this couldn't be an error in the formula. I think the problem stems from the numerator which returns negative when it should be returning positive (at least I think so). Maybe when you get some time you could figure out why? This is the test scenario.

 

https://www.dropbox.com/s/r1pkqe388jwukju/Virtual%20Velocity%20Testing.VR.zip?dl=0

Share this post


Link to post
Share on other sites

Okay. It is as it is...
I can't do anything more on it actually.
It will take some weeks until I can test the stuff to get the reason for it. Also I will ask a friend of mine who has a mathematics degree. He will solve it for sure.

sent from mobile using Tapatalk

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×