Astrology Engine

Find the shared usable interval

A successful source fit gives us kernel files, but each file may cover different dates and describe positions from a different origin. Before fitting chart directions, the native builder finds dates shared by its inputs and asks for position and velocity vectors expressed from a common origin.

Before this lesson

Read “Fit the source position curves” and “Put time and distance on a scale” first. You need addition, multiplication, inequalities and the distinction between position and velocity. No trigonometry is needed here.

Read the preceding lesson.

What you will learn

You will be able to decode segment extents, intersect their time windows, reserve the builder’s sampling margins, identify kernel targets, and translate an asteroid state to the solar-system barycenter.

Dotted-underlined terms open a definition beside the text. Select one to read more, then close it to continue.

Each step has its own check. Pass every step to complete the lesson. Your answers and checked steps are saved in this browser, so you can continue after leaving or reloading.

1. Read segment extents

Before using a file of recorded positions, we need to know which part of the file belongs to each object and which dates that part describes. The A kernel of position and velocity data An SPK file stores trajectories for identified objects over specified times. A kernel is a data file used by the planetary calculation software; it is not a completed chart.Its segments describe particular targets, centers and kinds of trajectory records. The planning reader in this lesson handles the supplied Chebyshev kernels, rather than every possible SPK layout. Jet Propulsion Laboratory: SPK states, centers and Chebyshev directories from the source-fitting lesson is divided into segments. Each segment has a summary that points to its data, and the preparation tool uses those pointers to plan its reads.

The addresses in those summaries do not count bytes in the way an array index does. They follow the Addresses in a Double Precision Array File DAF is the file architecture used by SPK. It organizes arrays of double-precision numbers and summaries that describe them.A DAF word address counts eight-byte words starting from one. A byte offset in a program counts bytes starting from zero. That difference explains the subtraction in the address conversion. Jet Propulsion Laboratory: DAF addresses and documented revision history convention: count eight-byte words, starting at one. Consider an invented segment occupying words 385 through 421, including both ends. Word 385 begins after 384 words, so its byte offset is 384 × 8 = 3072. The last word ends at byte offset 421 × 8 = 3368.

The end offset is exclusive: byte 3368 belongs to what follows the segment. Subtracting the start from that end gives 3368 − 3072 = 296 bytes. For any inclusive first word a and last word b, the same conversion is:

start byte = (a − 1) × 8; exclusive end byte = b × 8

The planner selects the first matching summary in the parsed list for each requested target. A missing target is an error. The resulting SegmentPlan keeps the data-type identifier alongside the extent, but this metadata reader does not choose a parsing algorithm from that type.

For these Chebyshev segments, the final 32 bytes contain a small directory of four little-endian floating-point numbers. In our byte example, that directory starts at 3368 − 32 = 3336. The first value, INIT, gives the first time; INTLEN gives the length of each time interval; RSIZE gives the record size; and N gives the number of records. INIT and INTLEN are measured in Time units in the segment directory Here ET is elapsed seconds from noon January 1, 2000 on the Barycentric Dynamical Time (TDB) scale, following the convention established in the time-and-units lesson.Coverage endpoints use this same reference and scale. Converting them to days changes the unit, not the time scale. Jet Propulsion Laboratory: SPK states, centers and Chebyshev directories seconds; RSIZE and N are counts.

Suppose the invented directory reads INIT = 86400 s, INTLEN = 172800 s, RSIZE = 11 and N = 3. Three intervals occupy 172800 × 3 = 518400 seconds. Adding that span to the start gives 604800 seconds, so the segment covers days 1 through 7 after the reference. RSIZE describes record layout, but does not enter this time calculation.

coverage = [INIT, INIT + INTLEN × N] ET seconds

Square brackets include both endpoints. This reader derives its dates from the directory, rather than using the dates advertised in the summary. It requires at least 32 bytes, INTLEN > 0, RSIZE ≥ 2 and N ≥ 1, then casts the counts to unsigned integers. Those checks establish plausibility, not complete validation of finite values, whole-number counts, record layout or an evaluable path through the ephemeris data.

Where the address convention is documented

The Jet Propulsion Laboratory’s Navigation and Ancillary Information Facility maintains the DAF architecture and documents its one-based word addresses. The guide’s revision history records contributors including E. D. Wright, N. J. Bachman and B. V. Semenov. These are contributors to that documented format, rather than an attribution of the local reader.

Connect this step to the source

