Orbit Fitting
Orbit fitting and observation ingestion.
Fitting tools:
Initial Orbit Determination (IOD) (
initial_orbit_determination()) – find candidate orbits from a small number of observations using statistical ranging. Returns scored candidates sorted best-first.Orbit Fitting (
fit_orbit()) – refine an orbit guess to best match the data, with automatic outlier rejection. Produces a best-fit state and Gaussian uncertainty estimate (covariance).MCMC Uncertainty Estimation (
fit_orbit_mcmc()) – for short arcs where the Gaussian approximation is unreliable, sample the full range of plausible orbits consistent with the data.
Observation ingestion:
MPC legacy format (
mpc_obs_to_observations(),MPCObservation) – parse the MPC 80-character fixed-width format and convert to Observations.MPC ADES API (
fetch_mpc_observations()) – query the MPC web API and apply EFCC18 catalog debiasing.Gaia DR3 (
fetch_gaia_observations()) – retrieve optical astrometry from the Gaia TAP service.JPL radar (
fetch_radar_observations(),fetch_radar_table()) – fetch delay and Doppler measurements from the JPL Small-Body Radar database.
- class kete.orbit_fitting.MPCObservation(desig, prov_desig, discovery, note1, note2, jd, ra, dec, mag_band, catalog_code, obs_code, sun2sc, geodetic=None)
Representation of an 80-character format MPC observation.
import kete import gzip # Comet Observations # url = "https://www.minorplanetcenter.net/iau/ECS/MPCAT-OBS/CmtObs.txt.gz" # Download the database of unnumbered observations from the MPC url = "https://www.minorplanetcenter.net/iau/ECS/MPCAT-OBS/UnnObs.txt.gz" url = "https://www.minorplanetcenter.net/iau/ECS/MPCAT-OBS/NumObs.txt.gz" path = kete.cache.download_file(url) # Fetch all lines from the file which contain C51 (WISE) observatory code. obs_code = "C51".encode() with gzip.open(path) as f: lines = [line.decode() for line in f if obs_code == line[77:80]] # Parse lines into a list of MPCObservations observations = kete.orbit_fitting.MPCObservation.from_lines(lines)
- Parameters:
desig (str)
prov_desig (str)
discovery (bool)
note1 (str)
note2 (str)
jd (float)
ra (float)
dec (float)
mag_band (str)
catalog_code (str)
obs_code (str)
sun2sc (list[float])
geodetic (tuple[float, float, float] | None)
- classmethod from_lines(lines, load_sc_pos=True)
Create a list of MPCObservations from a list of single 80 char lines.
Spacecraft (
S/s) and roving observer (V/v) observations span two lines. Lines whose observation type is not supported are skipped, and a warning reports how many of each type were skipped.
- property sc2obj
- class kete.orbit_fitting.Observation
Astronomical observation for orbit determination.
Observations can be optical (RA/Dec), radar range, or radar range-rate. Each carries the observer state, measured value(s), and 1-sigma uncertainties.
Use the static methods to construct instances:
- band
Photometric filter name (optical only, empty string for radar).
- dec
Declination in degrees (optical only, None otherwise).
- epoch
The observation epoch. For radar this is the receive epoch
t_rx.
- is_occultation
True if this optical measurement was derived from a stellar occultation (optical only, None for radar).
- mag
Apparent magnitude (NaN when unavailable).
- observer
The observer state (Sun-centered, Ecliptic).
For radar observations this returns the receiver state at the receive epoch (computed via PCK lookup on demand).
- static optical(observer, ra, dec, sigma_ra, sigma_dec, band=Ellipsis, mag=Ellipsis, time_sigma=0.1, sigma_corr=0.0, is_occultation=False)
Create an optical (RA/Dec) observation.
The observer state is automatically re-centered to the solar system barycenter if needed (the fitting engine works internally in SSB-centered coordinates).
- Parameters:
observer (
State) – Observer state (any center / frame - will be converted to SSB-centered Equatorial internally). The observation epoch is taken from the observer’s epoch.ra (float) – Right ascension in degrees.
dec (float) – Declination in degrees.
sigma_ra (float) – 1-sigma sky-plane RA uncertainty in arcseconds (
sigma_ra * cos(dec)). This matches the convention used by all standard astrometric data formats (MPC ADESrmsRA, Gaia DR3ra_error_*, etc.). Thecos(dec)factor is removed internally to convert to the RA coordinate direction used during chi-squared accumulation.sigma_dec (float) – 1-sigma Dec uncertainty in arcseconds.
band (str) – Photometric filter name (default
"V").mag (float) – Apparent magnitude (default
NaN).time_sigma (float) – 1-sigma timing uncertainty in seconds (default
0.5). When non-zero the along-track positional uncertainty is inflated bytime_sigma * apparent_speed, producing a non-diagonal weight matrix.sigma_corr (float) – Correlation coefficient between RA and Dec uncertainties (default
0.0, i.e. axis-aligned). Must lie in(-1, 1). Used when ADES-format astrometry reportsrmscorror Gaia DR3 reports a correlation between RA and Dec errors – the position covariance ellipse is tilted accordingly.is_occultation (bool) – Set to True when the measurement was derived from a stellar occultation rather than direct astrometry. Default
False. The fitting math is identical for both, but ingestion code uses this flag to skip the per-observatory residual table, EFCC18 debiasing, and over-observation reweighting – which do not apply to occultations.
- ra
Right ascension in degrees (optical only, None otherwise).
- static radar_range(xmit_lat, xmit_lon, xmit_height, rcvr_lat, rcvr_lon, rcvr_height, epoch, range, sigma_range)
Create a radar range observation.
The transmitter and receiver are stored as WGS84 geodetic coordinates. Their inertial states are computed inside the residual via PCK kernel lookups at the receive epoch (and at the iteratively-refined transmit epoch for the xmit station). Use the same site coordinates for monostatic observations.
- Parameters:
xmit_lat (float) – Transmitter geodetic latitude in degrees.
xmit_lon (float) – Transmitter geodetic longitude in degrees.
xmit_height (float) – Transmitter height above the WGS84 ellipsoid in km.
rcvr_lat (float) – Receiver geodetic latitude in degrees.
rcvr_lon (float) – Receiver geodetic longitude in degrees.
rcvr_height (float) – Receiver height above the WGS84 ellipsoid in km.
epoch (
Time) – Receive epocht_rx(TDB).range (float) – Measured range in AU.
sigma_range (float) – 1-sigma range uncertainty in AU.
- static radar_rate(xmit_lat, xmit_lon, xmit_height, rcvr_lat, rcvr_lon, rcvr_height, epoch, range_rate, sigma_range_rate)
Create a radar range-rate (Doppler) observation.
See
Observation.radar_range()for the station-coordinate convention.- Parameters:
xmit_lat (float) – Transmitter geodetic latitude in degrees.
xmit_lon (float) – Transmitter geodetic longitude in degrees.
xmit_height (float) – Transmitter height above the WGS84 ellipsoid in km.
rcvr_lat (float) – Receiver geodetic latitude in degrees.
rcvr_lon (float) – Receiver geodetic longitude in degrees.
rcvr_height (float) – Receiver height above the WGS84 ellipsoid in km.
epoch (
Time) – Receive epocht_rx(TDB).range_rate (float) – Measured range-rate in AU/day (positive = receding).
sigma_range_rate (float) – 1-sigma range-rate uncertainty in AU/day.
- range
Measured range in AU (radar range only, None otherwise).
- range_rate
Measured range-rate in AU/day (radar rate only, None otherwise).
- residual(obj_state)
Compute the residual
(observed - predicted)and the predicted measurement for this observation against a given object state.The state is expected at this observation’s epoch (use
propagate_n_body()to bring an arbitrary-epoch state toself.epochfirst). Light-time correction is applied internally; for radar the iterativet_txrefinement and relativistic two-way Doppler are handled by the same code path used during fitting.- Parameters:
obj_state (
State) – Object state at the observation epoch (any center / frame).- Returns:
(residual, predicted).Optical: 2-element lists.
residualis[dRA_sky, dDec]in arcseconds, sky-plane convention (dRAincludescos(dec), matchingObservation.optical()’ssigma_raunits).predictedis[ra_deg, dec_deg].Radar range: 1-element lists in AU.
Radar range-rate: 1-element lists in AU/day.
- Return type:
tuple[list[float], list[float]]
- sigma_corr
RA/Dec correlation coefficient (optical only, None otherwise).
- sigma_dec
1-sigma Dec uncertainty in arcseconds (optical only, None otherwise).
- sigma_ra
1-sigma sky-plane RA uncertainty in arcseconds (
sigma_ra * cos(dec)); optical only, None otherwise. Matches the input convention ofObservation.optical().
- sigma_range
1-sigma range uncertainty in AU (radar range only, None otherwise).
- sigma_range_rate
1-sigma range-rate uncertainty in AU/day (radar rate only, None otherwise).
- time_sigma
1-sigma timing uncertainty in seconds (optical only, None otherwise).
- class kete.orbit_fitting.OrbitFit
Result of fitting an orbit to observations.
Returned by
fit_orbit(). Contains the best-fit orbital state, its uncertainty (covariance), and diagnostic information about the fit.- all_observations
All input observations (time-sorted), including rejected outliers.
- converged
Whether the solver converged.
The fit has converged when the full Gauss-Newton step from it would reduce chi-squared by less than 1e-4, a step of about 0.01 sigma. When
Falsethe fit is the best found within the iteration limit, and the covariance is NaN when it could not be computed or the observations do not constrain every parameter.
- included
Per-observation inclusion mask (time-sorted).
Truemeans the observation was used in the final fit;Falsemeans it was rejected as an outlier.
- non_grav
Fitted non-gravitational model, or None if not fitted.
- observations
Observations included in the final fit (time-sorted).
Rejected outliers are not present in this list. To see all observations (including rejected ones), use
all_observations.Band and magnitude metadata from the original
Observationinputs are preserved.
- residuals
Post-fit residuals as a list of lists (included observations only, time-sorted order).
For optical observations the two elements are (DeltaRA, DeltaDec) in arcseconds. Radar residuals remain in AU or AU/day.
- rms
Reduced weighted RMS of post-fit residuals.
- state
Best-fit state at the reference epoch (Sun-centered, Ecliptic).
Convenience shortcut for
self.uncertain_state.state.
- uncertain_state
The uncertain orbit state (state + covariance + non-grav model).
- class kete.orbit_fitting.OrbitSamples
Collection of plausible orbits from MCMC uncertainty estimation.
Returned by
fit_orbit_mcmc(). Each orbit (draw) is statistically consistent with the observations; the spread of the collection represents the uncertainty in the orbit.- desig
Designator of the fitted object.
- divergent
Per-draw divergence flag.
A divergent sample indicates the sampler had difficulty exploring that region of orbit space. A small fraction of divergences is normal; many divergences suggest the model or data are problematic.
- draws
Sampled orbits as a list of
Stateobjects.Each state is Sun-centered Ecliptic at the reference epoch.
Non-gravitational parameters (if fitted) are available via
raw_draws.
- epoch
Common reference epoch (JD, TDB).
- log_posterior
Log-posterior density at each draw (natural log, relative only).
Values are only meaningful relative to each other within a single run. Useful for weighting draws or diagnosing chain quality.
- raw_draws
Raw orbit samples as a list of lists.
Each inner list is
[x, y, z, vx, vy, vz, ng_params...]in the Equatorial frame at the reference epoch. Usenp.array(samples.raw_draws)to convert to a 2-D array.
- seed_id
Seed index (0-based) that generated each draw.
Each seed produces an independent NUTS chain. This field records which seed produced each draw so per-seed diagnostics can be computed.
- class kete.orbit_fitting.RangingSamples
Orbit samples from the weighted ranging grid.
Returned by
fit_orbit_ranging().- convergence_warning
Warning message, else
None.Set when, after refinement, the grid ESS is below 50, the grid is still coarser than the posterior structure it samples, the linear attributable model does not describe the observations, or the best orbit fits the observations far worse than their uncertainties allow.
- effective_sample_size
Effective sample size over the grid cells before drawing.
This counts cells, not independent orbit solutions. Refinement splits cells, so it grows with grid resolution.
- epoch
Epoch of every draw (JD TDB), the attributable reference epoch.
- log_posterior
Log posterior density of the grid cell each draw came from, relative to the maximum across draws.
Draws are already distributed according to the posterior and are equally weighted; these values are not importance weights.
- raw_draws
Raw draws as [x, y, z, vx, vy, vz] in AU/AU*day, SSB Equatorial.
- kete.orbit_fitting.fetch_gaia_observations(desig, update_cache=False)
Fetch Gaia DR3 solar system object observations and convert to fitting Observations.
Queries the
gaiadr3.sso_observationtable via the Gaia TAP service for the given object and returns oneObservationper accepted astrometric transit. Only transits withastrometric_outcome_transit == 1(good positions) are returned.The Gaia spacecraft state is the table’s geocentric position and velocity added to the Earth’s state from the loaded SPICE kernels. The table’s barycentric vectors are not used: their barycenter differs from that of the loaded planetary ephemeris by an amount that is large at Gaia’s precision.
Gaia positions are corrected for aberration but not for the solar deflection of light. Each is shifted by minus the deflection of a star in the same direction, which puts it in the convention of positions reduced against background stars, the convention the fit models.
Observation epoch is the Gaia-centric TCB epoch stored in the table, converted to TDB via
Timewithscaling='tcb'.Positional uncertainties are the quadrature sum of the random and systematic components from the table (in mas, already multiplied by cos(dec) for the RA component), with both principal axes of the error ellipse limited to at least 0.1 mas.
Results are cached via
query_tap(); passupdate_cache=Trueto force a fresh query.- Parameters:
desig (str) – Object identifier as recognized by Gaia DR3. If the string parses as an integer it is matched against the
number_mpcolumn (recommended for numbered minor planets); otherwise it is matched against thedenominationcolumn (e.g."Apophis","1999 RQ36").update_cache (bool) – If
True, discard any cached result and re-query the TAP service.
- Returns:
One
Observation.opticalper accepted transit observation.- Return type:
list[Observation]
Examples
import kete observations = kete.observations.fetch_gaia_observations("Apophis") fit = kete.fitting.fit_orbit(initial_state, observations)
- kete.orbit_fitting.fetch_mpc_observations(desig, use_observatory_residuals=True, debias=True, apply_over_obs_reweight=True, update_cache=False)
Fetch observations from the MPC API and convert to fitting Observations.
Queries
https://data.minorplanetcenter.net/api/get-obsfor the given object designation and returns optical observations ready for orbit fitting. Only optical and occultation records with valid positions are included; radar and other types are silently skipped, as are records the MPC marks as deprecated.Results are cached under
~/.kete/observations/so that repeated queries for the same designation do not hit the network.Uncertainties are taken from the MPC-provided
rmsraandrmsdecfields when present. Otherwise they come from a pre-computed table of residual error by observatory code, and then from an epoch-based default. Both principal axes of the error ellipse of optical records are limited to at least 10 mas, and those of occultations to at least 0.1 mas. The limit keeps the orientation of the ellipse.The timing uncertainty is the ADES
rmsTimewhen present. Otherwise it is a default by epoch, with separate defaults for spacecraft observers (a position inICRF_KMorICRF_AU) and video observations (modeVID).Ground-station positions use the Earth orientation of the loaded PCK kernels. Before their coverage starts, they use the approximate Earth orientation of
approx_earth_pos_to_ecliptic().When
debiasis True, the EFCC18 star-catalog bias correction is applied to each observation’s (RA, Dec) using the ADESastCatfield. Observations with unknown or missing catalog codes are passed through unchanged.When
apply_over_obs_reweightis True, sigma is inflated by sqrt(n/4) for groups of more than 4 observations from the same observatory on the same night, following Veres et al. 2017.- Parameters:
desig (str) – Object designation recognized by the MPC (e.g.
"Apophis","101955","1999 RQ36").use_observatory_residuals (bool) – If
True, use the per-observatory sigmas from the pre-computed residual table for records withoutrmsraandrmsdec.debias (bool) – If
True, apply the EFCC18 star-catalog debiasing correction. Requires the JPLdebias_2018.tgzarchive, downloaded on first use.apply_over_obs_reweight (bool) – When True (default), inflate sigma for over-observed nights following Veres et al. 2017.
update_cache (bool) – If
True, discard any cached result and re-query the MPC.
- Returns:
One
Observation.opticalper valid optical record.- Return type:
list[Observation]
- Raises:
RuntimeError – If the MPC API request fails.
Examples
import kete observations = kete.observations.fetch_mpc_observations("Apophis")
- kete.orbit_fitting.fetch_observations(desig, update_cache=False)
Fetch all available observations for an object as fitting Observations.
This is a convenience wrapper around the various specific fetchers that tries to get “everything” for a given object. Currently this means MPC ADES optical, JPL radar, and Gaia DR3 measurements.
MPC observations have the EFCC18 debiasing correction applied and per-observatory sigmas when available.
- Parameters:
desig (str) – Object designation (e.g.
"Apophis","101955","1999 RQ36").update_cache (bool) – If
True, refresh any cached data before filtering.
- Returns:
All available observations for the object.
- Return type:
list[Observation]
- kete.orbit_fitting.fetch_radar_observations(desig, update_cache=False)
Fetch JPL radar observations for an object as fitting Observations.
Wraps
fetch_radar_table()and converts each record into either aObservation.radar_range()orObservation.radar_rate()instance ready for orbit fitting.Each record keeps the geodetic coordinates of its transmitter and receiver. The residual places the receiver at the receive epoch and the transmitter at the transmit epoch, found by iterating on the round-trip light time, so bistatic records are modeled exactly. Records with missing coordinates or unrecognized units are skipped, as are records referenced to the peak-power echo (
bp == "P") rather than the center of mass.Conversions assumed:
Delay (
us): round-trip light time. One-way range in AU isc * (delay_us * 1e-6) / 2 / AU_M.Doppler (
Hz): two-way Doppler shift at carrier frequencyfreq(MHz). Range-rate in AU/day is-c * doppler / (2 * freq_Hz)converted from m/s. JPL convention: positive Doppler means approaching; kete convention: positive range-rate means receding, so the sign is flipped.
- Parameters:
desig (str) – Object designation as it appears in the JPL
desfield (e.g."99942","1998 KY26").update_cache (bool) – If
True, refresh the cached master parquet before filtering.
- Returns:
One
Observation.radar_rangeorObservation.radar_rateper valid record.- Return type:
list[Observation]
- kete.orbit_fitting.fetch_radar_table(desig=None, update_cache=False)
Fetch JPL Small-Body radar astrometry data.
The complete JPL Small-Body Radar Astrometry data set is downloaded once and cached locally as a parquet file. Subsequent calls reuse the cached file unless
update_cacheis set. Ifdesigis supplied, only the records whosedesfield matches that designation are returned.The returned table includes the default API fields, the
observerlist for each measurement, and geodetic station coordinates joined onto each record for both the receiver (rcvr_*) and transmitter (xmit_*) stations.See https://ssd-api.jpl.nasa.gov/doc/sb_radar.html for field definitions.
- Parameters:
desig (str | None) – Object designation as it appears in the JPL
desfield (e.g."99942","1998 KY26","45P"). IfNone, every record in the data set is returned.update_cache (bool) – Force download a new file from JPL, refreshing the cached parquet.
- kete.orbit_fitting.fit_orbit(initial_state, observations, non_grav=None, include_asteroids=False, chi2_threshold=4.5, max_reject_passes=10)
Fit an orbit to observations using iterative least squares.
Given an initial guess and a set of observations, this function refines the orbital state until it best matches the data, and estimates the uncertainty of the result via a covariance matrix. It can also automatically identify and reject outlier observations.
This is the standard approach for orbit determination when you have a reasonable initial guess (e.g. from
initial_orbit_determination()). It works well for arcs of any length, but the Gaussian uncertainty estimate is most reliable for long, well-sampled arcs. For short arcs where the uncertainty is non-Gaussian, considerfit_orbit_mcmc()instead.When the arc extends more than 30 days from the reference epoch, time windows starting at +/-30 days and doubling in width are fitted around it first, so that each stage bootstraps from the previous converged solution. The final pass fits the full arc and re-evaluates all observations for outlier rejection (if enabled).
The input state is automatically re-centered to the solar system barycenter internally.
- Parameters:
initial_state (
State) – Initial guess for the object state (any center / frame).observations (list) – List of
Observationto fit.non_grav (
NonGravModel, optional) – Non-gravitational force model. Parameters set to NaN in the model are fit (starting from 0), after a gravity-only pass that movesinitial_stateto the gravity-only solution; parameters with concrete values are held fixed. Parameters freed withwith_free()are fit starting from their values, andinitial_stateis then taken as the matching starting point: the gravity-only pass is skipped. A starting value below a parameter’s lower bound raises; a parameter on its bound stays there until the fit moves it up. A model with no free parameters is used as a fixed force and nothing in it is fit. A fit that converges to a solution the observations do not constrain in some parameter raises, naming it.include_asteroids (bool) – If True, include asteroid masses in the force model (slower but more accurate for near-Earth objects). Default is False.
chi2_threshold (float) – Per-observation z-score threshold for outlier rejection following Carpino, Milani & Chesley (2003). An observation is rejected when its weighted chi-squared exceeds this multiple of the leverage- corrected expected value, and recovered when it falls below 6/7 of this threshold. For a 2-component optical observation with calibrated weights, a 3-sigma outlier in one axis gives z = 9/2 = 4.5, so values of 4-5 match per-component 3-sigma rejection. Default is 4.5.
max_reject_passes (int) – Maximum number of outlier-rejection passes. Set to 0 to disable outlier rejection entirely. Default is 10.
- Returns:
The fitted orbit, including the best-fit state, covariance, residuals, and convergence diagnostics.
- Return type:
- kete.orbit_fitting.fit_orbit_mcmc(seeds, observations, include_asteroids=False, num_draws=1000, num_tune=500, non_grav=None, maxdepth=10, target_accept=0.8)
Estimate orbit uncertainty from observations using Markov Chain Monte Carlo.
Given one or more candidate orbital states (seeds) and a set of observations, this function produces a collection of plausible orbits that are statistically consistent with the data. The spread of returned orbits represents the uncertainty in the orbit determination – wider spread means less certainty about the true orbit.
This is most useful for short-arc observations (a few nights) where the usual least-squares approach (
fit_orbit()) underestimates the true uncertainty. For well-observed objects with long arcs,fit_orbit()alone is usually sufficient and far cheaper.Under the hood this uses the No-U-Turn Sampler (NUTS), an adaptive variant of Hamiltonian Monte Carlo that efficiently explores the space of possible orbits. Each draw requires a full numerical propagation, so this is orders of magnitude more expensive than least squares – but the result captures non-Gaussian and multi-modal uncertainty that least squares cannot represent.
Seeds are raw
Stateobjects (typically frominitial_orbit_determination()). No prior orbit fit is required – the sampler builds its own internal mass matrix from a linearization at each seed.Sampling is parallelized automatically across available CPU cores. When there are fewer seeds than cores, each seed spawns multiple independent sub-chains. The
seed_idin the returnedOrbitSamplesidentifies which seed (orbital mode) produced each draw.num_drawsis the total number of orbit samples returned across all seeds. Each seed receives roughlynum_draws / len(seeds)draws. A chain that diverges through most of its warmup is dropped, and a new chain draws its samples. When every chain of a seed is dropped, the other seeds draw its samples.- Parameters:
seeds (list) – List of
Statecandidate states (e.g. frominitial_orbit_determination()), one per orbital mode. Seeds at different epochs are automatically propagated to the first seed’s epoch. The input states are re-centered to SSB Equatorial internally.observations (list) – List of
Observationto evaluate against.include_asteroids (bool) – If True, include asteroid masses in the force model. Default is False.
num_draws (int) – Total orbit samples across all seeds (after tuning). Default is 1000.
num_tune (int) – Number of warmup steps per sub-chain used to adapt internal sampling parameters. These draws are discarded. Default is 500.
non_grav (
NonGravModel, optional) – Shared non-gravitational force model applied to all chains. Parameters set to NaN in the model are sampled, starting from 0. Parameters freed withwith_free()are sampled, starting from their values. Other parameters are held fixed at their values.maxdepth (int) – Maximum tree depth for the sampler. Higher values allow more thorough exploration at greater computational cost. Default is 10.
target_accept (float) – Target acceptance probability for step-size adaptation during warmup. Lowering this (e.g. 0.6) makes the sampler take larger leapfrog steps, which helps in poorly constrained situations. Default is 0.8.
- Returns:
Collection of plausible orbits sampled from the posterior.
- Return type:
- Raises:
ValueError – If
seedsis empty or two-body epoch propagation fails, or if the chains keep diverging so the requested draws cannot be collected.
- kete.orbit_fitting.fit_orbit_ranging(observations, num_draws=1000, temperature=10.0, seed=0)
Generate orbit samples covering the full admissible region from sparse observations.
Scans a grid over topocentric range and range-rate, scores each cell by the chi^2 of its attributable, refines the grid where the posterior is significant, and draws samples in proportion to each cell’s posterior mass, with Gaussian jitter within the cell.
The attributable comes from a short window of observations from a single observer, chosen where the linear-motion approximation fits best. All observations are used to score the cells, and an orbit that cannot be compared with every one of them is not scored.
Observations are weighted by their RA/Dec uncertainties and correlation (
sigma_corr). The timing uncertaintytime_sigmais not used.This is the appropriate tool when the arc is short and MCMC cannot explore the ridge, or when multiple orbital families may be consistent with the data.
- Parameters:
observations (list) – At least 3 optical
Observationobjects. Radar observations are ignored.num_draws (int) – Number of orbit samples to return. Default 1000.
temperature (float) – Likelihood temperature. 1.0 gives the true Bayesian posterior. Higher values produce a softer distribution that is easier to sample but less statistically rigorous. Must be finite and positive. Default is 10.0, producing results similar to JPL Scout.
seed (int) – RNG seed. The same observations and seed give the same draws for a given build of kete. Default 0.
- Return type:
- kete.orbit_fitting.initial_orbit_determination(observations)
Compute an initial orbit from observations.
Returns candidate orbital states sorted by ascending residual score (best first). The score is the trimmed-mean angular residual (radians squared) plus a soft eccentricity penalty.
- Parameters:
observations (list[Observation]) – At least 2 optical observations.
- Returns:
(score, state)pairs sorted by ascending score. States are at the midpoint of the selected observational arc.- Return type:
list[tuple[float, State]]
- kete.orbit_fitting.lambert(r1, r2, dt, prograde=True, max_revs=0)
Solve Lambert’s problem for a Keplerian transfer.
Given two heliocentric position vectors and a transfer time, compute the velocity vectors at departure and arrival that connect them via two-body (Keplerian) motion.
When
max_revsis 0 only the zero-revolution (direct) solution is returned. Formax_revs > 0the solver also finds multi-revolution solutions up to that limit; the result list contains one pair per solution, ordered by ascending number of revolutions.- Parameters:
r1 (Vector) – Heliocentric position at departure (AU).
r2 (Vector) – Heliocentric position at arrival (AU).
dt (float) – Transfer time in days. Must be positive.
prograde (bool) – If True (default), selects the short-way transfer (transfer angle less than 180 degrees for prograde orbits). If False, selects the long-way transfer.
max_revs (int) – Maximum number of complete revolutions to include. Default is 0 (single direct transfer only).
- Returns:
[(v1, v2), ...]– velocity pairs atr1andr2for each solution found.- Return type:
- Raises:
ValueError – If
dt <= 0, positions are zero-length, or positions are nearly collinear (transfer angle near 0 or 180 degrees).RuntimeError – If the iterative solver fails to converge.
- kete.orbit_fitting.mpc_obs_to_observations(mpc_obs, apply_over_obs_reweight=True, debias=True)
Convert a list of MPCObservation objects to fitting Observations.
Only optical (RA/Dec) observations are supported. Each MPCObservation is converted to an
Observation.opticalwith the observer state computed from the MPC observatory code (ground-based), the stored spacecraft position, or the stored roving observer location. Ground-station positions use the Earth orientation of the loaded PCK kernels. Before their coverage starts, they use the approximate Earth orientation ofapprox_earth_pos_to_ecliptic(). Observations with an unknown observatory code, or with an epoch outside the loaded planetary ephemeris, are skipped with a logged warning.Per-observatory uncertainties are applied when available from the pre-computed residual table. When no table entry exists for an observatory code, a fallback based on the epoch and observation type is used.
When
debiasis True, the EFCC18 star-catalog bias correction is applied using the catalog code stored on each observation (column 72 of the 80-char format). Observations with an unknown or blank catalog code are passed through unchanged.- Parameters:
mpc_obs (list of MPCObservation) – The observations to convert.
apply_over_obs_reweight (bool, optional) – When True (default), inflate sigma by sqrt(n/4) for groups of more than 4 observations from the same observatory on the same night, following Veres et al. 2017. Spacecraft observations are exempt.
debias (bool, optional) – When True (default), apply the EFCC18 star-catalog bias correction. Requires the JPL
debias_2018.tgzarchive, downloaded on first use.
- Returns:
One
Observation.opticalper input observation that has an observer state.- Return type:
list[Observation]
Examples
import kete lines = [...] # 80-char MPC observation lines mpc_obs = kete.orbit_fitting.MPCObservation.from_lines(lines) observations = kete.orbit_fitting.mpc_obs_to_observations(mpc_obs) fit = kete.orbit_fitting.fit_orbit(initial_state, observations)