Astrology Engine

Measure angular motion

A position tells us where a body appears at one instant. To describe how that position is changing, the chart also reports angular motion per day. We estimate it from a pair of nearby longitudes, then handle the circle’s zero-degree seam so that a small forward motion does not look like a nearly complete backward turn.

Before this lesson

Read “Read a position from a curve.” Keep the distinction between the published interval and each fitted series in view. All longitudes in the examples are invented; no measured orbit is implied.

Read the preceding lesson.

What you will learn

You will be able to estimate daily angular motion, interpret its sign, and calculate a shortest signed difference across the seam, including the engine’s half-turn tie rule.

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. Measure change across the requested instant

Suppose a body’s longitude is 10.2° half a day before our requested time and 11.4° half a day after it. The change is 1.2° over one day, so the estimated speed is +1.2°/day. The plus sign means longitude increased. Sampling on both sides centers the measurement on the instant we want to describe, rather than putting the whole measurement before or after it.

speed ≈ signed_difference(longitude(t + h), longitude(t − h)) / (2h), with h = 0.5 day

Here h is half the sampling interval, measured in days. The denominator is 2 × 0.5 = 1 day. The implementation adds and subtracts time-library durations, converts the resulting instants to ephemeris seconds, and evaluates longitude twice. Because our speed unit is degrees per day, we divide by the interval in days, not by its equivalent 86,400 seconds.

This is a centered finite difference: “finite” means we use a definite separation in time, rather than a limiting separation of zero. The output estimates motion around the central instant; it is not the analytic derivative of the Chebyshev polynomial. A curved trajectory can change speed inside the interval, and the two samples may even fall in different stored segments. The engine keeps this half-day choice fixed.

The place accessor evaluates longitude and declination at the central instant, then computes speed from the two offset longitude samples. A successful central position does not guarantee a successful place: either offset sample can lie beyond fitted support. With support [0, 172800] s, the central instant 21600 s is readable, but its earlier sample would be −21600 s, so speed evaluation fails. The public domain’s preparation margins are intended to prevent this for a correctly prepared dataset.

Numerical differentiation and this engine’s choice

The National Institute of Standards and Technology documents estimating derivatives through differences of samples. That is the numerical-method background. The fixed half-day step is this engine’s policy; the reference does not prescribe it for all ephemerides, and no historical inventor is assigned to that local choice.

Connect this step to the source

src/cheb/eval.rs

Ephemeris::fd_speed; Ephemeris::place

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.

Give at least six decimal places if needed; tolerance ±0.00000001. Accepted tolerance: ±1e-8 degrees/day. Omit units and commas.

Give at least six decimal places if needed; tolerance ±0.00000001. Accepted tolerance: ±1e-8 degrees/day. Omit units and commas.

3. What does the speed value represent?
4. A central longitude succeeds at the very start of its fitted series. Must place also succeed?

2. Subtract directions without crossing the wrong way

Ordinary subtraction works for nearby directions away from the seam. It fails if our earlier longitude is 359.8° and the later one is 0.2°: subtraction gives −359.6°. On the circle these directions are only 0.4° apart in the forward direction. We need to choose the shortest signed change, adding or subtracting full turns as necessary.

Let a be the later angle and b the earlier one. First take a − b and its nonnegative remainder modulo 360: the value left after removing complete turns. Then shift and wrap once more to place the result between −180° inclusive and +180° exclusive. The source uses the following expression; rem means nonnegative remainder.

signed_delta(a, b) = rem(rem(a − b, 360) + 540, 360) − 180

For a = 0.2° and b = 359.8°, the raw subtraction is −359.6°. Its nonnegative remainder is 0.4°. Add 540° to get 540.4°; removing one full turn gives 180.4°. Subtracting 180° gives +0.4°. With the one-day sampling interval, that is +0.4°/day. Reversing the two inputs instead gives −0.4°, so the order matters.

At exactly a half-turn there are two equally short directions. The implementation selects −180°, consistent with its interval [−180, 180). Thus signed_delta(180, 0) is −180°, and signed_delta(0, 180) is also −180°. This is a deterministic tie convention, not evidence that either physical direction was observed.

Taking the shortest change also loses information about complete turns between samples. The two endpoint angles alone cannot reveal whether a body made extra revolutions, or moved more than half a turn during the interval. This calculation reports the chosen signed angular change, not an unwrapped travel history. The same helper later supports event searches.

Why this helper has no discovery story

Wrapping directions follows the circle’s repeated full turns. The exact nested-remainder expression and half-turn tie are current engine conventions, documented by the linked source. They are not attributed to Clenshaw or to the history of numerical differentiation.

See the teaching TypeScript
const normalize360 = (value: number): number => { const remainder = value % 360; return remainder < 0 ? remainder + 360 : remainder; };
const signedDelta = (after: number, before: number): number => normalize360(normalize360(after - before) + 540) - 180;
const dailyMotion = (before: number, after: number): number => signedDelta(after, before) / (2 * 0.5);

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/astro/find_moment.rs

signed_delta; normalize360

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.

Give at least six decimal places if needed; tolerance ±0.00000001. Accepted tolerance: ±1e-8 degrees/day. Omit units and commas.

Give at least six decimal places if needed; tolerance ±0.00000001. Accepted tolerance: ±1e-8 degrees/day. Omit units and commas.

Give at least six decimal places if needed; tolerance ±0.00000001. Accepted tolerance: ±1e-8 degrees. Omit units and commas.

4. Can these two endpoint samples distinguish a +1° change from a +361° travel history?

Your lesson checks

0 of 2 steps passed.

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