tools/dataset-builder/src/kernel/daf_index.rs

plan_segments; read_cheby_meta; ChebyshevMeta::coverage_window

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

Count the first and last word. Accepted tolerance: ±0 bytes. Omit units and commas.

Return seconds after the reference. Accepted tolerance: ±0 ET seconds. Omit units and commas.

2. Intersect coverage and reserve sampling margins

Knowing one segment’s dates is only a start. A direction calculation may need an object, Earth and other states on the same date. Like arranging a meeting when everyone has a different availability window, we must find dates that work for every required input. The builder first finds a shared window within each kernel, then compares the planet, Ceres and Chiron windows.

Take invented windows in days after the reference: planets [0, 120], Ceres [2, 118] and Chiron [1, 115]. Day 0 is too early for the asteroids, and day 120 is too late for them. The latest start is day 2 and the earliest end is day 115, leaving the common interval [2, 115]. The next step develops the general rule behind this overlap.

The builder narrows that overlap again before fitting. The later light-travel-time calculation can ask for a state before the requested date, with a delay of up to one day. To leave room for that request, the builder adds one day to the common start. It also reserves one day at the high edge by subtracting it from the end. Our example becomes [3, 114], with duration 114 − 3 = 111 days.

fit lo = max(planet lo, Ceres lo, Chiron lo) + 86400 s

fit hi = min(planet hi, Ceres hi, Chiron hi) − 86400 s

Here lo and hi name the lower and upper bounds; max chooses the largest start and min the smallest end. The implementation performs this arithmetic in ET seconds. Thus the example interval is [259200, 9849600] ET seconds. The day labels are elapsed durations from the reference, not UTC calendar dates, and removing a day from each edge shortens the span by two days.

Every required source matters, even while fitting one target. Within the planet kernel the requested identifiers are 10, 3, 399, 301, 1, 2, 4, 5, 6, 7, 8 and 9. The asteroid identifiers are 2000001 for Ceres and 2002060 for Chiron. We will connect these numbers to their objects later in this lesson.

Why there is no universal margin value here

SPK documentation describes coverage as the times at which ephemeris data is available. Reserving one day at each edge is this repository’s preparation policy; it is not a margin imposed by NAIF or an astronomical constant.

Connect this step to the source

tools/dataset-builder/src/main.rs

fit

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

First find the latest start. Accepted tolerance: ±0 days after reference. Omit units and commas.

Subtract the new start from the new end. Accepted tolerance: ±0 days. Omit units and commas.

3. Intersect interval bounds

The common window in the preceding step came from a simple requirement: a requested time must belong to every input window. Suppose three sources cover [10, 70], [20, 60] and [15, 55] seconds. A time before 20 seconds misses the second source, while a time after 55 seconds misses the third. Their common window is therefore [20, 55], lasting 55 − 20 = 35 seconds.

This common part is called the intersection. For a single closed interval [lo, hi], a time t belongs when t ≥ lo and t ≤ hi. To satisfy every lower bound, choose the largest lo. To satisfy every upper bound, choose the smallest hi. That explains why the largest-start/smallest-end rule works:

shared lo = max(all lower bounds)

shared hi = min(all upper bounds); intersection exists when shared lo ≤ shared hi

The helper intersect_windows begins with the first interval and combines each remaining interval using these two operations. With no input intervals, it returns None. Otherwise it returns the merged interval only when lo ≤ hi. No sorting is needed because every comparison tightens the same two bounds.

Equality matters. Windows [10, 20] and [20, 30] both contain the instant 20, so their intersection is [20, 20]. Its duration is zero, even though it contains an instant. Windows [10, 19] and [20, 30] have no common time: their proposed lower bound 20 exceeds their upper bound 19.

Taking an intersection differs from taking a union. A union includes times that belong to at least one window; an intersection includes only times belonging to all of them. This helper does not bridge gaps or make a zero-duration overlap useful for fitting. It also has no general validator for malformed or nonfinite inputs. The rule and teaching code assume finite, ordered endpoints from usable kernel metadata.

Coverage and the interval rule

The SPK guide supplies the ephemeris context for these windows. The max/min rule itself follows from satisfying every bound; it does not require a special astronomical convention or a named inventor.

See the teaching TypeScript
type TimeWindow = { lo: number; hi: number };
const intersectFiniteWindows = (windows: readonly TimeWindow[]): TimeWindow | null => {
  if (windows.length === 0) return null;
  const lo = Math.max(...windows.map(({ lo }) => lo));
  const hi = Math.min(...windows.map(({ hi }) => hi));
  return lo <= hi ? { lo, hi } : null;
}; // teaching version: assumes finite, ordered endpoints

