Astrology Engine

Find the horizon and meridian angles

The observer’s meridian orientation tells us how the equator is turned above a location. A chart records angles along the ecliptic instead. We will connect those two circles, find the directions associated with the meridian and horizon, and append their opposite points to the chart.

Before this lesson

Read “Turn time into sky orientation.” We will use its local meridian angle α, north-positive latitude φ and true obliquity ε. The earlier rotation lessons introduced sine and cosine; this lesson explains tangent and the two-argument inverse tangent. Examples use invented angles, including a deliberately simplified 30° obliquity.

Read the preceding lesson.

What you will learn

Evaluate the engine’s two angle formulas, preserve their quadrants, construct the opposite longitudes, and recognize the zero-valued output placeholders.

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. Connect the equator to the ecliptic

Picture two circles crossing on a sphere: the equator and the tilted ecliptic. The local meridian cuts them at generally different angle readings. In ordinary chart terminology the ecliptic meridian angle is the midheaven, often shortened to MC; the horizon intersection associated with rising is the Ascendant. We will calculate the particular branches returned by this engine rather than assuming every possible location has the same geometry.

An angle can be recovered from two component values. On a unit circle, cosine supplies the horizontal component x and sine supplies the vertical component y. The function atan2(y, x) returns the direction of that pair, retaining which quadrant contains it. For example, pairs (x, y) = (1, 1) and (−1, −1) have the same ratio y/x, but point at 45° and −135°. A single-argument inverse tangent of the ratio would lose this distinction.

Tangent, written tan, is sin divided by cos. It enters the horizon formula because tilting the observer’s horizon depends on latitude. The source formulas are below. α is the local meridian right ascension, ε the true obliquity and φ the geographic latitude. Each input is converted from degrees to radians before a trigonometric function; each atan2 result is converted back to degrees before wrapping.

To see why MC needs the tilt, take an ecliptic direction at longitude λ. Its equatorial components are x = cos λ and y = sin λ × cos ε, because the ecliptic’s sideways component projects onto the equator by cos ε. The meridian angle therefore satisfies tan α = tan λ × cos ε. Recovering λ gives the MC component pair below; atan2 retains the branch without dividing by a possibly zero component.

MC = wrap(atan2(sin α, cos α × cos ε) × 180/π)

Asc = wrap(atan2(cos α, −(sin ε × tan φ + cos ε × sin α)) × 180/π)

Work through the invented α = 30°, ε = 30°, φ = 0°. For MC, y = sin 30° = 0.5 and x = cos 30° × cos 30° = 0.75. Thus atan2(0.5, 0.75) gives 33.690067526° after conversion. For Asc, y = cos 30° = 0.866025404 and x = −(0.5 × 0 + 0.866025404 × 0.5) = −0.433012702. A positive y with a negative x lies in the second quadrant; the result is 116.565051177°.

The latitude affects Asc but does not enter MC. At the same α and ε with φ = 30°, tan φ ≈ 0.577350269, making the Asc x component approximately −0.721687836 and giving Asc ≈ 129.805571092°. This shows why changing the observer’s latitude can change the horizon direction without changing the meridian longitude.

What the names and formulas establish

Swiss Ephemeris documents the Ascendant, MC and polar complications, including differing branch choices among programs. That is context rather than an oracle for this engine. These particular formulas are taken from src/astro/houses.rs::compute_angles; the repository does not establish their original inventor. The ECMAScript specification documents the teaching snippet’s atan2 behavior.

See the teaching TypeScript
const wrap = (x: number): number => { const r = x % 360; return r < 0 ? r + 360 : r; };
const chartAngles = (ramcDegrees: number, latitudeDegrees: number, obliquityDegrees: number) => {
  const r = Math.PI / 180;
  const a = ramcDegrees * r, phi = latitudeDegrees * r, e = obliquityDegrees * r;
  return {
    midheaven: wrap(Math.atan2(Math.sin(a), Math.cos(a) * Math.cos(e)) / r),
    ascendant: wrap(Math.atan2(Math.cos(a), -(Math.sin(e) * Math.tan(phi) + Math.cos(e) * Math.sin(a))) / r)
  };
};

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/houses.rs

