Sthira、Drig、Shoola、Navamsha、Niryaana Shoola 与 K.N. Rao 的 Chara 从本盘算出。Lagna Kendradi 不上表。未推送。
217 lines
7.0 KiB
Python
217 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Goel / BVB short graha and rashi strength for the report rashi dashas.
|
|
|
|
Dignity, amsha, and kendradi units follow the short method in
|
|
Predicting through Jaimini Astrology, printed pages 100-108.
|
|
This is not Shadbala and not a house-strength table from another program.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, Mapping
|
|
|
|
try:
|
|
from jaimini import FRIENDSHIP, KARAKA_7, SIGNS, calc_chara_karaka_7
|
|
from narayana_dasha import SIGN_LORDS, _rasi_aspects
|
|
except ImportError: # pragma: no cover - package import
|
|
from scripts.jaimini import FRIENDSHIP, KARAKA_7, SIGNS, calc_chara_karaka_7
|
|
from scripts.narayana_dasha import SIGN_LORDS, _rasi_aspects
|
|
|
|
CLASSICAL_PLANETS = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn")
|
|
_EXALT_SIGN = {
|
|
"Sun": 0,
|
|
"Moon": 1,
|
|
"Mars": 9,
|
|
"Mercury": 5,
|
|
"Jupiter": 3,
|
|
"Venus": 11,
|
|
"Saturn": 6,
|
|
}
|
|
_DEBIL_SIGN = {
|
|
"Sun": 6,
|
|
"Moon": 7,
|
|
"Mars": 3,
|
|
"Mercury": 11,
|
|
"Jupiter": 9,
|
|
"Venus": 5,
|
|
"Saturn": 0,
|
|
}
|
|
_OWN_SIGNS = {
|
|
"Sun": {4},
|
|
"Moon": {3},
|
|
"Mars": {0, 7},
|
|
"Mercury": {2, 5},
|
|
"Jupiter": {8, 11},
|
|
"Venus": {1, 6},
|
|
"Saturn": {9, 10},
|
|
}
|
|
# Moolatrikona bands are degree-in-sign, end exclusive.
|
|
_MOOLATRIKONA = {
|
|
"Sun": ((4, 0.0, 20.0),),
|
|
"Moon": ((1, 3.0, 30.0),),
|
|
"Mars": ((0, 0.0, 12.0),),
|
|
"Mercury": ((5, 15.0, 20.0),),
|
|
"Jupiter": ((8, 0.0, 10.0),),
|
|
"Venus": ((6, 0.0, 15.0),),
|
|
"Saturn": ((10, 0.0, 20.0),),
|
|
}
|
|
_AMSHA_BY_RANK = {1: 70, 2: 60, 3: 50, 4: 40, 5: 30, 6: 20, 7: 10}
|
|
_CHARA_RASHI = {0: 20, 1: 40, 2: 60}
|
|
|
|
|
|
def sign_index(longitude: float) -> int:
|
|
return int(float(longitude) % 360.0 // 30.0)
|
|
|
|
|
|
def degree_in_sign(longitude: float) -> float:
|
|
return float(longitude) % 30.0
|
|
|
|
|
|
def _in_moolatrikona(planet: str, sign_idx: int, degree: float) -> bool:
|
|
for sign, start, end in _MOOLATRIKONA.get(planet, ()):
|
|
if sign == sign_idx and start <= degree < end:
|
|
return True
|
|
return False
|
|
|
|
|
|
def dignity_units(planet: str, sign_idx: int, degree: float) -> int:
|
|
"""Short-method dignity. Whole exalt and debil signs, except Mercury in Virgo."""
|
|
if planet not in CLASSICAL_PLANETS:
|
|
raise KeyError(planet)
|
|
if sign_idx == _DEBIL_SIGN[planet]:
|
|
return 10
|
|
if planet == "Mercury" and sign_idx == 5:
|
|
if degree < 15.0:
|
|
return 70
|
|
if degree < 20.0:
|
|
return 60
|
|
return 50
|
|
if sign_idx == _EXALT_SIGN[planet]:
|
|
return 70
|
|
if _in_moolatrikona(planet, sign_idx, degree):
|
|
return 60
|
|
if sign_idx in _OWN_SIGNS[planet]:
|
|
return 50
|
|
lord = SIGN_LORDS[SIGNS[sign_idx]]
|
|
relation = FRIENDSHIP.get(planet, {})
|
|
if lord in relation.get("friend", []):
|
|
return 40
|
|
if lord in relation.get("enemy", []):
|
|
return 20
|
|
return 30
|
|
|
|
|
|
def kendradi_units(sign_idx: int, ak_sign_idx: int) -> int:
|
|
offset = (int(sign_idx) - int(ak_sign_idx)) % 12
|
|
if offset % 3 == 0:
|
|
return 60
|
|
if offset % 3 == 1:
|
|
return 40
|
|
return 20
|
|
|
|
|
|
def karaka_ranks(planet_lons: Mapping[str, float]) -> Dict[str, int]:
|
|
degrees = {
|
|
planet: degree_in_sign(planet_lons[planet])
|
|
for planet in CLASSICAL_PLANETS
|
|
if planet in planet_lons
|
|
}
|
|
table = calc_chara_karaka_7(degrees).get("karaka_table") or {}
|
|
ranks = {}
|
|
for rank, name in KARAKA_7.items():
|
|
row = table.get(name) or {}
|
|
planet = row.get("planet")
|
|
if planet:
|
|
ranks[str(planet)] = int(rank)
|
|
return ranks
|
|
|
|
|
|
def graha_strengths(planet_lons: Mapping[str, float]) -> Dict[str, Dict[str, int]]:
|
|
ranks = karaka_ranks(planet_lons)
|
|
if "Mars" not in ranks and len(ranks) < 7:
|
|
raise ValueError("short graha strength needs the seven classical planets")
|
|
ak_planet = next(planet for planet, rank in ranks.items() if rank == 1)
|
|
ak_sign = sign_index(planet_lons[ak_planet])
|
|
rows = {}
|
|
for planet in CLASSICAL_PLANETS:
|
|
sign_idx = sign_index(planet_lons[planet])
|
|
degree = degree_in_sign(planet_lons[planet])
|
|
dignity = dignity_units(planet, sign_idx, degree)
|
|
amsha = _AMSHA_BY_RANK[ranks[planet]]
|
|
kendradi = kendradi_units(sign_idx, ak_sign)
|
|
rows[planet] = {
|
|
"sign_index": sign_idx,
|
|
"dignity": dignity,
|
|
"amsha": amsha,
|
|
"kendradi": kendradi,
|
|
"total": dignity + amsha + kendradi,
|
|
"karaka_rank": ranks[planet],
|
|
}
|
|
return rows
|
|
|
|
|
|
def _aspect_units(sign_idx: int, planet_lons: Mapping[str, float], lord: str) -> int:
|
|
contributors = []
|
|
for planet in (lord, "Jupiter", "Mercury"):
|
|
if planet not in contributors:
|
|
contributors.append(planet)
|
|
total = 0
|
|
for planet in contributors:
|
|
if planet not in planet_lons:
|
|
continue
|
|
source = sign_index(planet_lons[planet])
|
|
if source != sign_idx and not _rasi_aspects(source, sign_idx):
|
|
continue
|
|
if planet == lord and planet in {"Jupiter", "Mercury"}:
|
|
total += 120
|
|
else:
|
|
total += 60
|
|
return total
|
|
|
|
|
|
def rashi_strengths(
|
|
planet_lons: Mapping[str, float],
|
|
graha: Mapping[str, Mapping[str, int]] | None = None,
|
|
) -> Dict[int, Dict[str, int]]:
|
|
graha_rows = graha if graha is not None else graha_strengths(planet_lons)
|
|
occupants: Dict[int, int] = {index: 0 for index in range(12)}
|
|
for planet in CLASSICAL_PLANETS:
|
|
occupants[sign_index(planet_lons[planet])] += 1
|
|
rows = {}
|
|
for sign_idx in range(12):
|
|
lord = SIGN_LORDS[SIGNS[sign_idx]]
|
|
chara = _CHARA_RASHI[sign_idx % 3]
|
|
sthira = 10 * occupants[sign_idx]
|
|
aspect = _aspect_units(sign_idx, planet_lons, lord)
|
|
lord_graha = int(graha_rows[lord]["total"])
|
|
rows[sign_idx] = {
|
|
"chara": chara,
|
|
"sthira": sthira,
|
|
"aspect": aspect,
|
|
"lord_graha": lord_graha,
|
|
"total": chara + sthira + aspect + lord_graha,
|
|
}
|
|
return rows
|
|
|
|
|
|
def stronger_sign(left: int, right: int, strengths: Mapping[int, Mapping[str, int]], planet_lons: Mapping[str, float]) -> Dict[str, object]:
|
|
"""Pick the stronger sign. Equal totals use the higher chara-karaka lord."""
|
|
left_total = int(strengths[left]["total"])
|
|
right_total = int(strengths[right]["total"])
|
|
if left_total != right_total:
|
|
selected = left if left_total > right_total else right
|
|
return {"status": "selected", "sign_index": selected, "tie_break": "rashi_total"}
|
|
ranks = karaka_ranks(planet_lons)
|
|
left_lord = SIGN_LORDS[SIGNS[left]]
|
|
right_lord = SIGN_LORDS[SIGNS[right]]
|
|
left_rank = ranks.get(left_lord)
|
|
right_rank = ranks.get(right_lord)
|
|
if left_rank is None or right_rank is None or left_rank == right_rank:
|
|
return {
|
|
"status": "unresolved",
|
|
"reason": "equal_rashi_strength_and_karaka",
|
|
"candidates": (left, right),
|
|
}
|
|
selected = left if left_rank < right_rank else right
|
|
return {"status": "selected", "sign_index": selected, "tie_break": "lord_karaka_rank"}
|