From my last blog post, I've posted a demonstration of implementing Predictive Guidance (PG) in game environment. In that blog post, one of the things mentioned was the importance of Time to Go (TGO) variable in the homing loop. Just what exactly is TGO and why is it important? Easy question but turns out it was a bit harder than I thought to solve..
Time to Go (TGO)
TGO just as its name speaks for itself, stands for estimated "time to go until intercept". Basically, it's the fancy TOF counter that counts down to 0 when you watch Hellfire kill videos. In missile guidance parlance, TGO opens you up to many many more advanced guidance laws. If you think about it, TGO is the single important number which can express the geometry of the engagement.
As discussed in the previous blog post, having an accurate TGO is very important to achieving good homing performance. If TGO is inaccurate, you could actually have significantly higher miss distance than classical homing methods that you might as well not bother. Higher miss distance means, further correction efforts your missile has to commit to, thereby rapidly draining down energy and lowering the chance of a successful intercept.
How to not estimate TGO
Getting a good estimate of TGO ended up being a bit of challenge. If you think about TGO from elementary and rudimentary perspective, one could just say "range to target divide by speed = time left to go". The obvious problem with this statement is that both the missile and the target are never constant speed objects, they're accelerating more or less unpredictably.
Other more funnier proposals included, solving quadratic equations to obtain time-to-intercept, or even using a trajectory equation to estimate the time of flight. In fact, the sample code from my last blog post used the trajectory equation to estimate TOF:
# Estimate Time to Go (tgo) using trajectory equation. # self.IMU_Measured_VR is the current speed of the missile as measured by # Inertial Measurement Unit (IMU) v_tgo = EXFLINT_Trajectory_ComputeTOF( 2 * range_to_target / self.IMU_Measured_VR )
Both of these estimations ended up being a comedy. While above estimation resulted in several successful intercepts against slower moving aircraft in the last blog's video (actually in one of the scenes, the missile missed), when engagement was made against high speed targets (like supersonic cruise missile), homing performance went down the crapper to unacceptable levels -- more or less it hit about 2 out of 5 times against high speed targets. Test performance against ballistic missile targets resulted in kill ratio of zero.
The problem of above estimations is that they assume constant speed and that target and the missile are not accelerating. Missile's speed constantly changes throughout its flight envelope -- it accelerates to peak speed, then slows down as it coasts. Accurate estimation of TGO and engagement geometry requires solving nonlinear missile and target equations ahead of time.
How to better "estimate" TGO
Solving nonlinear equations of missile and target flight envelopes is way beyond the scope of math I wish to think about and is too overly complicated. Instead, we need find a much simpler method.
As it turns out, a great reference to this question is "Tactical and Strategic Missile Guidance" by Paul Zarchan; in one of his documents, he states: "the predicted intercept is calculated in flight by rapidly integrating the nonlinear missile and target equations forward in flight at each guidance update".
The keywords here are "integrating" and "at each guidance update." Ah, integration! When we're integrating, that means we only need to deal with 'at each guidance update', or more or less need to find an accurate instantaneous TGO that corresponds to concurrent target & missile's relative velocities. Turns out this happens to be very easy. No need for all that strange math porn!
To estimate TGO at each integration update, one could argue:
Tgo = Rt / abs(Vc) Rt = relative distance (displacement) between missile and the target Vc = closing velocity
Rt is the relative distance, or in other words, the displacement vector between the target and the missile at the time of calculation. Vc is the closing velocity.
Closing Velocity means "range closing rate" -- it is the rate at which the relative distance between the missile and the target is being closed, or the rate at which it is being "shortened" as the two objects are closing at each other. Relative distance is the distance (displacement) between missile and the target.
So to express closing velocity in integration terms:
Vc = ( Rt0 - Rt1 ) / dT Rt0 = relative distance between msl & target at T=0 Rt1 = relative distance between msl & target at T=1 dT = time elapsed between measurements made for Rt0 and Rt1 Note that Vc is a negative number
So we can argue that the distance left to go (relative distance, aka "Rt") divided by the rate at which we're currently closing that distance (Vc), equals our instantaneous Time to Go. Very simple solution to a complex problem! Integration ftw.
Putting TGO to work
Recall from my last blog post that at each guidance update, acceleration shall be proportional to Zero Effort Miss (ZEM) and inversely proportional to the square of Time to Go:
Required Acceleration = N * ZEM / Tgo^2 N = navigation constant (3 to 5; I use 3) ZEM = Zero Effort Miss Tgo^2 = time to go squared
Zero Effort Miss (ZEM) is expressed as:
ZEM_y = y1 + yt * Tgo y1 = vertical (y-direction) separation between missile and target yt = derivative of y (relative velocity) Tgo = time to go # Or more colloquially: ZEM = Rtm + Vtm * Tgo Rtm = missile-target relative position Vtm = missile-target relative velocity Tgo = time to go
Final Python code in FLINT missile script for AIM-120 AMRAAM:
# Navigation constant Biased_NC = 3.0 # Integrate missile-target data try: self.rt_prev_frame except NameError, ValueError: # first frame initialization; IMU speed is safe here as missile hasn't accelerated yet self.rt_prev_frame = range_to_target Vc = -self.IMU_Measured_VR else: # at each integration update, we compute Tgo; # EXFLINT_TICKTOCK is simulator update constant (dT) Vc = ( range_to_target - self.rt_prev_frame ) / EXFLINT_TICKTOCK self.rt_prev_frame = range_to_target # Compute Time to Go try: v_tgo = range_to_target / abs(Vc) except ZeroDivisionError: raise EXFLINT_GuidanceLoopError(167) # Compute required acceleration command latax = LOS_new * Biased_NC * ( ZEM / ( v_tgo * v_tgo ) ) + LOS_Delta * ( Biased_NC / 2 )
Here is the concluding demonstration of the new homing performance by AIM-120 after applying the new Tgo estimation as described above. Is the homing performance any better? I surely think so, but you be the judge!