Is there an analytical solution for time to intercept?

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
11,318
Reaction score
2,786
Points
203
Location
between the planets
Imagine you have two objects. One on a circular orbit, one on an eccentric orbit that intercepts the circular one. The properties of both orbits, the positions, as well as the true anomaly of the intersection point in both orbits are all known. What I need to calculate is if the two objects ever meet, and how long it takes if yes.

I can do this by iteration (to a reasonable amount of time). What I'm wondering is if there's an analytical solution to the problem that might help me save a couple of CPU cycles. I don't think there is, but I don't exactly know a lot of math. ChatGPT says there is a solution, but I know enough to determine that the equation it gave me is rubbish, so now I'm asking the people that actually know what they're doing.
 
ChatGPT is likely wrong. AFAIR, there is not even an analytical solution possible by plain mathematics, the function for position by time can not be inverted. You can calculate where a spacecraft is at a time, but not how much time you need to get to a position.... (or was it the other way around?)
 
There can't really be an explicit analytical solution. It would require, at minimimum, an explicit formula for the perimeter of an ellipse. And the aceleration as a function of time while traveling along an elliptical path.

The way to actually determine this is by solving Lambert's Problem. It usually takes the form or an iterative solution to a boundary value problem.

ChatGPT is almost certianly not the right tool to find specific technical information like this. It doesn't "know" anything.
 
Imagine you have two objects. One on a circular orbit, one on an eccentric orbit that intercepts the circular one. The properties of both orbits, the positions, as well as the true anomaly of the intersection point in both orbits are all known. What I need to calculate is if the two objects ever meet, and how long it takes if yes.
Don't know about ChatGPT but Claude seems to give a reasonable answer.
I guess you could call this "semi- analytical", because you do need the extended Euclidean Algorithm to solve a Diophantine equation.

Let's say:
  • t₁ is the time for object 1 to reach intersection from current position
  • t₂ is the time for object 2 to reach intersection from current position
  • T₁ is the period of orbit 1
  • T₂ is the period of orbit 2

For the objects to meet, we need : t₁ + nT₁ = t₂ + mT₂

Where n and m are integers that we need to find (representing the number of complete orbits each object makes before meeting).

We can rearrange this to: nT₁ - mT₂ = t₂ - t₁

This is a linear Diophantine equation. It has solutions if and only if the greatest common divisor (GCD) of T₁ and T₂ divides (t₂ - t₁).

If solutions exist, the first meeting time would be: t_meeting = t₁ + nT₁ = t₂ + mT₂

where n is the smallest positive integer that satisfies the equation.

Here is the Lua script to solve this:


Code:
-- Extended Euclidean Algorithm
local function extended_gcd(a, b)
    local old_r, r = a, b
    local old_s, s = 1, 0
    local old_t, t = 0, 1
    
    while r > 1e-10 do  -- Using epsilon for float comparison
        local quotient = math.floor(old_r / r)
        old_r, r = r, old_r - quotient * r
        old_s, s = s, old_s - quotient * s
        old_t, t = t, old_t - quotient * t
    end
    
    return old_r, old_s, old_t
end

-- Main function to find meeting time
function find_meeting_time(t1, t2, T1, T2)
    -- Normalize times to be within one period
    t1 = t1 % T1
    t2 = t2 % T2
    
    -- Calculate time difference
    local delta_t = t2 - t1
    
    -- Find GCD and Bézout coefficients
    local gcd, x, y = extended_gcd(T1, T2)
    
    -- Check if solution exists (using small epsilon for float comparison)
    if math.abs(delta_t % gcd) > 1e-10 then
        return nil  -- No solution exists
    end
    
    -- Calculate particular solution
    local n = (delta_t * x) / gcd
    
    -- Find smallest positive n
    local k = math.ceil(-n * gcd / T2)  -- k is multiplier to make n positive
    n = n + k * (T2 / gcd)
    
    -- Calculate meeting time
    local meeting_time = t1 + n * T1
    
    -- Return first positive meeting time
    return meeting_time > 0 and meeting_time or meeting_time + T1
end

-- Example usage:
--[[
local t1 = 350   -- time to intersection for orbit 1
local t2 = 719   -- time to intersection for orbit 2
local T1 = 5000  -- period of orbit 1
local T2 = 7213  -- period of orbit 2

local result = find_meeting_time(t1, t2, T1, T2)
if result then
    print(string.format("Objects will meet after %.2f seconds", result))
else
    print("Objects will never meet at the intersection point")
end
--]]

Haven't had the time to test it, but you can check against your own code to see if it works and if it's faster.
 
The problem is that one of the orbit (at least) is not circular, hence there is not any analytical formula for the "true" anomaly [imath]\theta(t)[/imath]. In Space Situational Awareness (SSA), the closest approach is searched my minimizing the function [imath]d(t) = \text{distance}(M_c(t),P_e(t))[/imath], where M and P are objects on the circular and elliptical orbits respectively.

Again, the problem is that [imath]P_e(t)[/imath] is not analytical and needs to be integrated. Then, you can only say: "there won't be any collision within the next XXX days/years...".