Finite, correctly labeled inputs are assumed. This demonstrates the arithmetic; it does not fetch data or replace the engine.

Connect this step to the source

src/coverage_window.rs

intersect_windows

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

Choose the smallest upper endpoint. Accepted tolerance: ±0 seconds. Omit units and commas.

2. The helper receives [0, 10] and [10, 20]. What does it return?

4. Evaluate geometric source states

We can now choose a time shared by the inputs. To turn the recorded trajectories into a direction, we next need coordinates at that time. A position tells us where an object is, while a velocity tells us how that position is changing. Together the three position components (x, y, z) and the three velocity components (vₓ, vᵧ, v_z) form a state. For example, a velocity of 2 km/s along x changes x by about 2 km in one second if it stays constant.

Coordinates only make sense once their measuring point and axes are specified. Imagine two locations measured from the same starting point O: the target is at (12, 8, 1) km and the new center C at (2, 3, 1) km. To describe the target from C, subtract C’s coordinates. The result is (10, 5, 0) km. If their velocities from O are (4, 2, 0) and (1, 1, 0) km/s, subtraction likewise gives (3, 1, 0) km/s.

r_target/C = r_target/O − r_C/O

v_target/C = v_target/O − v_C/O

Here r denotes position and v velocity; the text after the slash identifies the origin. Each subtraction uses matching components at the same time with aligned axes and compatible units. These invented numbers illustrate a change of origin, rather than interpolation of a real orbit.

The preparation tool delegates the actual trajectory evaluation to ANISE. Its eval_state_from_spk obtains the body identifier, constructs the body’s J2000 frame and calls translate_geometric with SSB_J2000 as the center frame. SSB means solar-system barycenter, the system’s center of mass rather than the Sun’s center. The centered variant constructs a frame for the requested center instead.

ANISE finds and evaluates the required path through the kernel data. Both variants return Eqj-tagged position in kilometers and velocity in kilometers per second, using the The orientation of the source vectors EQJ labels the equatorial axis orientation used for the source-state vectors. The origin is specified separately: two EQJ vectors can still be measured from different centers.Before adding or subtracting vectors, match the origin relationships, orientation, time and units. An orientation tag alone does not establish all four. Jet Propulsion Laboratory: SPK states, centers and Chebyshev directoriesANISE: geometric translation API. A missing body identifier or unavailable ephemeris path returns an error. Reading a segment’s trailer alone did not prove that this path exists.

The library boundary behind this calculation

NAIF’s SPK system describes center-relative trajectories and connections between them. ANISE documents geometric translation as translation without aberration corrections. The repository uses ANISE during native preparation; the tutorial and Wasm chart runtime do not provide a second SPK evaluator.

Connect this step to the source

tools/dataset-builder/src/kernel/store.rs

eval_state_from_spk; eval_state_centered_from_spk

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

Keep the sign. Accepted tolerance: ±0 km. Omit units and commas.

Apply the same subtraction to velocities. Accepted tolerance: ±0 km/s. Omit units and commas.

3. A successful barycentric geometric state request returns which result?

5. Identify sampled and derived bodies

A function call also needs to identify which object’s state to evaluate. Names such as “Jupiter” are convenient for us, but a kernel request uses an integer identifier. The conventions maintained by the The source of the target identifiers NAIF is the Jet Propulsion Laboratory group whose planetary calculation software defines the integer identifiers used here. A target number identifies an object or center of mass; it does not specify the vector’s axes, units or date. Jet Propulsion Laboratory: integer identifiers and barycenters distinguish a planet’s center from the center of mass of the planet and its surrounding system. We need to follow those distinctions when translating the engine’s body names into requests.

To see what a center of mass means, put masses of 3 and 1 arbitrary mass units at x = 0 and x = 8 km. Multiply each position by its mass, add the results, then divide by the total mass: (3 × 0 + 1 × 8) / (3 + 1) = 2 km. The heavier mass pulls the average closer to its position. This weighted location is the A center of mass A barycenter is a center of mass. For two objects on a line, their positions contribute in proportion to their masses.The solar-system barycenter and the Sun’s center are different origins. A planet-system barycenter likewise need not be the center of the visible planet. Jet Propulsion Laboratory: integer identifiers and barycenters.