compute_angles

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.

Enter a number in degrees; absolute tolerance ±0.001. Accepted tolerance: ±0.001 degrees. Omit units and commas.

Enter a number in degrees; absolute tolerance ±0.001. Accepted tolerance: ±0.001 degrees. Omit units and commas.

Enter a number in degrees; absolute tolerance ±0.001. Accepted tolerance: ±0.001 degrees. Omit units and commas.

4. Why keep both arguments in atan2 rather than using atan(y/x)?

2. Find the other ends of the two lines

A line through the sphere reaches the ecliptic on two opposite sides. Once the engine has chosen Asc and MC, it obtains the paired Descendant and imum coeli by adding half a turn. Imum coeli is commonly shortened to IC. These names label chart directions; they do not introduce new sampled bodies.

Descendant = wrap(Asc + 180°); IC = wrap(MC + 180°)

With invented Asc = 350° and MC = 80°, the Descendant is wrap(530°) = 170°, and IC is 260°. angle_longitudes returns [350°, 80°, 170°, 260°] in the order Asc, MC, Descendant, IC. The ordering preserves the labels when the next step zips the angles with their names.

Do not assume the four entries divide the circle into four equal pieces. Opposite pairs are separated by 180°, but Asc and MC need not be 90° apart. In our earlier nontrivial example, Asc − MC is about 82.875°, even though each opposite pair still spans exactly half a circle. House conventions decide how to subdivide intervals later.

A half-turn, rather than another angle solver

The local implementation calculates the opposite points in src/astro/houses.rs::angle_longitudes. Swiss Ephemeris supplies terminology and broader chart-angle context; it does not establish the source or inventor of this repository helper.

See the teaching TypeScript
const wrap = (x: number): number => { const r = x % 360; return r < 0 ? r + 360 : r; };
const oppositeAngles = (ascendant: number, midheaven: number): number[] =>
  [ascendant, midheaven, wrap(ascendant + 180), wrap(midheaven + 180)];

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/houses.rs

angle_longitudes

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.

Enter a number in degrees; absolute tolerance ±0. Accepted tolerance: ±0 degrees. Omit units and commas.

Enter a number in degrees; absolute tolerance ±0. Accepted tolerance: ±0 degrees. Omit units and commas.

3. Must the successive Asc, MC, Descendant and IC entries be 90° apart?

3. Append named angle records

The fifteen-entry body list from lesson 15 is now ready to receive the four angles. calculate_chart pairs the four longitudes with AscendantSymbol, Midheaven, Descendant and ImumCoeli in that order. Each becomes a ChartBody with its wire name and longitude, plus speed = 0 and declination = 0.

For invented Asc = 350°, the first appended record therefore has longitude 350°, speed 0°/day and declination 0°. These zeros are placeholders in a shared output shape. They do not report a measured stationary Ascendant or a physical direction lying on the equator. No centered speed calculation or declination transformation is performed for these four records.

15 body-list entries + 4 angle records = 19 entries in calculate_chart bodies

The resulting nineteen-entry list and the optional house-cusp list are returned as separate fields. The house calculation does not replace the four angles with twelve cusp records. Keeping those two outputs separate lets the next lessons teach house conventions without changing what these angle entries mean.

Names and placeholder fields are interface decisions

Swiss Ephemeris’s programmer documentation also lists Ascendant, MC and ARMC separately, helping distinguish the angles from a meridian right ascension. This repository’s decision to append four ChartBody records with zero speed and declination is its own interface policy; no historical attribution for that policy is established.

Connect this step to the source

src/chart.rs

calculate_chart

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.

Enter a number in entries; absolute tolerance ±0. Accepted tolerance: ±0 entries. Omit units and commas.

Enter a number in degrees/day; absolute tolerance ±0. Accepted tolerance: ±0 degrees/day. Omit units and commas.

3. A Descendant record has declination 0°. What does that field establish?
4. Do twelve house cusps replace the four angle entries in the bodies list?

Your lesson checks

0 of 3 steps passed.

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