(EDIT#2, sorry) The only simple solution is if you start from a "collision" (meeting), then could meet again at the LCM (lowest common multiple) of both periods, i.e. if integers n, m exist that yield n*Tc = m*Te. That's, by the way, the problem of space debris when they collide once, they will multiply indefinitely and make some orbits crowded as illustrated in the movie Gravity.

(EDIT: ouch, I thought LaTeX equations were recognized.... too bad)
 
Last edited:
ChatGPT is almost certianly not the right tool to find specific technical information like this. It doesn't "know" anything.
Oh, I'm very well aware of that. It can however regurgitate well-known solutions to problems in different forms, which served me quite well when I needed newtons method to solve eccentric anomaly in code. The problem is, of course, that if there isn't a well-known solution, it just makes stuff up, which I could see was clearly the case here by the equations it gave me (I mean, one of the terms was Te/Te, it doesn't take a mathematician to realize that it's talking out of its digital butt...)

In any case, thanks everyone, especially dgatsoulis. I'll have a look at that code!
 
Here is a slightly improved version of the code that I posted, to handle edge cases a bit better.
Also added a time metric to check the time over a number of runs. It seems to be pretty fast.

You can try it out in the link below:
https://claude.site/artifacts/7484a9be-00eb-4fbf-bd54-4ffdd56f92b0


It took 6,050.1 milliseconds (6.05 secs) to run 10,000,000 (ten million) times.

1734849627132.png

Lua code below :

Code:
-- Extended Euclidean Algorithm
local function extended_gcd(a, b)
    local old_r, r = a, b
    local old_s, s = 1, 0
    local old_t, t = 0, 1
    
    while math.abs(r) > 1e-10 do
        local quotient = math.floor(old_r / r)
        old_r, r = r, old_r - quotient * r
        old_s, s = s, old_s - quotient * s
        old_t, t = t, old_t - quotient * t
    end
    
    return old_r, old_s, old_t
end

-- Format time into years, days, hours, minutes, seconds
local function format_time(seconds)
    local years = math.floor(seconds / (365.25 * 24 * 3600))
    seconds = seconds % (365.25 * 24 * 3600)
    local days = math.floor(seconds / (24 * 3600))
    seconds = seconds % (24 * 3600)
    local hours = math.floor(seconds / 3600)
    seconds = seconds % 3600
    local minutes = math.floor(seconds / 60)
    seconds = math.floor(seconds % 60)
    
    return string.format("%d years, %d days, %d hours, %d minutes, %d seconds",
        years, days, hours, minutes, seconds)
end

-- Find meeting time between two orbiting objects
function find_meeting_time(t1, t2, T1, T2)
    -- Normalize times to be within one period
    t1 = t1 % T1
    t2 = t2 % T2
    
    -- Calculate time difference
    local delta_t = t2 - t1
    
    -- Special handling for very close periods
    if math.abs(T1 - T2) < 1 then
        -- Calculate how many periods needed to make up the position difference
        local period_diff = math.abs(1/T1 - 1/T2)  -- Difference in frequencies
        if period_diff < 1e-10 then
            return nil -- Periods too close, effectively equal
        end
        
        local meeting_time = math.abs(delta_t / period_diff)
        
        -- Ensure we get the first future meeting time
        if meeting_time < t1 then
            -- Calculate approximate LCM for close periods
            local lcm = (T1 * T2) / math.min(T1, T2)
            meeting_time = meeting_time + lcm
        end
        
        return math.max(meeting_time, t1)
    end
    
    -- Regular case for different periods
    local gcd, x, y = extended_gcd(T1, T2)
    
    -- Check if meeting is possible
    if math.abs(delta_t % gcd) > 1e-6 then
        return nil
    end
    
    -- Calculate number of orbits needed
    local n = (delta_t * x) / gcd
    local k = math.ceil(-n * gcd / T2)
    n = n + k * (T2 / gcd)
    
    -- Calculate meeting time
    local meeting_time = t1 + n * T1
    
    -- Ensure positive future time
    while meeting_time <= 0 do
        local lcm = (T1 * T2) / gcd
        meeting_time = meeting_time + lcm
    end
    
    return meeting_time
end

-- Timing function
local function time_execution(func, ...)
    local start = os.clock()
    local result = func(...)
    local end_time = os.clock()
    return result, end_time - start
end

-- Example usage with timing:
local t1 = 0        -- time to intersection for orbit 1
local t2 = 2500     -- time to intersection for orbit 2
local T1 = 5000     -- period of orbit 1
local T2 = 5000.1   -- period of orbit 2

-- Run the calculation multiple times to get an average
local num_runs = 1000
local total_time = 0

for i = 1, num_runs do
    local result, execution_time = time_execution(find_meeting_time, t1, t2, T1, T2)
    total_time = total_time + execution_time
    
    -- Only print the result for the first run
    if i == 1 then
        if result then
            print(string.format("Objects will meet after %.2f seconds", result))
            print(format_time(result))
        else
            print("Objects will never meet at the intersection point")
        end
    end
end

print(string.format("\nAverage execution time over %d runs: %.6f seconds", num_runs, total_time/num_runs))
print(string.format("Total time for %d runs: %.6f seconds", num_runs, total_time))
 
I'll definitely have to spend some serious time with this once I get my first more naive version working, so that I have all the tests for it set up already! :cheers:
 
Back
Top