Implement Nakshatra advanced dasha layer
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
cmd_nakshatra_adv: Nakshatra Advanced CLI 子命令
|
||||
Jyotish Vedic Astrology Skill — v6.0.22
|
||||
|
||||
子命令:
|
||||
nakshatra-adv: 高级Nakshatra分析(Tara Bala / Chandra Bala / Sub-Lord / 综合报告)
|
||||
nakshatra-dasha: 星宿大运推演(Ashtottari / Vimshottari Nakshatra-level / Transit Overlay)
|
||||
nakshatra-full: 综合星宿完整报告(本命 + 大运 + 过境)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from typing import Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def _compute_chart_from_args(args: Any) -> tuple:
|
||||
"""
|
||||
从 args 计算星盘数据。
|
||||
返回 (chart, asc_idx, jd, ayanamsa, planet_lons, moon_lon, moon_nak_idx, asc_sign_idx)
|
||||
"""
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from jyotish_engine import compute_chart_data
|
||||
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
|
||||
if chart is None:
|
||||
return None, None, None, None, {}, 0, 0, 0
|
||||
|
||||
planets = chart.get('planets', {})
|
||||
planet_lons = {}
|
||||
for pn, pd in planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
planet_lons[pn] = pd['degree']
|
||||
|
||||
if not planet_lons:
|
||||
return chart, asc_idx, jd, ayanamsa, {}, 0, 0, 0
|
||||
|
||||
moon_lon = planet_lons.get('Moon', 0)
|
||||
moon_nak_idx = int(moon_lon / (360.0 / 27)) % 27
|
||||
asc_sign_idx = asc_idx if asc_idx is not None else 0
|
||||
|
||||
return chart, asc_idx, jd, ayanamsa, planet_lons, moon_lon, moon_nak_idx, asc_sign_idx
|
||||
|
||||
|
||||
def cmd_nakshatra_adv(args: Any) -> Dict[str, Any]:
|
||||
"""
|
||||
nakshatra-adv 子命令:高级Nakshatra分析(v3.7 → v6.0.22 升级)
|
||||
|
||||
支持 mode:
|
||||
all — 全部(detail + tara + chandra + sublord + full)
|
||||
detail — Nakshatra详情
|
||||
tara — Tara Bala
|
||||
chandra — Chandra Bala (v6.0.22 新增)
|
||||
combined — Tara + Chandra 双维综合 (v6.0.22 新增)
|
||||
sublord — Sub-Lord KP
|
||||
full — 综合星宿报告 (v6.0.22 新增)
|
||||
"""
|
||||
result = _compute_chart_from_args(args)
|
||||
chart, asc_idx, jd, ayanamsa, planet_lons, moon_lon, moon_nak_idx, asc_sign_idx = result
|
||||
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
try:
|
||||
from nakshatra_advanced import (
|
||||
find_nakshatra, calc_all_tara_balas, calc_sub_lord,
|
||||
calc_chandra_bala, calc_tara_chandra_combined,
|
||||
calc_nakshatra_transits_natal, nakshatra_full_report,
|
||||
nakshatra_compatibility,
|
||||
)
|
||||
except ImportError as e:
|
||||
return {"error": f"nakshatra_advanced模块导入失败: {e}"}
|
||||
|
||||
mode = getattr(args, 'mode', 'all') or 'all'
|
||||
|
||||
# === mode: 'detail' ===
|
||||
if mode in ('all', 'detail'):
|
||||
detail = {}
|
||||
for pn, lon in planet_lons.items():
|
||||
detail[pn] = find_nakshatra(lon)
|
||||
result_dict = {'planets': detail}
|
||||
if mode == 'detail':
|
||||
return result_dict
|
||||
else:
|
||||
result_dict = {}
|
||||
|
||||
# === mode: 'tara' ===
|
||||
if mode in ('all', 'tara'):
|
||||
tara = calc_all_tara_balas(moon_nak_idx, planet_lons)
|
||||
if mode == 'tara':
|
||||
return {'tara_bala': tara, 'moon_nakshatra_idx': moon_nak_idx}
|
||||
result_dict['tara_bala'] = tara
|
||||
|
||||
# === mode: 'chandra' (v6.0.22 新增) ===
|
||||
if mode in ('all', 'chandra'):
|
||||
moon_sign_idx = int(moon_lon / 30) % 12
|
||||
chandra = {}
|
||||
for pn, lon in planet_lons.items():
|
||||
p_sign = int(lon / 30) % 12
|
||||
chandra[pn] = {
|
||||
'planet_sign': p_sign,
|
||||
'chandra': calc_chandra_bala(moon_sign_idx, p_sign),
|
||||
}
|
||||
if mode == 'chandra':
|
||||
return {'chandra_bala': chandra, 'moon_sign_idx': moon_sign_idx}
|
||||
result_dict['chandra_bala'] = chandra
|
||||
|
||||
# === mode: 'combined' (v6.0.22 新增) ===
|
||||
if mode in ('all', 'combined'):
|
||||
moon_sign_idx = int(moon_lon / 30) % 12
|
||||
combined = calc_tara_chandra_combined(moon_nak_idx, moon_sign_idx, planet_lons)
|
||||
if mode == 'combined':
|
||||
return {'tara_chandra_combined': combined}
|
||||
result_dict['tara_chandra_combined'] = combined
|
||||
|
||||
# === mode: 'sublord' ===
|
||||
if mode in ('all', 'sublord'):
|
||||
sublords = {pn: calc_sub_lord(lon) for pn, lon in planet_lons.items()}
|
||||
if mode == 'sublord':
|
||||
return {'sub_lords': sublords}
|
||||
result_dict['sub_lords'] = sublords
|
||||
|
||||
# === mode: 'all' / 'full' — 综合报告 ===
|
||||
if mode in ('all', 'full'):
|
||||
full = nakshatra_full_report(chart)
|
||||
result_dict.update(full)
|
||||
|
||||
return result_dict
|
||||
|
||||
|
||||
def cmd_nakshatra_dasha(args: Any) -> Dict[str, Any]:
|
||||
"""
|
||||
nakshatra-dasha 子命令:星宿大运推演(v6.0.22 新增)
|
||||
|
||||
支持 mode:
|
||||
ashtottari — 仅 Ashtottari Dasha
|
||||
vimshottari — 仅 Vimshottari Nakshatra-level 拆解
|
||||
overlay — 仅 Nakshatra Transit Overlay
|
||||
all — 全部(默认)
|
||||
"""
|
||||
result = _compute_chart_from_args(args)
|
||||
chart, asc_idx, jd, ayanamsa, planet_lons, moon_lon, moon_nak_idx, asc_sign_idx = result
|
||||
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
try:
|
||||
from nakshatra_dasha import (
|
||||
calc_ashtottari_dasha, calc_current_ashtottari,
|
||||
calc_nakshatra_dasha_breakdown,
|
||||
calc_nakshatra_transit_overlay,
|
||||
nakshatra_dasha_full_report,
|
||||
)
|
||||
except ImportError as e:
|
||||
return {"error": f"nakshatra_dasha模块导入失败: {e}"}
|
||||
|
||||
# 构建 birth_date_str
|
||||
birth_date_str = f"{args.year}-{args.month:02d}-{args.day:02d}"
|
||||
|
||||
# 年龄
|
||||
age = getattr(args, 'age', None)
|
||||
if age is None:
|
||||
try:
|
||||
birth_date = datetime(args.year, args.month, args.day)
|
||||
age = (datetime.now() - birth_date).days / 365.25
|
||||
except:
|
||||
age = 30.0
|
||||
|
||||
mode = getattr(args, 'mode', 'all') or 'all'
|
||||
|
||||
# 确定 Rahu 宫位
|
||||
rahu_deg = planet_lons.get('Rahu', 0)
|
||||
rahu_sign_idx = int(rahu_deg / 30) % 12
|
||||
rahu_house = ((rahu_sign_idx - asc_sign_idx) % 12) + 1
|
||||
|
||||
if mode == 'ashtottari':
|
||||
asht = calc_ashtottari_dasha(moon_lon, birth_date_str, rahu_house)
|
||||
if asht['applicable']:
|
||||
asht['current'] = calc_current_ashtottari(asht, age)
|
||||
return asht
|
||||
|
||||
if mode == 'vimshottari':
|
||||
return calc_nakshatra_dasha_breakdown(moon_lon, planet_lons, birth_date_str, age)
|
||||
|
||||
if mode == 'overlay':
|
||||
# 需要过境数据:获取当前日期或 --transit-date
|
||||
transit_date_str = getattr(args, 'transit_date', None) or datetime.now().strftime('%Y-%m-%d')
|
||||
from jyotish_engine import _calc_sidereal_planets_for_jd
|
||||
try:
|
||||
import swisseph as swe
|
||||
ty, tm, td = map(int, transit_date_str.split('-'))
|
||||
transit_jd = swe.julday(ty, tm, td, 12.0 - args.tz)
|
||||
transit_planets, transit_ayanamsa = _calc_sidereal_planets_for_jd(
|
||||
transit_jd, node_mode=getattr(args, 'node_mode', 'mean'), include_ketu=True)
|
||||
transit_lons = {}
|
||||
for pn, pd in transit_planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
transit_lons[pn] = pd['degree']
|
||||
overlay = calc_nakshatra_transit_overlay(planet_lons, transit_lons, moon_nak_idx)
|
||||
overlay['transit_date'] = transit_date_str
|
||||
return overlay
|
||||
except ImportError:
|
||||
return {"error": "swisseph 未安装,无法计算过境星宿"}
|
||||
|
||||
# mode == 'all': 综合报告
|
||||
# 尝试获取过境数据
|
||||
transit_lons = None
|
||||
try:
|
||||
import swisseph as swe
|
||||
transit_date_str = getattr(args, 'transit_date', None) or datetime.now().strftime('%Y-%m-%d')
|
||||
from jyotish_engine import _calc_sidereal_planets_for_jd
|
||||
ty, tm, td = map(int, transit_date_str.split('-'))
|
||||
transit_jd = swe.julday(ty, tm, td, 12.0 - args.tz)
|
||||
transit_planets, _ = _calc_sidereal_planets_for_jd(
|
||||
transit_jd, node_mode=getattr(args, 'node_mode', 'mean'), include_ketu=True)
|
||||
transit_lons = {}
|
||||
for pn, pd in transit_planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
transit_lons[pn] = pd['degree']
|
||||
except:
|
||||
pass
|
||||
|
||||
return nakshatra_dasha_full_report(chart, birth_date_str, age, transit_lons)
|
||||
|
||||
|
||||
def cmd_nakshatra_full(args: Any) -> Dict[str, Any]:
|
||||
"""
|
||||
nakshatra-full 子命令:综合星宿完整报告(本命 + 大运 + 过境)— v6.0.22 新增
|
||||
|
||||
整合三个维度:
|
||||
1. 本命 Nakshatra 分析(Tara/Chandra/Sub-Lord/兼容性)
|
||||
2. Nakshatra Dasha 推演(Ashtottari + Vimshottari Nakshatra-level)
|
||||
3. Nakshatra Transit Overlay(当前过境星宿)
|
||||
"""
|
||||
adv_result = cmd_nakshatra_adv(args)
|
||||
dasha_result = cmd_nakshatra_dasha(args)
|
||||
|
||||
return {
|
||||
'system': 'Nakshatra Comprehensive Report(星宿综合报告)',
|
||||
'version': 'v6.0.22',
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'nakshatra_advanced': adv_result,
|
||||
'nakshatra_dasha': dasha_result,
|
||||
}
|
||||
@@ -33,7 +33,17 @@ def cmd_narayana_dasha(args, chart_data):
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
planet_lons[pn] = pd['degree']
|
||||
|
||||
asc_idx = chart_data.get('ascendant', {}).get('sign_idx', 0)
|
||||
asc_idx = chart_data.get('ascendant_index')
|
||||
if asc_idx is None:
|
||||
asc_info = chart_data.get('ascendant', {}) if isinstance(chart_data, dict) else {}
|
||||
asc_idx = asc_info.get('sign_idx', 0)
|
||||
if asc_idx == 0 and isinstance(asc_info, dict):
|
||||
# compute_chart_data 输出 ascendant.sign,但不一定输出 sign_idx;兼容旧结构
|
||||
sign_name = asc_info.get('sign')
|
||||
signs = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
|
||||
'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces']
|
||||
if sign_name in signs:
|
||||
asc_idx = signs.index(sign_name)
|
||||
if isinstance(asc_idx, str):
|
||||
asc_idx = int(asc_idx)
|
||||
|
||||
|
||||
+90
-54
@@ -62,7 +62,7 @@ try:
|
||||
except ImportError:
|
||||
HAS_SWE = False
|
||||
from cmd_solar_return import cmd_solar_return # v6.0.18
|
||||
from cmd_narayana_dasha import cmd_narayana_dasha # v6.0.20
|
||||
from cmd_narayana_dasha import cmd_narayana_dasha as _cmd_narayana_dasha_impl # v6.0.20
|
||||
from cmd_muhurta import cmd_muhurta # v6.0.21
|
||||
|
||||
# ============================================================================
|
||||
@@ -2142,11 +2142,11 @@ def _assess_conjunction_quality(lord, houses, planets):
|
||||
dusthana = {6, 8, 12}
|
||||
trikona = {1, 5, 9}
|
||||
kendra = {1, 4, 7, 10}
|
||||
|
||||
|
||||
has_dusthana = any(h in dusthana for h in houses)
|
||||
has_trikona = any(h in trikona for h in houses)
|
||||
has_kendra = any(h in kendra for h in houses)
|
||||
|
||||
|
||||
if has_dusthana and has_trikona:
|
||||
return f"凶吉混合 — 挑战与成长并存"
|
||||
elif has_dusthana and has_kendra:
|
||||
@@ -2168,27 +2168,27 @@ def _conflict_arbitration(report):
|
||||
conflicts = []
|
||||
planets = report.get('planets', {})
|
||||
audit = report.get('audit', {})
|
||||
|
||||
|
||||
p1 = audit.get('P1_identity', {})
|
||||
p7 = audit.get('P7_dignity', {})
|
||||
p2 = audit.get('P2_health', {})
|
||||
|
||||
|
||||
asc_lord = p1.get('asc_lord', '')
|
||||
|
||||
|
||||
# 规则1: P1清理者+P7入旺 → 检查上升主是否掌管8/12宫(清理者角色)
|
||||
# 清理者定义:掌管8宫或12宫的行星
|
||||
SIGN_LORDS_MAP = {'Aries': 'Mars', 'Taurus': 'Venus', 'Gemini': 'Mercury', 'Cancer': 'Moon',
|
||||
'Leo': 'Sun', 'Virgo': 'Mercury', 'Libra': 'Venus', 'Scorpio': 'Mars',
|
||||
'Sagittarius': 'Jupiter', 'Capricorn': 'Saturn', 'Aquarius': 'Saturn', 'Pisces': 'Jupiter'}
|
||||
|
||||
|
||||
asc_sign = p1.get('asc_sign', '')
|
||||
asc_idx_local = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
|
||||
|
||||
|
||||
# 找8宫主和12宫主
|
||||
h8_sign = SIGNS[(asc_idx_local + 7) % 12]
|
||||
h12_sign = SIGNS[(asc_idx_local + 11) % 12]
|
||||
destroyer_lords = {SIGN_LORDS_MAP.get(h8_sign, ''), SIGN_LORDS_MAP.get(h12_sign, '')}
|
||||
|
||||
|
||||
for dl in destroyer_lords:
|
||||
if dl and dl in p7:
|
||||
dl_status = p7[dl].get('status', '')
|
||||
@@ -2199,7 +2199,7 @@ def _conflict_arbitration(report):
|
||||
'verdict': '带毒高价值资产',
|
||||
'instruction': f"{dl}既是清理者(掌8/12宫)又入旺/入庙,力量极强但方向凶险——禁止说逢凶化吉",
|
||||
})
|
||||
|
||||
|
||||
# 规则2: P5凶宫+BAV高 → 检查6/8/12宫的SAV是否 >28
|
||||
asht_data = report.get('ashtakavarga', {})
|
||||
house_scores = {}
|
||||
@@ -2212,7 +2212,7 @@ def _conflict_arbitration(report):
|
||||
house_scores = asht_result.get('house_scores', {})
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
for h in [6, 8, 12]:
|
||||
hs = house_scores.get(f'house_{h}', {})
|
||||
sav_score = hs.get('score', 0)
|
||||
@@ -2224,7 +2224,7 @@ def _conflict_arbitration(report):
|
||||
'verdict': '乱世出英雄',
|
||||
'instruction': f"{h}宫是凶宫但SAV={sav_score}(>28),在困境中反而能出成就",
|
||||
})
|
||||
|
||||
|
||||
# 规则3: P1吉+P2受损 → 上升主星状态好但太阳(健康指标)受损
|
||||
sun_info = p2.get('sun_status', '')
|
||||
lord_info = p7.get(asc_lord, {})
|
||||
@@ -2236,7 +2236,7 @@ def _conflict_arbitration(report):
|
||||
'verdict': '空有雄心无着力点',
|
||||
'instruction': f"上升主{asc_lord}强健但太阳受损,有野心但执行力/健康跟不上",
|
||||
})
|
||||
|
||||
|
||||
return conflicts
|
||||
|
||||
|
||||
@@ -2595,41 +2595,30 @@ def cmd_jaimini(args):
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 18. 高级Nakshatra分析(v3.7新增)
|
||||
# 18. 高级Nakshatra分析(v3.7 → v6.0.22 移至 cmd_nakshatra_adv.py)
|
||||
# ============================================================================
|
||||
def cmd_nakshatra_adv(args):
|
||||
def cmd_narayana_dasha(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
from nakshatra_advanced import find_nakshatra, calc_all_tara_balas, calc_sub_lord, nakshatra_compatibility
|
||||
except ImportError as e:
|
||||
return {"error": f"nakshatra_advanced模块导入失败: {e}"}
|
||||
planets = chart.get('planets', {})
|
||||
planet_lons = {}
|
||||
for pn, pd in planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
planet_lons[pn] = pd['degree']
|
||||
if chart is not None:
|
||||
chart['ascendant_index'] = asc_idx
|
||||
return _cmd_narayana_dasha_impl(args, chart)
|
||||
|
||||
result = {'planets': {}}
|
||||
mode = args.mode or 'all'
|
||||
|
||||
if mode in ('all', 'detail'):
|
||||
for pn, lon in planet_lons.items():
|
||||
result['planets'][pn] = find_nakshatra(lon)
|
||||
def cmd_nakshatra_adv(args):
|
||||
from cmd_nakshatra_adv import cmd_nakshatra_adv as _impl
|
||||
return _impl(args)
|
||||
|
||||
if mode in ('all', 'tara'):
|
||||
moon_lon = planet_lons.get('Moon', 0)
|
||||
moon_nak_idx = int(moon_lon / (360/27)) % 27
|
||||
result['tara_bala'] = calc_all_tara_balas(moon_nak_idx, planet_lons)
|
||||
|
||||
if mode in ('all', 'sublord'):
|
||||
result['sub_lords'] = {pn: calc_sub_lord(lon) for pn, lon in planet_lons.items()}
|
||||
def cmd_nakshatra_dasha(args):
|
||||
from cmd_nakshatra_adv import cmd_nakshatra_dasha as _impl
|
||||
return _impl(args)
|
||||
|
||||
return result
|
||||
|
||||
def cmd_nakshatra_full(args):
|
||||
from cmd_nakshatra_adv import cmd_nakshatra_full as _impl
|
||||
return _impl(args)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -3648,7 +3637,6 @@ def cmd_full_reading(args):
|
||||
try:
|
||||
from narayana_dasha import narayana_dasha_full_report
|
||||
# 计算当前年龄(从出生到 today)
|
||||
from datetime import datetime
|
||||
birth_dt = datetime(args.year, args.month, args.day, args.hour, args.minute)
|
||||
today_dt = datetime.strptime(args.today, '%Y-%m-%d') if hasattr(args, 'today') and args.today else datetime.now()
|
||||
current_age = (today_dt - birth_dt).days / 365.25
|
||||
@@ -3708,20 +3696,50 @@ def cmd_full_reading(args):
|
||||
except Exception as e:
|
||||
report['errors'].append(f"jaimini: {e}")
|
||||
|
||||
# ── Step 7: 高级Nakshatra ──
|
||||
# ── Step 7: 高级Nakshatra(v6.0.22 升级:含 Chandra Bala + 综合报告)──
|
||||
try:
|
||||
from nakshatra_advanced import find_nakshatra, calc_all_tara_balas, calc_sub_lord
|
||||
|
||||
nak_result = {'planets': {}}
|
||||
for pn, lon in planet_lons.items():
|
||||
nak_result['planets'][pn] = find_nakshatra(lon)
|
||||
moon_nak_idx = int(planet_lons.get('Moon', 0) / (360/27)) % 27
|
||||
nak_result['tara_bala'] = calc_all_tara_balas(moon_nak_idx, planet_lons)
|
||||
nak_result['sub_lords'] = {pn: calc_sub_lord(lon) for pn, lon in planet_lons.items()}
|
||||
report['modules']['nakshatra_advanced'] = nak_result
|
||||
from nakshatra_advanced import nakshatra_full_report as nakshatra_report
|
||||
nak_full = nakshatra_report(chart)
|
||||
report['modules']['nakshatra_advanced'] = nak_full
|
||||
except Exception as e:
|
||||
report['errors'].append(f"nakshatra-adv: {e}")
|
||||
|
||||
# ── Step 7.5: Nakshatra Dasha 星宿大运(v6.0.22 新增)──
|
||||
try:
|
||||
from nakshatra_dasha import nakshatra_dasha_full_report
|
||||
|
||||
birth_date_str = f"{args.year}-{args.month:02d}-{args.day:02d}"
|
||||
# 年龄
|
||||
nak_age = args.age
|
||||
if nak_age is None:
|
||||
try:
|
||||
birth_date = datetime(args.year, args.month, args.day)
|
||||
nak_age = (datetime.now() - birth_date).days / 365.25
|
||||
except:
|
||||
nak_age = None
|
||||
|
||||
if nak_age is not None:
|
||||
# 获取过境星宿数据
|
||||
transit_lons_for_nak = None
|
||||
try:
|
||||
transit_ref = getattr(args, 'transit_date', None) or datetime.now().strftime('%Y-%m-%d')
|
||||
ty, tm, td = map(int, transit_ref.split('-'))
|
||||
transit_jd_nak = swe.julday(ty, tm, td, 12.0 - args.tz)
|
||||
transit_pl_nak, _ = _calc_sidereal_planets_for_jd(
|
||||
transit_jd_nak, node_mode=getattr(args, 'node_mode', 'mean'), include_ketu=True)
|
||||
transit_lons_for_nak = {}
|
||||
for pn, pd in transit_pl_nak.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
transit_lons_for_nak[pn] = pd['degree']
|
||||
except:
|
||||
pass
|
||||
|
||||
nak_dasha = nakshatra_dasha_full_report(
|
||||
chart, birth_date_str, nak_age, transit_lons_for_nak)
|
||||
report['modules']['nakshatra_dasha'] = nak_dasha
|
||||
except Exception as e:
|
||||
report['errors'].append(f"nakshatra-dasha: {e}")
|
||||
|
||||
# ── Step 8: Argala门闩 ──
|
||||
try:
|
||||
from argala import calc_argala
|
||||
@@ -4109,10 +4127,27 @@ def main():
|
||||
p.add_argument('--mode', default='all', choices=['all','karaka','dasha','karakamsha'], help='分析模式')
|
||||
p.add_argument('--antardasha', action='store_true', help='Chara Dasha含Antardasha子周期(当前timing实现为partial)')
|
||||
|
||||
# 18. nakshatra-adv (v3.7新增)
|
||||
p = sub.add_parser('nakshatra-adv', help='高级Nakshatra分析')
|
||||
# 18. nakshatra-adv (v3.7新增 → v6.0.22 升级)
|
||||
p = sub.add_parser('nakshatra-adv', help='高级Nakshatra分析(Tara/Chandra/Sub-Lord/综合)')
|
||||
_add_chart_args(p)
|
||||
p.add_argument('--mode', default='all', choices=['all','detail','tara','sublord'], help='分析模式')
|
||||
p.add_argument('--mode', default='all',
|
||||
choices=['all','detail','tara','chandra','combined','sublord','full'],
|
||||
help='分析模式')
|
||||
|
||||
# 18.5 nakshatra-dasha (v6.0.22新增)
|
||||
p = sub.add_parser('nakshatra-dasha', help='星宿大运推演(Ashtottari / Vimshottari Nakshatra-level / Transit Overlay)')
|
||||
_add_chart_args(p)
|
||||
p.add_argument('--age', type=float, default=None, help='当前年龄(自动计算如果不提供)')
|
||||
p.add_argument('--mode', default='all',
|
||||
choices=['all','ashtottari','vimshottari','overlay'],
|
||||
help='分析模式')
|
||||
p.add_argument('--transit-date', default=None, help='过境日期 YYYY-MM-DD(overlay/all模式,默认今天)')
|
||||
|
||||
# 18.6 nakshatra-full (v6.0.22新增)
|
||||
p = sub.add_parser('nakshatra-full', help='综合星宿完整报告(本命 + 大运 + 过境)')
|
||||
_add_chart_args(p)
|
||||
p.add_argument('--age', type=float, default=None, help='当前年龄')
|
||||
p.add_argument('--transit-date', default=None, help='过境日期 YYYY-MM-DD(默认今天)')
|
||||
|
||||
# 19. argala (v3.7新增)
|
||||
p = sub.add_parser('argala', help='Argala门闩系统')
|
||||
@@ -4207,7 +4242,8 @@ def main():
|
||||
'shadbala': cmd_shadbala, 'ashtakavarga': cmd_ashtakavarga, 'memory': cmd_memory,
|
||||
'validate': cmd_validate, 'audit': cmd_audit, 'report': cmd_report,
|
||||
'varga-full': cmd_varga_full, 'aspects': cmd_aspects, 'jaimini': cmd_jaimini,
|
||||
'nakshatra-adv': cmd_nakshatra_adv, 'argala': cmd_argala, 'tajika': cmd_tajika,
|
||||
'nakshatra-adv': cmd_nakshatra_adv, 'nakshatra-dasha': cmd_nakshatra_dasha,
|
||||
'nakshatra-full': cmd_nakshatra_full, 'argala': cmd_argala, 'tajika': cmd_tajika,
|
||||
'synastry': cmd_synastry, 'solar-return': cmd_solar_return,
|
||||
'narayana-dasha': cmd_narayana_dasha, 'muhurta': cmd_muhurta,
|
||||
'full-reading': cmd_full_reading, 'prashna': cmd_prashna,
|
||||
|
||||
+352
-25
@@ -1,18 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
高级星宿分析模块 v1.0
|
||||
高级星宿分析模块 v2.0
|
||||
Nakshatra深度分析系统
|
||||
|
||||
支持:
|
||||
- Tara Bala(星宿力量匹配)
|
||||
- Tara Bala(星宿力量匹配 — Navatara 9星宿循环)
|
||||
- Chandra Bala(月座力量 — 12宫循环)
|
||||
- Sub-Lord体系(KP占星)
|
||||
- 精确Vimshottari起始点计算
|
||||
- Nakshatra兼容性分析
|
||||
- Navatara(9星宿组)分类
|
||||
- 综合星宿力量报告
|
||||
"""
|
||||
from typing import Dict, List, Tuple
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
NAK_LIST = [
|
||||
("Ashwini","Ketu",7),("Bharani","Venus",20),("Krittika","Sun",6),
|
||||
@@ -59,7 +62,7 @@ def find_nakshatra(longitude: float) -> Dict:
|
||||
pada_span = nak_span / 4 # 每个Pada 3.333...°
|
||||
pada = int(deg_in_nak / pada_span) + 1
|
||||
deg_in_pada = deg_in_nak - (pada - 1) * pada_span
|
||||
|
||||
|
||||
return {
|
||||
'nakshatra': NAK_NAMES[nak_idx],
|
||||
'nakshatra_idx': nak_idx,
|
||||
@@ -76,7 +79,7 @@ def find_nakshatra(longitude: float) -> Dict:
|
||||
def calc_tara_bala(moon_nak_idx: int, target_nak_idx: int) -> Dict:
|
||||
"""
|
||||
Tara Bala计算:月亮星宿到目标星宿的9星宿循环关系
|
||||
|
||||
|
||||
用于判断某个行星/事件星宿与月亮星宿的关系
|
||||
0=Janma(中性), 1=Sampat(吉), 2=Vipat(凶), 3=Kshema(吉),
|
||||
4=Pratyak(凶), 5=Sadhana(吉), 6=Vadha(凶), 7=Mitra(吉), 8=ParamaMitra(大吉)
|
||||
@@ -84,10 +87,10 @@ def calc_tara_bala(moon_nak_idx: int, target_nak_idx: int) -> Dict:
|
||||
distance = (target_nak_idx - moon_nak_idx) % 9
|
||||
tara_name = TARA_NAMES[distance]
|
||||
tara_cn = TARA_CN[distance]
|
||||
|
||||
|
||||
is_auspicious = distance in (1, 3, 5, 7, 8)
|
||||
is_dangerous = distance in (2, 4, 6)
|
||||
|
||||
|
||||
return {
|
||||
'tara_index': distance,
|
||||
'tara_name': tara_name,
|
||||
@@ -115,25 +118,25 @@ def calc_all_tara_balas(moon_nak_idx: int, planet_lons: Dict[str, float]) -> Dic
|
||||
def calc_vimshottari_start(moon_longitude: float) -> Dict:
|
||||
"""
|
||||
精确计算Vimshottari Dasha的起始点
|
||||
|
||||
|
||||
返回月亮所在星宿的守护星、已用度数比例、剩余年数
|
||||
"""
|
||||
nak = find_nakshatra(moon_longitude)
|
||||
nak_idx = nak['nakshatra_idx']
|
||||
nak_lord = NAK_LORDS[nak_idx]
|
||||
total_years = NAK_YEARS[nak_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
|
||||
remaining_ratio = 1 - used_ratio
|
||||
|
||||
|
||||
# 第一个Mahadasha的剩余年数
|
||||
first_mahadasha_remaining = total_years * remaining_ratio
|
||||
first_mahadasha_elapsed = total_years * used_ratio
|
||||
|
||||
|
||||
return {
|
||||
'moon_nakshatra': nak['nakshatra'],
|
||||
'moon_nakshatra_lord': nak_lord,
|
||||
@@ -151,7 +154,7 @@ def calc_sub_lord(longitude: float, division: int = 9) -> Dict:
|
||||
"""
|
||||
Sub-Lord计算(KP占星体系)
|
||||
将星宿进一步细分为Sub-Lord和Sub-Sub-Lord
|
||||
|
||||
|
||||
参数:
|
||||
longitude: 恒星黄道经度
|
||||
division: 细分层级(9=Sub-Lord, 81=Sub-Sub-Lord)
|
||||
@@ -159,24 +162,21 @@ def calc_sub_lord(longitude: float, division: int = 9) -> Dict:
|
||||
# 主星宿
|
||||
nak = find_nakshatra(longitude)
|
||||
nak_span = 360.0 / 27
|
||||
|
||||
# Sub-Lord: 将星宿按守护星的大运年数比例细分
|
||||
total_dasha_years = sum(NAK_YEARS) # 120年
|
||||
nak_lord = NAK_NAMES[nak['nakshatra_idx']]
|
||||
|
||||
# 计算Sub-Lord
|
||||
|
||||
# Sub-Lord: 当前版本为简化版,直接按9等分。
|
||||
# 完整 KP 体系应按 Vimshottari 年数比例不等分,后续需单独对标。
|
||||
nak_lord = NAK_LORDS[nak['nakshatra_idx']]
|
||||
deg_in_nak = nak['degree_in_nakshatra']
|
||||
sub_lord_span = nak_span * (NAK_YEARS[NAK_LORDS.index(NAK_LORDS[nak['nakshatra_idx']])]) / total_dasha_years
|
||||
|
||||
|
||||
# 简化版:直接按9等分
|
||||
sub_span = nak_span / 9
|
||||
sub_idx = int(deg_in_nak / sub_span) % 9
|
||||
|
||||
|
||||
# Sub-Lord是第sub_idx个行星(从星宿守护星开始的大运顺序)
|
||||
DASHA_ORDER = ["Ketu","Venus","Sun","Moon","Mars","Rahu","Jupiter","Saturn","Mercury"]
|
||||
nak_lord_idx = DASHA_ORDER.index(NAK_LORDS[nak['nakshatra_idx']])
|
||||
sub_lord = DASHA_ORDER[(nak_lord_idx + sub_idx) % 9]
|
||||
|
||||
|
||||
return {
|
||||
'nakshatra': nak['nakshatra'],
|
||||
'nakshatra_lord': nak_lord,
|
||||
@@ -195,17 +195,17 @@ def nakshatra_compatibility(nak1_idx: int, nak2_idx: int) -> Dict:
|
||||
# Tara匹配
|
||||
tara = calc_tara_bala(nak1_idx, nak2_idx)
|
||||
tara_score = 3 if tara['is_auspicious'] else 0 if tara['is_dangerous'] else 1.5
|
||||
|
||||
|
||||
# 元素匹配
|
||||
el1 = NAK_ELEMENT.get(nak1_idx, '')
|
||||
el2 = NAK_ELEMENT.get(nak2_idx, '')
|
||||
el_compatible = el1 == el2 or (el1 in ('火','风') and el2 in ('火','风')) or (el1 in ('土','水') and el2 in ('土','水'))
|
||||
|
||||
|
||||
# Gana匹配
|
||||
g1 = NAK_GANA.get(nak1_idx, '')
|
||||
g2 = NAK_GANA.get(nak2_idx, '')
|
||||
gana_score = 6 if g1 == g2 else 3 if ('Dev' in g1 and 'Manushya' in g2) or ('Manushya' in g1 and 'Dev' in g2) else 0
|
||||
|
||||
|
||||
return {
|
||||
'nak1': NAK_NAMES[nak1_idx],
|
||||
'nak2': NAK_NAMES[nak2_idx],
|
||||
@@ -230,3 +230,330 @@ def _tara_interp(idx):
|
||||
8: '至友Tara—大吉,最强吉Tara',
|
||||
}
|
||||
return interps.get(idx, '')
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Chandra Bala(月座力量)— v2.0 新增
|
||||
# ============================================================================
|
||||
|
||||
# 12宫 Chandra Bala 名称(从月亮星座起算)
|
||||
CHANDRA_NAMES = [
|
||||
'Janma(生命)', 'Sampat(财富)', 'Vipat(危险)', 'Kshema(安全)',
|
||||
'Pratyak(障碍)', 'Sadhana(成就)', 'Vadha(毁灭)', 'Mitra(友好)',
|
||||
'ParamaMitra(至友)', 'Karma(业力)', 'Labha(收益)', 'Vyaya(消耗)',
|
||||
]
|
||||
CHANDRA_CN = {
|
||||
0: '生命位', 1: '财富位', 2: '危险位', 3: '安全位',
|
||||
4: '障碍位', 5: '成就位', 6: '毁灭位', 7: '友好位',
|
||||
8: '至友位', 9: '业力位', 10: '收益位', 11: '消耗位',
|
||||
}
|
||||
|
||||
|
||||
def calc_chandra_bala(natal_moon_sign_idx: int, transit_moon_sign_idx: int) -> Dict:
|
||||
"""
|
||||
Chandra Bala(月座力量)计算 — v2.0 新增
|
||||
|
||||
基于月亮所在宫位(Rashi/Sign)的相对位置评估力量。
|
||||
从本命月亮星座起算到当前(过境)月亮星座的12宫循环。
|
||||
|
||||
与 Tara Bala(星宿/Nakshatra 层面)互补,提供 Rashi/Sign 层面的力量评估。
|
||||
常用于 Muhurta 择时和 Transit 过境分析。
|
||||
|
||||
参数:
|
||||
natal_moon_sign_idx: 本命月亮星座索引 (0=Aries ~ 11=Pisces)
|
||||
transit_moon_sign_idx: 过境月亮星座索引 (0=Aries ~ 11=Pisces)
|
||||
|
||||
返回:
|
||||
dict: {
|
||||
'rashi_from_moon': 0-11,
|
||||
'chandra_name': 'Janma/Sampat/...',
|
||||
'chandra_cn': '生命位/财富位/...',
|
||||
'is_auspicious': bool,
|
||||
'is_dangerous': bool,
|
||||
'quality': 'auspicious'/'dangerous'/'neutral',
|
||||
'interpretation': str,
|
||||
}
|
||||
"""
|
||||
# 从本命月亮星座到过境月亮星座的步数(0-based, 12宫循环)
|
||||
distance = (transit_moon_sign_idx - natal_moon_sign_idx) % 12
|
||||
|
||||
name = CHANDRA_NAMES[distance]
|
||||
cn = CHANDRA_CN[distance]
|
||||
|
||||
# 吉位:Sampat(1), Kshema(3), Sadhana(5), Mitra(7), ParamaMitra(8), Labha(10)
|
||||
# 凶位:Vipat(2), Pratyak(4), Vadha(6), Karma(9)
|
||||
# 中性:Janma(0), Vyaya(11)
|
||||
is_auspicious = distance in (1, 3, 5, 7, 8, 10)
|
||||
is_dangerous = distance in (2, 4, 6, 9)
|
||||
|
||||
if is_auspicious:
|
||||
quality = 'auspicious'
|
||||
elif is_dangerous:
|
||||
quality = 'dangerous'
|
||||
else:
|
||||
quality = 'neutral'
|
||||
|
||||
return {
|
||||
'rashi_from_moon': distance,
|
||||
'chandra_name': name,
|
||||
'chandra_cn': cn,
|
||||
'is_auspicious': is_auspicious,
|
||||
'is_dangerous': is_dangerous,
|
||||
'quality': quality,
|
||||
'interpretation': _chandra_interp(distance),
|
||||
}
|
||||
|
||||
|
||||
def _chandra_interp(idx: int) -> str:
|
||||
"""Chandra Bala 解释"""
|
||||
interps = {
|
||||
0: '生命位—代表身体和自我状态,中性',
|
||||
1: '财富位—吉,有利于财务和资源获取',
|
||||
2: '危险位—凶,可能遭遇损失和挑战',
|
||||
3: '安全位—吉,提供保护和稳定感',
|
||||
4: '障碍位—凶,面临困难和拖延',
|
||||
5: '成就位—吉,有利于达成目标和获得成功',
|
||||
6: '毁灭位—凶,需警惕冲突和破坏',
|
||||
7: '友好位—吉,获得他人的支持和帮助',
|
||||
8: '至友位—大吉,最强吉位,全面有利',
|
||||
9: '业力位—凶,涉及过往业力的清算',
|
||||
10: '收益位—吉,有利于收入和事业增长',
|
||||
11: '消耗位—中性偏凶,可能消耗能量和资源',
|
||||
}
|
||||
return interps.get(idx, '')
|
||||
|
||||
|
||||
def calc_tara_chandra_combined(
|
||||
natal_moon_nak_idx: int,
|
||||
natal_moon_sign_idx: int,
|
||||
planet_lons: Dict[str, float],
|
||||
transit_moon_lon: Optional[float] = None,
|
||||
transit_moon_sign_idx: Optional[int] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
Tara Bala + Chandra Bala 双维度综合行星力量分析 — v2.0 新增
|
||||
|
||||
同时评估:
|
||||
1. Tara Bala(Star层面):月亮星宿到行星星宿的9星宿循环
|
||||
2. Chandra Bala(Sign层面):月亮星座到行星星座的12宫循环
|
||||
|
||||
双吉 = 最强力,双凶 = 最弱力,一吉一凶 = 混合力量
|
||||
|
||||
参数:
|
||||
natal_moon_nak_idx: 本命月亮星宿索引 (0-26)
|
||||
natal_moon_sign_idx: 本命月亮星座索引 (0-11)
|
||||
planet_lons: {行星名: 恒星黄道经度}
|
||||
transit_moon_lon: 过境月亮经度(可选,用于过境分析)
|
||||
transit_moon_sign_idx: 过境月亮星座索引(可选,用于过境分析)
|
||||
|
||||
返回:
|
||||
dict: {planet: {tara, chandra, combined_score, interpretation}}
|
||||
"""
|
||||
results = {}
|
||||
|
||||
for pname, lon in planet_lons.items():
|
||||
# Tara Bala(Nakshatra层面)
|
||||
p_nak = find_nakshatra(lon)
|
||||
tara = calc_tara_bala(natal_moon_nak_idx, p_nak['nakshatra_idx'])
|
||||
|
||||
# Chandra Bala(Rashi层面)
|
||||
p_sign_idx = int(lon / 30) % 12
|
||||
chandra = calc_chandra_bala(natal_moon_sign_idx, p_sign_idx)
|
||||
|
||||
# 综合评分
|
||||
combined_score = _combined_score(tara, chandra)
|
||||
|
||||
results[pname] = {
|
||||
'nakshatra': p_nak['nakshatra'],
|
||||
'sign_idx': p_sign_idx,
|
||||
'tara': tara,
|
||||
'chandra': chandra,
|
||||
'combined_score': combined_score,
|
||||
'assessment': _combined_assessment(combined_score),
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def _combined_score(tara: Dict, chandra: Dict) -> str:
|
||||
"""综合 Tara + Chandra 评分"""
|
||||
t_good = tara['is_auspicious']
|
||||
c_good = chandra['is_auspicious']
|
||||
t_bad = tara['is_dangerous']
|
||||
c_bad = chandra['is_dangerous']
|
||||
|
||||
if t_good and c_good:
|
||||
return 'double_auspicious'
|
||||
elif t_bad and c_bad:
|
||||
return 'double_dangerous'
|
||||
elif t_good and c_bad:
|
||||
return 'tara_good_chandra_bad'
|
||||
elif t_bad and c_good:
|
||||
return 'tara_bad_chandra_good'
|
||||
else:
|
||||
# 至少有一方中性
|
||||
if t_good or c_good:
|
||||
return 'mixed_favorable'
|
||||
elif t_bad or c_bad:
|
||||
return 'mixed_unfavorable'
|
||||
else:
|
||||
return 'neutral'
|
||||
|
||||
|
||||
def _combined_assessment(score: str) -> str:
|
||||
"""综合评估说明"""
|
||||
assessments = {
|
||||
'double_auspicious': '双吉—Star与Sign层面均有利,最强力支持',
|
||||
'double_dangerous': '双凶—Star与Sign层面均不利,需格外谨慎',
|
||||
'tara_good_chandra_bad': '星吉宫凶—Nakshatra有利但Rashi层面受阻,可能好事多磨',
|
||||
'tara_bad_chandra_good': '星凶宫吉—Nakshatra不利但Rashi层面有保护,或许有惊无险',
|
||||
'mixed_favorable': '偏吉—至少一方有利,方向积极',
|
||||
'mixed_unfavorable': '偏凶—至少一方不利,需注意',
|
||||
'neutral': '中性—无明确吉凶指向',
|
||||
}
|
||||
return assessments.get(score, '')
|
||||
|
||||
|
||||
def calc_nakshatra_transits_natal(
|
||||
planet_lons: Dict[str, float],
|
||||
) -> Dict:
|
||||
"""
|
||||
计算所有行星在星盘中的 Nakshatra 分布 — v2.0 新增
|
||||
|
||||
用于快速查看每个行星落在哪个星宿、Pada、守护星等信息。
|
||||
|
||||
返回:
|
||||
dict: {planet: {nakshatra, pada, lord, gana, element, degree_in_nak}}
|
||||
"""
|
||||
results = {}
|
||||
for pname, lon in planet_lons.items():
|
||||
nak = find_nakshatra(lon)
|
||||
results[pname] = {
|
||||
'nakshatra': nak['nakshatra'],
|
||||
'nakshatra_idx': nak['nakshatra_idx'],
|
||||
'nakshatra_lord': nak['nakshatra_lord'],
|
||||
'pada': nak['pada'],
|
||||
'gana': nak['gana'],
|
||||
'element': nak['element'],
|
||||
'degree_in_nakshatra': nak['degree_in_nakshatra'],
|
||||
'dasha_years': nak['dasha_years'],
|
||||
}
|
||||
return results
|
||||
|
||||
|
||||
def nakshatra_full_report(
|
||||
chart_data: Dict,
|
||||
age: Optional[float] = None,
|
||||
transit_date: Optional[str] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
综合星宿力量完整报告 — v2.0 新增
|
||||
|
||||
包含:
|
||||
1. 本命 Nakshatra 分布(所有行星)
|
||||
2. Tara Bala 全部分析(以月亮星宿为基准)
|
||||
3. Chandra Bala 全部分析(以月亮星座为基准)
|
||||
4. Tara+Chandra 双维度综合评分
|
||||
5. Nakshatra 兼容性(行星间的星宿关系)
|
||||
6. Vimshottari 起始点信息
|
||||
7. Sub-Lord KP 分析
|
||||
|
||||
参数:
|
||||
chart_data: 星盘数据 (from jyotish_engine chart command)
|
||||
age: 当前年龄(可选)
|
||||
transit_date: 过境日期 YYYY-MM-DD(可选)
|
||||
|
||||
返回:
|
||||
dict: 完整星宿报告
|
||||
"""
|
||||
planets = chart_data.get('planets', {})
|
||||
planet_lons = {}
|
||||
planet_signs = {}
|
||||
for pn, pd in planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
planet_lons[pn] = pd['degree']
|
||||
planet_signs[pn] = int(pd['degree'] / 30) % 12
|
||||
|
||||
if not planet_lons:
|
||||
return {'error': '无法获取行星经度数据'}
|
||||
|
||||
moon_lon = planet_lons.get('Moon', 0)
|
||||
moon_nak_idx = int(moon_lon / (360/27)) % 27
|
||||
moon_sign_idx = int(moon_lon / 30) % 12
|
||||
|
||||
report = {}
|
||||
|
||||
# 1. 本命星宿分布
|
||||
report['natal_nakshatras'] = calc_nakshatra_transits_natal(planet_lons)
|
||||
|
||||
# 2. Tara Bala
|
||||
report['tara_bala'] = calc_all_tara_balas(moon_nak_idx, planet_lons)
|
||||
|
||||
# 3. Chandra Bala
|
||||
chandra_bala = {}
|
||||
for pn, lon in planet_lons.items():
|
||||
p_sign = int(lon / 30) % 12
|
||||
chandra_bala[pn] = {
|
||||
'planet_sign': p_sign,
|
||||
'chandra': calc_chandra_bala(moon_sign_idx, p_sign),
|
||||
}
|
||||
report['chandra_bala'] = chandra_bala
|
||||
|
||||
# 4. Tara + Chandra 双维综合
|
||||
report['tara_chandra_combined'] = calc_tara_chandra_combined(
|
||||
moon_nak_idx, moon_sign_idx, planet_lons)
|
||||
|
||||
# 5. Sub-Lords
|
||||
report['sub_lords'] = {pn: calc_sub_lord(lon) for pn, lon in planet_lons.items()}
|
||||
|
||||
# 6. Vimshottari 起始点
|
||||
report['vimshottari_start'] = calc_vimshottari_start(moon_lon)
|
||||
|
||||
# 7. 星宿力量排序
|
||||
power_ranking = []
|
||||
for pn, combined in report['tara_chandra_combined'].items():
|
||||
score_val = _score_value(combined['combined_score'])
|
||||
power_ranking.append({
|
||||
'planet': pn,
|
||||
'nakshatra': combined['nakshatra'],
|
||||
'tara_name': combined['tara']['tara_cn'],
|
||||
'tara_quality': combined['tara']['quality'],
|
||||
'chandra_name': combined['chandra']['chandra_cn'],
|
||||
'chandra_quality': combined['chandra']['quality'],
|
||||
'combined': combined['combined_score'],
|
||||
'score': score_val,
|
||||
'assessment': combined['assessment'],
|
||||
})
|
||||
power_ranking.sort(key=lambda x: x['score'], reverse=True)
|
||||
report['power_ranking'] = power_ranking
|
||||
|
||||
# 8. 汇总
|
||||
double_auspicious = [p['planet'] for p in power_ranking if p['combined'] == 'double_auspicious']
|
||||
double_dangerous = [p['planet'] for p in power_ranking if p['combined'] == 'double_dangerous']
|
||||
report['summary'] = {
|
||||
'moon_nakshatra': NAK_NAMES[moon_nak_idx],
|
||||
'moon_nakshatra_lord': NAK_LORDS[moon_nak_idx],
|
||||
'moon_sign_idx': moon_sign_idx,
|
||||
'strongest_planets': [p['planet'] for p in power_ranking[:3]],
|
||||
'weakest_planets': [p['planet'] for p in power_ranking[-3:]],
|
||||
'double_auspicious_planets': double_auspicious,
|
||||
'double_dangerous_planets': double_dangerous,
|
||||
'tara_auspicious_count': sum(1 for p in power_ranking if p['tara_quality'] == 'auspicious'),
|
||||
'chandra_auspicious_count': sum(1 for p in power_ranking if p['chandra_quality'] == 'auspicious'),
|
||||
}
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def _score_value(combined: str) -> int:
|
||||
"""将组合评分转为数值"""
|
||||
scores = {
|
||||
'double_auspicious': 100,
|
||||
'tara_good_chandra_bad': 60,
|
||||
'tara_bad_chandra_good': 60,
|
||||
'mixed_favorable': 55,
|
||||
'neutral': 50,
|
||||
'mixed_unfavorable': 45,
|
||||
'double_dangerous': 0,
|
||||
}
|
||||
return scores.get(combined, 50)
|
||||
|
||||
@@ -0,0 +1,692 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
星宿大运推演系统 v1.0 — Nakshatra Dasha Engine
|
||||
|
||||
支持:
|
||||
- Ashtottari Dasha(108年星宿大运,条件性系统)
|
||||
- Vimshottari Nakshatra-level 详细拆解
|
||||
- Nakshatra Transit Overlay(过境星宿叠加大运)
|
||||
- Nakshatra-based 时间窗口推演
|
||||
- 综合星宿大运报告
|
||||
|
||||
设计原则:
|
||||
- Ashtottari 作为 Vimshottari 的补充系统(条件:Rahu 不在 Kendra 时适用)
|
||||
- Nakshatra-level 拆解提供当前大运的星宿层面的精细时间线
|
||||
- Transit Overlay 将过境星宿叠加上大运周期,提供精确时间窗口
|
||||
"""
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# 从 nakshatra_advanced 导入需要的数据
|
||||
try:
|
||||
from nakshatra_advanced import (
|
||||
NAK_LIST, NAK_NAMES, NAK_LORDS, NAK_YEARS,
|
||||
find_nakshatra, calc_tara_bala,
|
||||
)
|
||||
except ImportError:
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from nakshatra_advanced import (
|
||||
NAK_LIST, NAK_NAMES, NAK_LORDS, NAK_YEARS,
|
||||
find_nakshatra, calc_tara_bala,
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# Ashtottari Dasha 数据表
|
||||
# ============================================================================
|
||||
|
||||
# Ashtottari 行星年数(108年制)
|
||||
ASHTOTTARI_YEARS = {
|
||||
'Sun': 6, 'Moon': 15, 'Mars': 8, 'Mercury': 17,
|
||||
'Saturn': 10, 'Jupiter': 19, 'Rahu': 12, 'Venus': 21,
|
||||
}
|
||||
ASHTOTTARI_TOTAL = sum(ASHTOTTARI_YEARS.values()) # 108
|
||||
|
||||
# Ashtottari 大运顺序(固定循环)
|
||||
ASHTOTTARI_ORDER = ['Sun', 'Moon', 'Mars', 'Mercury', 'Saturn', 'Jupiter', 'Rahu', 'Venus']
|
||||
|
||||
# Nakshatra → Ashtottari 起始守护星映射
|
||||
# 按 Vimshottari 星宿守护星对应 Ashtottari 起始点
|
||||
# Ketu 星宿 → 不作为 Ashtottari 起点,回溯到上一个 Venus
|
||||
NAK_TO_ASHTOTTARI_START = {
|
||||
'Ashwini': 'Venus', # Ketu nakshatra → 上一周期末尾 Venus
|
||||
'Bharani': 'Venus', # Venus
|
||||
'Krittika': 'Sun', # Sun
|
||||
'Rohini': 'Moon', # Moon
|
||||
'Mrigashira': 'Mars', # Mars
|
||||
'Ardra': 'Rahu', # Rahu
|
||||
'Punarvasu': 'Jupiter', # Jupiter
|
||||
'Pushya': 'Saturn', # Saturn
|
||||
'Ashlesha': 'Mercury', # Mercury
|
||||
'Magha': 'Venus', # Ketu → Venus
|
||||
'Purva Phalguni': 'Venus', # Venus
|
||||
'Uttara Phalguni': 'Sun', # Sun
|
||||
'Hasta': 'Moon', # Moon
|
||||
'Chitra': 'Mars', # Mars
|
||||
'Swati': 'Rahu', # Rahu
|
||||
'Vishakha': 'Jupiter', # Jupiter
|
||||
'Anuradha': 'Saturn', # Saturn
|
||||
'Jyeshtha': 'Mercury', # Mercury
|
||||
'Mula': 'Venus', # Ketu → Venus
|
||||
'Purva Ashadha': 'Venus', # Venus
|
||||
'Uttara Ashadha': 'Sun', # Sun
|
||||
'Shravana': 'Moon', # Moon
|
||||
'Dhanishta': 'Mars', # Mars
|
||||
'Shatabhisha': 'Rahu', # Rahu
|
||||
'Purva Bhadrapada': 'Jupiter', # Jupiter
|
||||
'Uttara Bhadrapada': 'Saturn', # Saturn
|
||||
'Revati': 'Mercury', # Mercury
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 核心计算函数
|
||||
# ============================================================================
|
||||
|
||||
def is_ashtottari_applicable(rahu_house: int) -> bool:
|
||||
"""
|
||||
判断 Ashtottari Dasha 是否适用
|
||||
|
||||
传统规则:当 Rahu 不在 Kendra(1/4/7/10宫)时,Ashtottari 优先。
|
||||
若 Rahu 在 Kendra,则使用 Vimshottari。
|
||||
|
||||
参数:
|
||||
rahu_house: Rahu 所在宫位 (1-12)
|
||||
|
||||
返回:
|
||||
bool: True = Ashtottari 适用
|
||||
"""
|
||||
kendras = {1, 4, 7, 10}
|
||||
return rahu_house not in kendras
|
||||
|
||||
|
||||
def calc_ashtottari_dasha(
|
||||
moon_longitude: float,
|
||||
birth_date_str: str,
|
||||
rahu_house: int,
|
||||
) -> Dict:
|
||||
"""
|
||||
计算 Ashtottari Dasha 108年大运序列 — v1.0
|
||||
|
||||
参数:
|
||||
moon_longitude: 月亮恒星黄道经度
|
||||
birth_date_str: 出生日期 YYYY-MM-DD
|
||||
rahu_house: Rahu 所在宫位 (1-12)
|
||||
|
||||
返回:
|
||||
dict: {
|
||||
'applicable': bool,
|
||||
'reason': str,
|
||||
'starting_lord': str,
|
||||
'sequence': [...],
|
||||
'total_years': 108,
|
||||
}
|
||||
"""
|
||||
applicable = is_ashtottari_applicable(rahu_house)
|
||||
|
||||
# 月亮星宿信息
|
||||
nak_data = find_nakshatra(moon_longitude)
|
||||
moon_nak = nak_data['nakshatra']
|
||||
moon_nak_idx = nak_data['nakshatra_idx']
|
||||
|
||||
# 起始守护星
|
||||
starting_lord = NAK_TO_ASHTOTTARI_START.get(moon_nak, 'Sun')
|
||||
|
||||
# 计算起始点的已用/剩余比例
|
||||
nak_span = 360.0 / 27
|
||||
nak_start = moon_nak_idx * nak_span
|
||||
deg_in_nak = moon_longitude - nak_start
|
||||
used_ratio = deg_in_nak / nak_span
|
||||
remaining_ratio = 1 - used_ratio
|
||||
|
||||
start_lord_years = ASHTOTTARI_YEARS.get(starting_lord, 6)
|
||||
first_remaining = start_lord_years * remaining_ratio
|
||||
|
||||
# 构建大运序列
|
||||
start_idx = ASHTOTTARI_ORDER.index(starting_lord)
|
||||
sequence = []
|
||||
cumulative = 0.0
|
||||
|
||||
# 首个大运的剩余部分
|
||||
sequence.append({
|
||||
'lord': starting_lord,
|
||||
'full_years': start_lord_years,
|
||||
'remaining_years': round(first_remaining, 4),
|
||||
'elapsed_years': round(start_lord_years - first_remaining, 4),
|
||||
'cumulative_from_birth': 0.0,
|
||||
'cumulative_to_birth': round(first_remaining, 4),
|
||||
'is_first': True,
|
||||
})
|
||||
cumulative += first_remaining
|
||||
|
||||
# 后续完整大运循环
|
||||
idx = (start_idx + 1) % 8
|
||||
cycle_count = 1
|
||||
while cumulative < 120: # 超出人生预期范围
|
||||
lord = ASHTOTTARI_ORDER[idx]
|
||||
years = ASHTOTTARI_YEARS[lord]
|
||||
sequence.append({
|
||||
'lord': lord,
|
||||
'full_years': years,
|
||||
'remaining_years': years,
|
||||
'elapsed_years': 0,
|
||||
'cumulative_from_birth': round(cumulative, 4),
|
||||
'cumulative_to_birth': round(cumulative + years, 4),
|
||||
'is_first': False,
|
||||
})
|
||||
cumulative += years
|
||||
idx = (idx + 1) % 8
|
||||
cycle_count += 1
|
||||
if cycle_count > 20: # 安全上限
|
||||
break
|
||||
|
||||
# 出生日期
|
||||
try:
|
||||
birth_date = datetime.strptime(birth_date_str, '%Y-%m-%d')
|
||||
except:
|
||||
birth_date = datetime(2000, 1, 1)
|
||||
|
||||
# 为大运添加日期范围
|
||||
for item in sequence:
|
||||
from_years = item['cumulative_from_birth']
|
||||
to_years = item['cumulative_to_birth']
|
||||
item['date_from'] = _add_years_to_date(birth_date, from_years)
|
||||
item['date_to'] = _add_years_to_date(birth_date, to_years)
|
||||
item['age_from'] = round(from_years, 2)
|
||||
item['age_to'] = round(to_years, 2)
|
||||
|
||||
if applicable:
|
||||
reason = f"Rahu 在第{rahu_house}宫,不在 Kendra(1/4/7/10),Ashtottari 适用"
|
||||
else:
|
||||
reason = f"Rahu 在第{rahu_house}宫,在 Kendra,建议使用 Vimshottari"
|
||||
|
||||
return {
|
||||
'system': 'Ashtottari Dasha(108年星宿大运)',
|
||||
'applicable': applicable,
|
||||
'reason': reason,
|
||||
'rahu_house': rahu_house,
|
||||
'moon_nakshatra': moon_nak,
|
||||
'starting_lord': starting_lord,
|
||||
'total_years': ASHTOTTARI_TOTAL,
|
||||
'first_remaining_years': round(first_remaining, 4),
|
||||
'sequence': sequence,
|
||||
'note': ('Ashtottari 作为 Vimshottari 的补充系统,'
|
||||
'当 Rahu 不在 Kendra 时提供替代视角。'
|
||||
'两者各有侧重:Vimshottari 偏"心识",Ashtottari 偏"命运安排"。'),
|
||||
}
|
||||
|
||||
|
||||
def calc_current_ashtottari(
|
||||
ashtottari_data: Dict,
|
||||
age: float,
|
||||
) -> Dict:
|
||||
"""
|
||||
从 Ashtottari 序列中定位当前年龄的大运
|
||||
|
||||
返回:
|
||||
dict: {lord, years_remaining, progress, sub_periods, ...}
|
||||
"""
|
||||
if not ashtottari_data.get('applicable', False):
|
||||
return {'applicable': False, 'reason': ashtottari_data.get('reason', '')}
|
||||
|
||||
sequence = ashtottari_data.get('sequence', [])
|
||||
current = None
|
||||
next_item = None
|
||||
|
||||
for i, item in enumerate(sequence):
|
||||
if item['cumulative_from_birth'] <= age < item['cumulative_to_birth']:
|
||||
current = item
|
||||
if i + 1 < len(sequence):
|
||||
next_item = sequence[i + 1]
|
||||
break
|
||||
|
||||
if current is None:
|
||||
return {'error': f'无法定位年龄 {age} 在 Ashtottari 序列中'}
|
||||
|
||||
years_elapsed_in_md = age - current['cumulative_from_birth']
|
||||
years_remaining = current['cumulative_to_birth'] - age
|
||||
progress = years_elapsed_in_md / current['remaining_years'] if current['remaining_years'] > 0 else 0
|
||||
|
||||
# 计算 Antardasha 子周期(简化:按比例分配)
|
||||
antardasha_sequence = _calc_ashtottari_antardasha(current['lord'], current['remaining_years'])
|
||||
|
||||
# 定位当前 Antardasha
|
||||
ad_cumulative = 0
|
||||
current_ad = None
|
||||
for ad in antardasha_sequence:
|
||||
ad_end = ad_cumulative + ad['years']
|
||||
if ad_cumulative <= years_elapsed_in_md < ad_end:
|
||||
current_ad = {
|
||||
**ad,
|
||||
'progress_in_ad': (years_elapsed_in_md - ad_cumulative) / ad['years'] if ad['years'] > 0 else 0,
|
||||
'remaining_years': round(ad_end - years_elapsed_in_md, 4),
|
||||
}
|
||||
break
|
||||
ad_cumulative = ad_end
|
||||
|
||||
return {
|
||||
'applicable': True,
|
||||
'current_mahadasha': current['lord'],
|
||||
'mahadasha_progress': round(progress, 4),
|
||||
'years_elapsed_in_md': round(years_elapsed_in_md, 4),
|
||||
'years_remaining_in_md': round(years_remaining, 4),
|
||||
'age_from': current['age_from'],
|
||||
'age_to': current['age_to'],
|
||||
'next_mahadasha': next_item['lord'] if next_item else None,
|
||||
'antardasha_sequence': antardasha_sequence,
|
||||
'current_antardasha': current_ad,
|
||||
}
|
||||
|
||||
|
||||
def _calc_ashtottari_antardasha(md_lord: str, md_years: float) -> List[Dict]:
|
||||
"""计算 Ashtottari Antardasha 子周期"""
|
||||
md_start_idx = ASHTOTTARI_ORDER.index(md_lord)
|
||||
total = sum(ASHTOTTARI_YEARS.values())
|
||||
|
||||
result = []
|
||||
for i in range(8):
|
||||
ad_lord = ASHTOTTARI_ORDER[(md_start_idx + i) % 8]
|
||||
ad_years_total = ASHTOTTARI_YEARS[ad_lord]
|
||||
# 按比例缩放
|
||||
ad_years_in_md = md_years * ad_years_total / total
|
||||
result.append({
|
||||
'lord': ad_lord,
|
||||
'years': round(ad_years_in_md, 4),
|
||||
'months': round(ad_years_in_md * 12, 1),
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Vimshottari Nakshatra-level 拆解
|
||||
# ============================================================================
|
||||
|
||||
# Vimshottari Dasha 顺序(9星循环)
|
||||
VIMSHOTTARI_ORDER = ['Ketu', 'Venus', 'Sun', 'Moon', 'Mars', 'Rahu', 'Jupiter', 'Saturn', 'Mercury']
|
||||
VIMSHOTTARI_YEARS = {'Ketu': 7, 'Venus': 20, 'Sun': 6, 'Moon': 10, 'Mars': 7, 'Rahu': 18, 'Jupiter': 16, 'Saturn': 19, 'Mercury': 17}
|
||||
|
||||
|
||||
def calc_nakshatra_dasha_breakdown(
|
||||
moon_longitude: float,
|
||||
planet_lons: Dict[str, float],
|
||||
birth_date_str: str,
|
||||
age: float,
|
||||
) -> Dict:
|
||||
"""
|
||||
Vimshottari Nakshatra-level 详细拆解 — v1.0
|
||||
|
||||
在当前 Vimshottari 大运/小运框架下,提供:
|
||||
1. 大运守护星的 Nakshatra 位置及其 Tara 关系
|
||||
2. 小运守护星的 Nakshatra 位置
|
||||
3. Nakshatra Pada 层面的时间窗口
|
||||
4. 月亮星宿到各大运/小运守护星星宿的 Tara Bala
|
||||
|
||||
参数:
|
||||
moon_longitude: 月亮恒星黄道经度
|
||||
planet_lons: {行星名: 恒星经度}
|
||||
birth_date_str: 出生日期 YYYY-MM-DD
|
||||
age: 当前年龄(岁)
|
||||
|
||||
返回:
|
||||
dict: 星宿层面的大运拆解
|
||||
"""
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# 大运守护星的 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
|
||||
current_ad = None
|
||||
for ad in antardasas:
|
||||
ad_end = ad_cumulative + ad['years']
|
||||
if ad_cumulative <= years_in_md < ad_end:
|
||||
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),
|
||||
}
|
||||
break
|
||||
ad_cumulative = ad_end
|
||||
|
||||
# 当前 Antardasha 守护星的 Nakshatra
|
||||
ad_lord_nak = None
|
||||
if current_ad and current_ad['lord'] in planet_lons:
|
||||
ad_lord_nak = find_nakshatra(planet_lons[current_ad['lord']])
|
||||
|
||||
# Nakshatra Tara 关系分析
|
||||
tara_analysis = {}
|
||||
if md_lord_nak:
|
||||
tara_analysis['mahadasha_lord'] = {
|
||||
'planet': md_lord,
|
||||
'nakshatra': md_lord_nak['nakshatra'],
|
||||
'pada': md_lord_nak['pada'],
|
||||
'tara_to_moon': calc_tara_bala(moon_nak_idx, md_lord_nak['nakshatra_idx']),
|
||||
}
|
||||
if ad_lord_nak and current_ad:
|
||||
tara_analysis['antardasha_lord'] = {
|
||||
'planet': current_ad['lord'],
|
||||
'nakshatra': ad_lord_nak['nakshatra'],
|
||||
'pada': ad_lord_nak['pada'],
|
||||
'tara_to_moon': calc_tara_bala(moon_nak_idx, ad_lord_nak['nakshatra_idx']),
|
||||
}
|
||||
|
||||
# 星宿层面的时间窗口(Pada-level 细分)
|
||||
pada_windows = []
|
||||
if md_lord_nak and current_ad:
|
||||
nak_span_deg = 360.0 / 27
|
||||
pada_span_deg = nak_span_deg / 4
|
||||
md_years_per_nak = md_years / 9 # 每个 Antardasha 时长近似
|
||||
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))
|
||||
pada_windows.append({
|
||||
'period': f"AD {ad['lord']}",
|
||||
'nakshatra': ad_nak['nakshatra'],
|
||||
'pada': ad_nak['pada'],
|
||||
'years': round(ad['years'], 4),
|
||||
'tara': calc_tara_bala(moon_nak_idx, ad_nak['nakshatra_idx']),
|
||||
})
|
||||
|
||||
# 构建返回
|
||||
birth_date = None
|
||||
try:
|
||||
birth_date = datetime.strptime(birth_date_str, '%Y-%m-%d')
|
||||
except:
|
||||
pass
|
||||
|
||||
return {
|
||||
'system': 'Vimshottari Nakshatra-level Breakdown',
|
||||
'moon_nakshatra': moon_nak_data['nakshatra'],
|
||||
'moon_nakshatra_idx': moon_nak_idx,
|
||||
'moon_pada': moon_nak_data['pada'],
|
||||
'current_age': age,
|
||||
'mahadasha': {
|
||||
'lord': md_lord,
|
||||
'years_total': round(md_years, 4),
|
||||
'age_range': f"{round(md_start_age, 2)}-{round(md_start_age + md_years, 2)}",
|
||||
'years_elapsed': round(years_in_md, 4),
|
||||
'years_remaining': round(md_years - years_in_md, 4),
|
||||
'progress': round(years_in_md / md_years, 4) if md_years > 0 else 0,
|
||||
'nakshatra': md_lord_nak,
|
||||
'tara': tara_analysis.get('mahadasha_lord', {}),
|
||||
},
|
||||
'antardasha': current_ad,
|
||||
'tara_analysis': tara_analysis,
|
||||
'pada_windows': pada_windows,
|
||||
}
|
||||
|
||||
|
||||
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(过境星宿叠加大运)
|
||||
# ============================================================================
|
||||
|
||||
def calc_nakshatra_transit_overlay(
|
||||
natal_planet_lons: Dict[str, float],
|
||||
transit_planet_lons: Dict[str, float],
|
||||
moon_nak_idx: int,
|
||||
) -> Dict:
|
||||
"""
|
||||
将过境星宿叠加在本命大运框架上 — v1.0
|
||||
|
||||
分析每个过境行星当前所在的星宿,及其:
|
||||
1. 与本命月亮星宿的 Tara 关系
|
||||
2. 与本命行星的星宿距离
|
||||
3. 当前星宿的 Pada 位置
|
||||
4. 吉凶评估
|
||||
|
||||
参数:
|
||||
natal_planet_lons: 本命行星经度
|
||||
transit_planet_lons: 过境行星经度
|
||||
moon_nak_idx: 本命月亮星宿索引
|
||||
|
||||
返回:
|
||||
dict: 过境星宿叠加分析
|
||||
"""
|
||||
results = {}
|
||||
|
||||
for pname, t_lon in transit_planet_lons.items():
|
||||
t_nak = find_nakshatra(t_lon)
|
||||
tara = calc_tara_bala(moon_nak_idx, t_nak['nakshatra_idx'])
|
||||
|
||||
# 过境星宿是否与某本命行星的星宿重叠
|
||||
same_nak_as_natal = []
|
||||
n_lon = natal_planet_lons.get(pname)
|
||||
if n_lon is not None:
|
||||
n_nak = find_nakshatra(n_lon)
|
||||
if n_nak['nakshatra_idx'] == t_nak['nakshatra_idx']:
|
||||
same_nak_as_natal.append('self')
|
||||
|
||||
for np, nl in natal_planet_lons.items():
|
||||
if np == pname:
|
||||
continue
|
||||
n_nak = find_nakshatra(nl)
|
||||
if n_nak['nakshatra_idx'] == t_nak['nakshatra_idx']:
|
||||
same_nak_as_natal.append(np)
|
||||
|
||||
# 评估
|
||||
if tara['is_auspicious']:
|
||||
transit_quality = 'favorable'
|
||||
elif tara['is_dangerous']:
|
||||
transit_quality = 'challenging'
|
||||
else:
|
||||
transit_quality = 'neutral'
|
||||
|
||||
if same_nak_as_natal:
|
||||
transit_quality = transit_quality + '_nak_conjunction'
|
||||
note = f"过境星宿与本命{'/'.join(same_nak_as_natal)}同星宿,叠加效应增强"
|
||||
else:
|
||||
note = ''
|
||||
|
||||
results[pname] = {
|
||||
'transit_nakshatra': t_nak['nakshatra'],
|
||||
'transit_nakshatra_lord': t_nak['nakshatra_lord'],
|
||||
'transit_pada': t_nak['pada'],
|
||||
'tara_to_natal_moon': tara,
|
||||
'same_nakshatra_as': same_nak_as_natal if same_nak_as_natal else None,
|
||||
'quality': transit_quality,
|
||||
'note': note,
|
||||
'gana': t_nak['gana'],
|
||||
'element': t_nak['element'],
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 综合星宿大运报告
|
||||
# ============================================================================
|
||||
|
||||
def nakshatra_dasha_full_report(
|
||||
chart_data: Dict,
|
||||
birth_date_str: str,
|
||||
age: float,
|
||||
transit_planet_lons: Optional[Dict[str, float]] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
综合星宿大运完整报告 — v1.0
|
||||
|
||||
包含:
|
||||
1. Ashtottari Dasha(条件性,Rahu 不在 Kendra 时计算)
|
||||
2. Vimshottari Nakshatra-level 拆解
|
||||
3. Nakshatra Transit Overlay(如提供过境数据)
|
||||
4. 综合时间窗口与 Tara 评估
|
||||
|
||||
参数:
|
||||
chart_data: 星盘数据
|
||||
birth_date_str: 出生日期 YYYY-MM-DD
|
||||
age: 当前年龄
|
||||
transit_planet_lons: 过境行星经度(可选)
|
||||
|
||||
返回:
|
||||
dict: 完整星宿大运报告
|
||||
"""
|
||||
planets = chart_data.get('planets', {})
|
||||
planet_lons = {}
|
||||
for pn, pd in planets.items():
|
||||
if isinstance(pd, dict) and 'degree' in pd:
|
||||
planet_lons[pn] = pd['degree']
|
||||
|
||||
if not planet_lons:
|
||||
return {'error': '无法获取行星经度数据'}
|
||||
|
||||
moon_lon = planet_lons.get('Moon', 0)
|
||||
moon_nak = find_nakshatra(moon_lon)
|
||||
moon_nak_idx = moon_nak['nakshatra_idx']
|
||||
|
||||
# 确定 Rahu 宫位
|
||||
asc_idx = chart_data.get('ascendant_index', 0)
|
||||
rahu_deg = None
|
||||
rahu_house = 1 # 默认
|
||||
if 'Rahu' in planet_lons:
|
||||
rahu_deg = planet_lons['Rahu']
|
||||
rahu_sign_idx = int(rahu_deg / 30) % 12
|
||||
rahu_house = ((rahu_sign_idx - asc_idx) % 12) + 1
|
||||
|
||||
report = {}
|
||||
|
||||
# 1. Ashtottari Dasha
|
||||
report['ashtottari'] = calc_ashtottari_dasha(moon_lon, birth_date_str, rahu_house)
|
||||
if report['ashtottari']['applicable']:
|
||||
report['ashtottari_current'] = calc_current_ashtottari(report['ashtottari'], age)
|
||||
|
||||
# 2. Vimshottari Nakshatra-level
|
||||
report['vimshottari_nak_breakdown'] = calc_nakshatra_dasha_breakdown(
|
||||
moon_lon, planet_lons, birth_date_str, age)
|
||||
|
||||
# 3. Nakshatra Transit Overlay
|
||||
if transit_planet_lons:
|
||||
report['transit_overlay'] = calc_nakshatra_transit_overlay(
|
||||
planet_lons, transit_planet_lons, moon_nak_idx)
|
||||
|
||||
# 4. 汇总
|
||||
md_info = report['vimshottari_nak_breakdown'].get('mahadasha', {})
|
||||
ad_info = report['vimshottari_nak_breakdown'].get('antardasha', {})
|
||||
|
||||
summary = {
|
||||
'moon_nakshatra': moon_nak['nakshatra'],
|
||||
'moon_nakshatra_lord': moon_nak['nakshatra_lord'],
|
||||
'moon_pada': moon_nak['pada'],
|
||||
'current_age': age,
|
||||
'vimshottari_md': md_info.get('lord', 'N/A'),
|
||||
'vimshottari_ad': ad_info.get('lord', 'N/A') if ad_info else 'N/A',
|
||||
'ashtottari_applicable': report['ashtottari']['applicable'],
|
||||
}
|
||||
|
||||
if report['ashtottari']['applicable'] and 'ashtottari_current' in report:
|
||||
ac = report['ashtottari_current']
|
||||
summary['ashtottari_md'] = ac.get('current_mahadasha', 'N/A')
|
||||
if ac.get('current_antardasha'):
|
||||
summary['ashtottari_ad'] = ac['current_antardasha']['lord']
|
||||
|
||||
# Dasha 系统的 Nakshatra Tara 评估
|
||||
tara_md = report['vimshottari_nak_breakdown'].get('tara_analysis', {}).get('mahadasha_lord', {})
|
||||
tara_ad = report['vimshottari_nak_breakdown'].get('tara_analysis', {}).get('antardasha_lord', {})
|
||||
|
||||
summary['md_tara_to_moon'] = tara_md.get('tara_to_moon', {}).get('tara_cn', 'N/A') if tara_md else 'N/A'
|
||||
summary['ad_tara_to_moon'] = tara_ad.get('tara_to_moon', {}).get('tara_cn', 'N/A') if tara_ad else 'N/A'
|
||||
|
||||
report['summary'] = summary
|
||||
|
||||
return report
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 工具函数
|
||||
# ============================================================================
|
||||
|
||||
def _add_years_to_date(dt: datetime, years: float) -> str:
|
||||
"""日期 + 年数 → ISO 日期字符串"""
|
||||
total_days = years * 365.25
|
||||
new_dt = dt + timedelta(days=total_days)
|
||||
return new_dt.strftime('%Y-%m-%d')
|
||||
Reference in New Issue
Block a user