Skip to content

Functions: Transfers

Functions for computing interplanetary transfer orbits using the Lambert problem solver. Given a departure body, arrival body, departure time, and arrival time, the solver finds the transfer orbit that connects the two positions.


Solves the Lambert problem for a transfer between two planets and returns the full solution record. The solver computes heliocentric positions of both bodies at the departure and arrival times using VSOP87, then finds the conic section connecting them.

lambert_transfer(
dep_body int4,
arr_body int4,
dep_time timestamptz,
arr_time timestamptz
) → RECORD(
c3_departure float8,
c3_arrival float8,
v_inf_departure float8,
v_inf_arrival float8,
tof_days float8,
transfer_sma float8
)
ParameterTypeDescription
dep_bodyint4Departure planet body ID (1-8). See Body ID Reference.
arr_bodyint4Arrival planet body ID (1-8)
dep_timetimestamptzDeparture epoch
arr_timetimestamptzArrival epoch. Must be after dep_time.

A record with the following fields:

FieldTypeUnitDescription
c3_departurefloat8km^2/s^2Departure characteristic energy. The square of the hyperbolic excess velocity at the departure planet. A smaller C3 requires less launch energy.
c3_arrivalfloat8km^2/s^2Arrival characteristic energy. The energy that must be shed for orbit insertion at the arrival planet.
v_inf_departurefloat8km/sHyperbolic excess velocity at departure (= sqrt(C3)).
v_inf_arrivalfloat8km/sHyperbolic excess velocity at arrival.
tof_daysfloat8daysTime of flight from departure to arrival.
transfer_smafloat8AUSemi-major axis of the transfer orbit. Negative for hyperbolic transfers.
-- Earth to Mars transfer for a 2028 opportunity
SELECT round(c3_departure::numeric, 2) AS c3_depart,
round(c3_arrival::numeric, 2) AS c3_arrive,
round(v_inf_departure::numeric, 3) AS v_inf_dep,
round(v_inf_arrival::numeric, 3) AS v_inf_arr,
round(tof_days::numeric, 1) AS flight_days,
round(transfer_sma::numeric, 4) AS sma_au
FROM lambert_transfer(3, 4,
'2028-10-01'::timestamptz,
'2029-06-15'::timestamptz);
-- Pork chop plot: scan departure and arrival dates for Earth-Mars
-- 150 departure dates x 150 arrival dates = 22,500 solutions
SELECT dep, arr,
round(c3_departure::numeric, 2) AS c3
FROM generate_series('2028-08-01'::timestamptz, '2029-01-28'::timestamptz, interval '1 day') AS dep
CROSS JOIN generate_series('2029-03-01'::timestamptz, '2029-07-28'::timestamptz, interval '1 day') AS arr,
LATERAL lambert_transfer(3, 4, dep, arr) AS lt
WHERE c3_departure IS NOT NULL
AND c3_departure < 50
ORDER BY c3_departure;
-- Compare transfer windows for all outer planets from Earth
SELECT arr_body,
CASE arr_body
WHEN 5 THEN 'Jupiter' WHEN 6 THEN 'Saturn'
WHEN 7 THEN 'Uranus' WHEN 8 THEN 'Neptune'
END AS target,
round(c3_departure::numeric, 2) AS c3,
round(tof_days::numeric, 0) AS flight_days
FROM unnest(ARRAY[5,6,7,8]) AS arr_body,
lambert_transfer(3, arr_body,
'2030-01-01'::timestamptz,
'2030-01-01'::timestamptz + interval '2 years') AS lt;

A convenience function that solves the Lambert problem and returns only the departure C3 value. Returns NULL on solver failure (e.g., degenerate geometry, transfer angle near 0 or 180 degrees). This is the function to use for generating pork chop plots where only departure energy matters.

lambert_c3(dep_body int4, arr_body int4, dep_time timestamptz, arr_time timestamptz) → float8
ParameterTypeDescription
dep_bodyint4Departure planet body ID (1-8)
arr_bodyint4Arrival planet body ID (1-8)
dep_timetimestamptzDeparture epoch
arr_timetimestamptzArrival epoch

Departure C3 in km^2/s^2, or NULL if the solver fails.

-- Quick C3 check for a specific transfer
SELECT round(lambert_c3(3, 4,
'2028-10-15'::timestamptz,
'2029-05-01'::timestamptz
)::numeric, 2) AS c3_km2s2;
-- Dense pork chop plot using the scalar function
-- Faster than lambert_transfer when you only need C3
SELECT dep, arr,
round(lambert_c3(3, 4, dep, arr)::numeric, 2) AS c3
FROM generate_series('2028-08-01'::timestamptz, '2029-01-28'::timestamptz, interval '1 day') AS dep
CROSS JOIN generate_series('2029-03-01'::timestamptz, '2029-07-28'::timestamptz, interval '1 day') AS arr
WHERE lambert_c3(3, 4, dep, arr) IS NOT NULL
AND lambert_c3(3, 4, dep, arr) < 30;