Mathematically it's not difficult. Instead of pointing right to the Desired direction (direction from the dish to the target), calculate how much the dish can turn in the given time frame from Current direction to Desired direction, then point it there instead. In the next frame, the Desired direction is again updated. If the Desired direction doesn't change too much (target is far away from dish, moving slowly or moving away or towards the dish), the dish will eventually catch up.
The math behind it is simple. The dish can rotate around a vertical axis, which gives you one angle and around a horizontal axis to point up and down, which gives you the second angle. The rotation rate of each of these two can be capped individually.
You then calculate the angle difference in the vertical axis and the horizontal one. Best done in Horizontal frame of reference. If d is distance from the dish to target and a is the relative altitude (altitude of target - altitude of dish), then the vertical angle difference (angle around horizontal axis) can be expressed as
Alpha = ArcSin[a / d]
Let Beta be the angle the dish is currently pointing at. If w is the maximum angular velocity of the dish around the horizontal axis, then w * delta-t gives you the maximum it can possibly travel in this time frame. If that angle is lower than than the angle difference abs(Alpha - Beta), then the dish can be turned directly towards the target. If not, it has to be turned into the direction Beta + sign(Alpha - Beta) * w * delta-t.
The function sign returns 1 if the argument is positive or 0 and -1 if the argument is negative.
Rotation around vertical axis is similar:
Let Delta be the current angle the dish is pointing at (with 0 being north, Pi/2 being east).
If you get the relative position between the target and dish in the horizontal reference frame, then you can use the x and z components of the vector to figure out the desired direction the dish should be pointing at. z component points north and x points east, but it gets a bit tricky here. The desired angle Gamma is calculated as ArcTan[z / x], but be careful here! ArcTan[-z / -x] = ArcTan[z / x] and ArcTan[z / -x] = ArcTan[-z / x], so you'll get phantom results!
You can write your own code to return the correct heading, but if you want to use mine:
Code:
bool GetAirspeedHeading(OBJHANDLE Vessel, double &Heading)
{
Heading = 0;
if (!oapiIsVessel(Vessel))
{
return false;
}
VESSEL *VesselInterface = oapiGetVesselInterface(Vessel);
VECTOR3 CurrentVelocity;
VesselInterface->GetHorizonAirspeedVector(CurrentVelocity);
if (CurrentVelocity.x > 0)
{
if (CurrentVelocity.z > 0)
{
Heading = atan(CurrentVelocity.x / CurrentVelocity.z);
}
else if (CurrentVelocity.z < 0)
{
Heading = PI + atan(CurrentVelocity.x / CurrentVelocity.z);
}
else
{
Heading = PI / 2;
}
}
else if (CurrentVelocity.x < 0)
{
if (CurrentVelocity.z > 0)
{
Heading = (2 * PI) + atan(CurrentVelocity.x / CurrentVelocity.z);
}
else if (CurrentVelocity.z < 0)
{
Heading = PI + atan(CurrentVelocity.x / CurrentVelocity.z);
}
else
{
Heading = 3 * PI / 2;
}
}
else
{
if (CurrentVelocity.z > 0)
{
Heading = PI;
}
else if (CurrentVelocity.z < 0)
{
Heading = 0;
}
else
{
Heading = 0;
}
}
return true;
}
This bit of code returns the vessel's current heading - not nose direction, but heading of airspeed vector. It returns a number between 0 and 2 Pi. The most important bit is the giant if statement that handles the ArcTan function correctly. This is atan included in <math.h>
Anyways, now you have your desired angle Gamma and current angle Delta. If q is the maximum angular velocity around the vertical axis, then the dish can turn a maximum of q * delta-t in this frame. If the angle q * delta-t is smaller than abs(Gamma - Delta), then the dish can just snap to Gamma, if not, things get a bit complicated yet again.
You can't just use sign(Gamma - Delta) like you would before, because in cases where Gamma = 0 and Delta = 3/2 Pi - dish pointing west, target is due north - the dish would first turn south, then east, then north, turning an angle of 3/2Pi, instead of directly north, turning a Pi/2.
The bit of code that will give the correct direction is this:
Code:
double Direction = sin(DesiredHeading + ((2 * PI) - Heading));
if (DesiredHeading > Heading)
{
Direction = sin(DesiredHeading - Heading);
}
If Heading is the current direction of the dish and DesiredHeading is where it needs to be pointed, then this bit of code will produce a number between -1 and 1. If the number is negative, then you need to turn the dish left (if it's pointing east, turn it towards north). If it's positive, turn it right. If it's 0, then the dish is either pointing right at it, or the target is right behind the dish.
So, now you have the change in angle you can do in this time frame and a direction you need to move towards. Then new angle should then be Delta + Sign(Direction) * q * delta-t.
Now you have the two angles required - Beta and Delta. Depending on how Face has the code set up, he'll either need to turn that back into a vector or not. It's easy enough to do that.
Face, if I was unclear anywhere let me know and I'll clarify.