fix(nakshatra): keep the first mahadasha's antardashas on the full clock
Upstream 8643bc9a. The nakshatra breakdown was restarting the nine antardashas from the birth remainder, so mid-Ashwini reported Ketu instead of Rahu. Reuse cmd_dasha and clip to birth. BUG-1380. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Grok (agent loop)
Cursor
parent
e7bf60c1f2
commit
68762ccec5
+43
-96
@@ -304,13 +304,6 @@ def _calc_ashtottari_antardasha(md_lord: str, md_years: float) -> List[Dict]:
|
||||
# Vimshottari Nakshatra-level 拆解
|
||||
# ============================================================================
|
||||
|
||||
# Vimshottari Dasha 顺序(9星循环)
|
||||
try:
|
||||
from dasha_common import VIMSHOTTARI_ORDER, VIMSHOTTARI_YEARS
|
||||
except ModuleNotFoundError: # pragma: no cover - package import
|
||||
from scripts.dasha_common import VIMSHOTTARI_ORDER, VIMSHOTTARI_YEARS
|
||||
|
||||
|
||||
def calc_nakshatra_dasha_breakdown(
|
||||
moon_longitude: float,
|
||||
planet_lons: Dict[str, float],
|
||||
@@ -338,87 +331,58 @@ def calc_nakshatra_dasha_breakdown(
|
||||
moon_nak_data = find_nakshatra(moon_longitude)
|
||||
moon_nak_idx = moon_nak_data['nakshatra_idx']
|
||||
|
||||
# 计算 Vimshottari 起始
|
||||
nak = find_nakshatra(moon_longitude)
|
||||
nak_idx = nak['nakshatra_idx']
|
||||
nak_span = 360.0 / 27
|
||||
nak_start = nak_idx * nak_span
|
||||
deg_in_nak = moon_longitude - nak_start
|
||||
used_ratio = deg_in_nak / nak_span
|
||||
|
||||
# 起始守护星和剩余年数
|
||||
start_lord = NAK_LORDS[nak_idx]
|
||||
start_total = VIMSHOTTARI_YEARS.get(start_lord, 7)
|
||||
first_remaining = start_total * (1 - used_ratio)
|
||||
|
||||
# 构建大运序列
|
||||
start_idx = VIMSHOTTARI_ORDER.index(start_lord)
|
||||
sequence = []
|
||||
cumulative = 0.0
|
||||
|
||||
# 首个(剩余)大运
|
||||
sequence.append({
|
||||
'lord': start_lord,
|
||||
'years': round(first_remaining, 4),
|
||||
'cumulative_from': 0.0,
|
||||
'is_first': True,
|
||||
})
|
||||
cumulative += first_remaining
|
||||
|
||||
idx = (start_idx + 1) % 9
|
||||
for _ in range(15): # 足够覆盖到120岁
|
||||
lord = VIMSHOTTARI_ORDER[idx]
|
||||
years = VIMSHOTTARI_YEARS[lord]
|
||||
sequence.append({
|
||||
'lord': lord,
|
||||
'years': round(years, 4),
|
||||
'cumulative_from': round(cumulative, 4),
|
||||
'is_first': False,
|
||||
})
|
||||
cumulative += years
|
||||
idx = (idx + 1) % 9
|
||||
if cumulative > 120:
|
||||
break
|
||||
|
||||
# 定位当前大运
|
||||
current_md = None
|
||||
current_md_idx = -1
|
||||
for i, item in enumerate(sequence):
|
||||
if item['cumulative_from'] <= age < (item['cumulative_from'] + item['years']):
|
||||
current_md = item
|
||||
current_md_idx = i
|
||||
break
|
||||
|
||||
# 小运沿用 cmd_dasha:从完整大运真实起点排,再裁到出生之后。
|
||||
# 把第一大运的剩余年数当成完整大运会把早期小运整段重切(BUG-1380)。
|
||||
from types import SimpleNamespace
|
||||
try:
|
||||
from jyotish_engine import cmd_dasha
|
||||
except ImportError:
|
||||
from scripts.jyotish_engine import cmd_dasha
|
||||
if not math.isfinite(age) or age < 0:
|
||||
raise ValueError('age must be finite and nonnegative')
|
||||
birth = datetime.fromisoformat(birth_date_str)
|
||||
reference = birth + timedelta(days=age * 365.25)
|
||||
packet = cmd_dasha(SimpleNamespace(
|
||||
moon_lon=moon_longitude, nakshatra=None, pada=None,
|
||||
birthdate=birth.date().isoformat(), year=birth.year, month=birth.month,
|
||||
day=birth.day, hour=birth.hour, minute=birth.minute, second=birth.second,
|
||||
lat=None, lon=None, tz=0, today=reference.date().isoformat(),
|
||||
dasha_year_days=365.25))
|
||||
current_md = next((row for row in packet['timeline']
|
||||
if datetime.fromisoformat(row['start_datetime']) <= reference
|
||||
< datetime.fromisoformat(row['end_datetime'])), None)
|
||||
if current_md is None:
|
||||
return {'error': f'无法定位年龄 {age} 在 Vimshottari 序列中'}
|
||||
|
||||
md_lord = current_md['lord']
|
||||
md_years = current_md['years']
|
||||
md_start_age = current_md['cumulative_from']
|
||||
years_in_md = age - md_start_age
|
||||
md_start = datetime.fromisoformat(current_md['start_datetime'])
|
||||
md_end = datetime.fromisoformat(current_md['end_datetime'])
|
||||
md_years = (md_end - md_start).total_seconds() / (86400 * 365.25)
|
||||
md_start_age = (md_start - birth).total_seconds() / (86400 * 365.25)
|
||||
years_in_md = (reference - md_start).total_seconds() / (86400 * 365.25)
|
||||
|
||||
# 大运守护星的 Nakshatra
|
||||
md_lord_nak = None
|
||||
if md_lord in planet_lons:
|
||||
md_lord_nak = find_nakshatra(planet_lons[md_lord])
|
||||
|
||||
# 计算当前 Antardasha
|
||||
antardasas = _calc_vimshottari_antardasha_full(md_lord, md_years)
|
||||
|
||||
# 定位当前 Antardasha
|
||||
ad_cumulative = 0.0
|
||||
antardasas = []
|
||||
current_ad = None
|
||||
for ad in antardasas:
|
||||
ad_end = ad_cumulative + ad['years']
|
||||
if ad_cumulative <= years_in_md < ad_end:
|
||||
for ad in current_md['antardasha_timeline']:
|
||||
ad_start = datetime.fromisoformat(ad['start_datetime'])
|
||||
ad_end = datetime.fromisoformat(ad['end_datetime'])
|
||||
years = (ad_end - ad_start).total_seconds() / (86400 * 365.25)
|
||||
antardasas.append({'lord': ad['lord'], 'years': years})
|
||||
if ad_start <= reference < ad_end:
|
||||
nominal_start = datetime.fromisoformat(ad['unclipped_start_datetime'])
|
||||
span = (ad_end - nominal_start).total_seconds()
|
||||
current_ad = {
|
||||
'lord': ad['lord'],
|
||||
'years': ad['years'],
|
||||
'progress': (years_in_md - ad_cumulative) / ad['years'] if ad['years'] > 0 else 0,
|
||||
'remaining': round(ad_end - years_in_md, 4),
|
||||
'years': round(years, 4),
|
||||
'progress': (reference - nominal_start).total_seconds() / span if span else 0,
|
||||
'remaining': round((ad_end - reference).total_seconds() / (86400 * 365.25), 4),
|
||||
'start_datetime': ad['start_datetime'],
|
||||
'end_datetime': ad['end_datetime'],
|
||||
}
|
||||
break
|
||||
ad_cumulative = ad_end
|
||||
|
||||
# 当前 Antardasha 守护星的 Nakshatra
|
||||
ad_lord_nak = None
|
||||
@@ -451,8 +415,8 @@ def calc_nakshatra_dasha_breakdown(
|
||||
md_years_per_pada = md_years_per_nak / 4
|
||||
|
||||
for ad in antardasas:
|
||||
if md_years_per_nak > 0:
|
||||
ad_nak = find_nakshatra(planet_lons.get(ad['lord'], 0))
|
||||
if md_years_per_nak > 0 and ad['lord'] in planet_lons:
|
||||
ad_nak = find_nakshatra(planet_lons[ad['lord']])
|
||||
pada_windows.append({
|
||||
'period': f"AD {ad['lord']}",
|
||||
'nakshatra': ad_nak['nakshatra'],
|
||||
@@ -470,6 +434,8 @@ def calc_nakshatra_dasha_breakdown(
|
||||
|
||||
return {
|
||||
'system': 'Vimshottari Nakshatra-level Breakdown',
|
||||
'period_source': 'jyotish_engine.cmd_dasha native timeline',
|
||||
'reference_datetime': reference.isoformat(timespec='seconds'),
|
||||
'moon_nakshatra': moon_nak_data['nakshatra'],
|
||||
'moon_nakshatra_idx': moon_nak_idx,
|
||||
'moon_pada': moon_nak_data['pada'],
|
||||
@@ -490,25 +456,6 @@ def calc_nakshatra_dasha_breakdown(
|
||||
}
|
||||
|
||||
|
||||
def _calc_vimshottari_antardasha_full(md_lord: str, md_years: float) -> List[Dict]:
|
||||
"""计算完整的 Vimshottari Antardasha(9个周期)"""
|
||||
md_start_idx = VIMSHOTTARI_ORDER.index(md_lord)
|
||||
total_years = sum(VIMSHOTTARI_YEARS.values()) # 120
|
||||
|
||||
result = []
|
||||
for i in range(9):
|
||||
ad_lord = VIMSHOTTARI_ORDER[(md_start_idx + i) % 9]
|
||||
ad_years_total = VIMSHOTTARI_YEARS[ad_lord]
|
||||
ad_years = md_years * ad_years_total / total_years
|
||||
result.append({
|
||||
'lord': ad_lord,
|
||||
'years': round(ad_years, 4),
|
||||
'months': round(ad_years * 12, 1),
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Nakshatra Transit Overlay(过境星宿叠加大运)
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user