Create app/engine.py
Browse files- app/engine.py +114 -0
app/engine.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import swisseph as swe
|
| 2 |
+
import math
|
| 3 |
+
import datetime
|
| 4 |
+
import os
|
| 5 |
+
from .models import GeoLocation, PlanetPosition, HexagramAddress, CognitiveProfile
|
| 6 |
+
|
| 7 |
+
# CONSTANTS
|
| 8 |
+
ARC_GATE = 5.625
|
| 9 |
+
ARC_LINE = 0.9375
|
| 10 |
+
ARC_COLOR = 0.15625
|
| 11 |
+
ARC_TONE = 0.02604166
|
| 12 |
+
ARC_BASE = 0.00520833
|
| 13 |
+
|
| 14 |
+
class PrecisionEngine:
|
| 15 |
+
def __init__(self):
|
| 16 |
+
# Point to the data files we downloaded in Dockerfile
|
| 17 |
+
ephe_path = os.getenv("SE_EPHE_PATH", "/app/ephe")
|
| 18 |
+
swe.set_ephe_path(ephe_path)
|
| 19 |
+
|
| 20 |
+
def _get_julian_day(self, dt: datetime.datetime) -> float:
|
| 21 |
+
# Calculate UT (Universal Time) Julian Day
|
| 22 |
+
return swe.julday(dt.year, dt.month, dt.day, dt.hour + dt.minute/60.0 + dt.second/3600.0)
|
| 23 |
+
|
| 24 |
+
def _calculate_solar_arc_regression(self, natal_jd: float) -> float:
|
| 25 |
+
"""
|
| 26 |
+
Solves for the exact moment the Sun was 88 degrees behind natal position.
|
| 27 |
+
"""
|
| 28 |
+
# 1. Get Natal Sun Longitude
|
| 29 |
+
res = swe.calc_ut(natal_jd, swe.SUN)
|
| 30 |
+
natal_sun_long = res[0][0]
|
| 31 |
+
|
| 32 |
+
target_long = (natal_sun_long - 88.0) % 360.0
|
| 33 |
+
|
| 34 |
+
# 2. Estimate 88 days back
|
| 35 |
+
current_jd = natal_jd - 88.0
|
| 36 |
+
|
| 37 |
+
# 3. Newton-Raphson Iteration to zero in
|
| 38 |
+
for _ in range(5):
|
| 39 |
+
res = swe.calc_ut(current_jd, swe.SUN)
|
| 40 |
+
current_long = res[0][0]
|
| 41 |
+
|
| 42 |
+
delta = target_long - current_long
|
| 43 |
+
if delta > 180: delta -= 360
|
| 44 |
+
if delta < -180: delta += 360
|
| 45 |
+
|
| 46 |
+
if abs(delta) < 0.00001:
|
| 47 |
+
break
|
| 48 |
+
|
| 49 |
+
current_jd += delta
|
| 50 |
+
|
| 51 |
+
return current_jd
|
| 52 |
+
|
| 53 |
+
def _decode_fractal(self, longitude: float) -> HexagramAddress:
|
| 54 |
+
l = longitude % 360
|
| 55 |
+
gate = math.floor(l / ARC_GATE) % 64 + 1
|
| 56 |
+
rem_g = l % ARC_GATE
|
| 57 |
+
line = math.floor(rem_g / ARC_LINE) + 1
|
| 58 |
+
rem_l = rem_g % ARC_LINE
|
| 59 |
+
color = math.floor(rem_l / ARC_COLOR) + 1
|
| 60 |
+
rem_c = rem_l % ARC_COLOR
|
| 61 |
+
tone = math.floor(rem_c / ARC_TONE) + 1
|
| 62 |
+
rem_t = rem_c % ARC_TONE
|
| 63 |
+
base = math.floor(rem_t / ARC_BASE) + 1
|
| 64 |
+
|
| 65 |
+
return HexagramAddress(gate=gate, line=line, color=color, tone=tone, base=base)
|
| 66 |
+
|
| 67 |
+
def calculate_chart(self, dt: datetime.datetime, loc: GeoLocation, mode: str = "Personality") -> dict:
|
| 68 |
+
jd_utc = self._get_julian_day(dt)
|
| 69 |
+
|
| 70 |
+
if mode == "Design":
|
| 71 |
+
jd_utc = self._calculate_solar_arc_regression(jd_utc)
|
| 72 |
+
|
| 73 |
+
# Set Topocentric Observer
|
| 74 |
+
swe.set_topo(loc.longitude, loc.latitude, loc.altitude)
|
| 75 |
+
|
| 76 |
+
planets = {}
|
| 77 |
+
bodies = {
|
| 78 |
+
"Sun": swe.SUN, "Moon": swe.MOON, "Mercury": swe.MERCURY,
|
| 79 |
+
"Venus": swe.VENUS, "Mars": swe.MARS, "Jupiter": swe.JUPITER,
|
| 80 |
+
"Saturn": swe.SATURN, "Uranus": swe.URANUS, "Neptune": swe.NEPTUNE,
|
| 81 |
+
"Pluto": swe.PLUTO, "North Node": swe.MEAN_NODE
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
for name, pid in bodies.items():
|
| 85 |
+
flags = swe.FLG_TOPOCTR | swe.FLG_SPEED | swe.FLG_SWIEPH
|
| 86 |
+
res = swe.calc_ut(jd_utc, pid, flags)
|
| 87 |
+
|
| 88 |
+
planets[name] = PlanetPosition(
|
| 89 |
+
name=name,
|
| 90 |
+
longitude=res[0][0],
|
| 91 |
+
latitude=res[0][1],
|
| 92 |
+
speed=res[0][3],
|
| 93 |
+
house=1,
|
| 94 |
+
is_retrograde=res[0][3] < 0
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
return planets
|
| 98 |
+
|
| 99 |
+
def analyze_cognitive(self, p_sun_long, p_node_long, d_sun_long, d_node_long) -> CognitiveProfile:
|
| 100 |
+
d_sun = self._decode_fractal(d_sun_long)
|
| 101 |
+
d_node = self._decode_fractal(d_node_long)
|
| 102 |
+
p_sun = self._decode_fractal(p_sun_long)
|
| 103 |
+
p_node = self._decode_fractal(p_node_long)
|
| 104 |
+
|
| 105 |
+
colors = {1: "Fear", 2: "Hope", 3: "Desire", 4: "Need", 5: "Guilt", 6: "Innocence"}
|
| 106 |
+
|
| 107 |
+
return CognitiveProfile(
|
| 108 |
+
digestion=colors.get(d_sun.color, "Unknown"),
|
| 109 |
+
environment=f"Color {d_node.color}",
|
| 110 |
+
perspective=f"Color {p_node.color}",
|
| 111 |
+
motivation=colors.get(p_sun.color, "Unknown"),
|
| 112 |
+
orientation_body="Left" if d_sun.tone <= 3 else "Right",
|
| 113 |
+
orientation_mind="Left" if p_sun.tone <= 3 else "Right"
|
| 114 |
+
)
|