x_bar = (m₁x₁ + m₂x₂) / (m₁ + m₂)

The symbols m₁ and m₂ denote the masses, x₁ and x₂ their positions along one axis, and x_bar the weighted average position. The bar is a label for that average. We use this example to understand the term; production asks the kernel for the chosen identifier instead of calculating planetary mass averages here.

The engine maps Sun to 10, Earth to 399 and Moon to 301. Its remaining planet mappings are Mercury 1, Venus 2, Mars 4, Jupiter 5, Saturn 6, Uranus 7, Neptune 8 and Pluto 9. Codes 1 through 9 identify planet-system barycenters. In particular, Body::Mars requests 4 rather than Mars-center code 499, and Body::Jupiter requests 5 rather than Jupiter-center code 599. The enum label does not change the identifier’s meaning.

Body::Jupiter → 5; Body::NorthNode → None

Ceres maps to 2000001 and Chiron to 2002060. NorthNode, SouthNode, AscendantSymbol, Midheaven, Descendant and ImumCoeli have no kernel identifier: they map to None. Their directions come from later calculations or chart conventions. To obtain a node, follow that derived calculation rather than inventing a trajectory request.

What the identifier names distinguish

NAIF documents the separate conventions for planet centers and planet-system barycenters. These identifiers make requests consistent across software; the engine selects particular codes from that convention.

Connect this step to the source

src/types.rs

Body::naif_id

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

Use the mass-weighted average. Accepted tolerance: ±0.000001 km. Omit units and commas.

2. Which target does this engine request for Body::Jupiter?
3. What should a kernel state request do for NorthNode?

6. Place asteroid states at the solar-system origin

The asteroid file measures its target from the Sun, while the planet provider measures Earth from the solar system’s center of mass. We cannot subtract those coordinates directly: they have different origins. First place the asteroid at the same origin as Earth by adding the Sun’s position from that origin.

Use invented x-only states at one TDB epoch. The asteroid is 149597870.7 km from the Sun along x, and the Sun is at −14959787.07 km from the solar-system barycenter. Adding them gives the asteroid’s barycentric x position: 149597870.7 − 14959787.07 = 134638083.63 km. The minus sign locates the Sun on the negative side of the shared x axis.

r_asteroid/SSB = r_asteroid/Sun + r_Sun/SSB

As before, r means position and the slash names its origin. SSB abbreviates solar-system barycenter. Apply the same addition to velocity: an asteroid/Sun velocity of 1 km/s and Sun/SSB velocity of −0.25 km/s sum to 0.75 km/s.

v_asteroid/SSB = v_asteroid/Sun + v_Sun/SSB

The DualStateProvider obtains the asteroid relative to center 10, the Sun, from the asteroid store. It obtains the Sun relative to the barycenter from the planet store. Both calls use the same TDB epoch and J2000 orientation. Their position and velocity sums still use kilometers and kilometers per second, so the adapter next converts them to the units needed by the direction calculations.

Dividing 134638083.63 km by 149597870.7 km per AU gives 0.9 AU. For velocity, multiplying 0.75 km/s by 86400 seconds per day gives 64800 km/day; division by the same length constant then gives approximately 0.00043316125 AU/day. The general component conversions are:

position AU = position km / 149597870.7

velocity AU/day = velocity km/s × 86400 / 149597870.7

The planet provider performs these same unit conversions for its barycentric states. There is exactly one conversion at the provider boundary. Adding the Sun changes the origin; converting units changes the numerical scale. Neither operation tilts the axes.

Connecting center-relative states

NAIF identifies the Sun as center 10 and the solar-system barycenter as center 0. Its SPK system permits connecting states through their centers. The two-store provider applies that idea to the Sun-relative asteroid file and the planet kernel.

Connect this step to the source

tools/dataset-builder/src/astro/provider.rs

DualStateProvider::state_at

Paths refer to the astrology-engine repository. Examples use invented inputs; a successful exercise is not an astronomical-accuracy test.

Sources for this section

Apply this step

Answer every part, then check. You can retry as often as you like.

Add the two center-relative components. Accepted tolerance: ±0.000001 AU. Omit units and commas.

There are 86400 seconds per day. Accepted tolerance: ±0 km/day. Omit units and commas.

3. The asteroid provider is configured for Ceres. What happens when the caller requests Earth?

Your lesson checks

0 of 6 steps passed.

Use the feedback beside each check to retry any unfinished step.