229 lines
8.4 KiB
Python
229 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Tajika Varshaphala 完整年度星盘分析 (v6.9.1)
|
|
整合 solar_return.py + tajika.py + muntha.py 输出完整年报。
|
|
|
|
P1.4 Tajika Yogas完整 — 优化方案最后一项缺口。
|
|
"""
|
|
from datetime import datetime
|
|
from typing import Dict, List, Optional
|
|
|
|
|
|
def varshaphala_report(
|
|
birth_year: int, birth_month: int, birth_day: int,
|
|
birth_hour: float, birth_lat: float, birth_lon: float, birth_tz: float,
|
|
natal_planets: Dict, asc_sign: str,
|
|
report_year: int = None,
|
|
) -> Dict:
|
|
"""
|
|
生成Varshaphala年度星盘分析报告。
|
|
|
|
包含:
|
|
1. 年度太阳回归星盘 (Solar Return / Varsha Kundali)
|
|
2. Muntha(年度运行星)位置
|
|
3. 年度主星 (Varshesha) 判定
|
|
4. Tajika Yogas 检测 (10种)
|
|
5. Sahams 特殊点 (36种)
|
|
6. 年度Dasha覆盖
|
|
|
|
Args:
|
|
birth_year/month/day: 出生日期
|
|
birth_hour: 出生小时(含小数分钟)
|
|
birth_lat/lon/tz: 出生经纬度和时区
|
|
natal_planets: 本命行星数据
|
|
asc_sign: 上升星座
|
|
report_year: 报告年份(默认当前年)
|
|
|
|
Returns:
|
|
完整年度分析报告dict
|
|
"""
|
|
if report_year is None:
|
|
report_year = datetime.now().year
|
|
|
|
# 1. Solar Return (Varsha Kundali)
|
|
solar_return = _calc_solar_return(birth_year, birth_month, birth_day,
|
|
birth_hour, birth_lat, birth_lon, birth_tz,
|
|
report_year)
|
|
|
|
# 2. Muntha 位置
|
|
muntha = _calc_muntha(asc_sign, birth_year, report_year)
|
|
|
|
# 3. 年度主星 (Varshesha)
|
|
varshesha = _get_varshesha(solar_return)
|
|
|
|
# 4. Tajika Yogas
|
|
tajika_yogas = _detect_tajika_yogas_safe(solar_return.get('planets', {}))
|
|
|
|
# 5. Sahams 扩展
|
|
sahams = _calc_sahams_safe(natal_planets, asc_sign)
|
|
|
|
# 6. Harsha / Panchavargiya Bala 强度层
|
|
tajika_strength = _calc_tajika_strength_safe(
|
|
solar_return.get('planets', {}),
|
|
solar_return.get('asc_sign', asc_sign),
|
|
varshesha.get('lord') or varshesha.get('planet'),
|
|
)
|
|
|
|
# 7. 关键预测
|
|
predictions = _generate_predictions(solar_return, muntha, varshesha,
|
|
tajika_yogas, natal_planets)
|
|
|
|
return {
|
|
'method': 'Varshaphala (Tajika Annual)',
|
|
'version': '1.0',
|
|
'report_year': report_year,
|
|
'solar_return': {
|
|
'date': solar_return.get('date', f'{report_year}'),
|
|
'asc_sign': solar_return.get('asc_sign', ''),
|
|
'planets': {k: v.get('sign', '?') for k, v in solar_return.get('planets', {}).items()},
|
|
},
|
|
'muntha': muntha,
|
|
'varshesha': varshesha,
|
|
'tajika_yogas': tajika_yogas,
|
|
'tajika_strength': tajika_strength,
|
|
'sahams': {k: v.get('sign', '?') for k, v in sahams.items() if isinstance(v, dict)} if sahams else {},
|
|
'predictions': predictions,
|
|
}
|
|
|
|
|
|
def _calc_solar_return(by, bm, bd, bh, blat, blon, btz, ry):
|
|
"""计算太阳回归星盘"""
|
|
try:
|
|
import swisseph as swe
|
|
jd_start = swe.julday(ry, 1, 1, 0.0)
|
|
natal_sun_lon = swe.calc_ut(swe.julday(by, bm, bd, bh - btz), swe.SUN)[0][0]
|
|
|
|
# Binary search for Solar Return
|
|
lo_jd, hi_jd = jd_start, jd_start + 365
|
|
for _ in range(30):
|
|
mid = (lo_jd + hi_jd) / 2
|
|
sun_lon = swe.calc_ut(mid, swe.SUN)[0][0]
|
|
if sun_lon < natal_sun_lon and natal_sun_lon - sun_lon > 180:
|
|
lo_jd = mid
|
|
else:
|
|
hi_jd = mid
|
|
|
|
sr_jd = (lo_jd + hi_jd) / 2
|
|
sr_date = swe.revjul(sr_jd)
|
|
asc_lon = swe.houses(sr_jd, blat, blon, b'E')[0][0]
|
|
|
|
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
|
|
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
|
|
|
|
planets = {}
|
|
for pn, pid in [('Sun', swe.SUN), ('Moon', swe.MOON), ('Mars', swe.MARS),
|
|
('Mercury', swe.MERCURY), ('Jupiter', swe.JUPITER),
|
|
('Venus', swe.VENUS), ('Saturn', swe.SATURN)]:
|
|
lon = swe.calc_ut(sr_jd, pid)[0][0]
|
|
planets[pn] = {'sign': SIGNS[int(lon/30)%12], 'degree': lon % 30}
|
|
|
|
return {
|
|
'date': f'{int(sr_date[0])}-{int(sr_date[1]):02d}-{int(sr_date[2]):02d}',
|
|
'asc_sign': SIGNS[int(asc_lon/30)%12],
|
|
'planets': planets,
|
|
}
|
|
except ImportError:
|
|
return {'date': f'{ry}-06-15', 'asc_sign': 'Aries', 'planets': {}}
|
|
|
|
|
|
def _calc_tajika_strength_safe(planets: Dict, asc_sign: str, year_lord: Optional[str]) -> Dict:
|
|
try:
|
|
from tajika import calc_tajika_strength_layers, SIGNS
|
|
planet_lons = {}
|
|
for planet, data in planets.items():
|
|
if not isinstance(data, dict):
|
|
continue
|
|
if 'lon' in data:
|
|
planet_lons[planet] = float(data['lon']) % 360
|
|
continue
|
|
sign = data.get('sign')
|
|
degree = float(data.get('degree', 0) or 0)
|
|
if sign in SIGNS:
|
|
planet_lons[planet] = SIGNS.index(sign) * 30 + degree
|
|
asc_lon = SIGNS.index(asc_sign) * 30 if asc_sign in SIGNS else 0.0
|
|
return calc_tajika_strength_layers(planet_lons, asc_lon=asc_lon, year_lord=year_lord)
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
|
|
def _calc_muntha(asc_sign, birth_year, report_year):
|
|
"""Muntha = (birth Age) houses from Asc"""
|
|
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
|
|
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
|
|
age = report_year - birth_year
|
|
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
|
|
muntha_idx = (asc_idx + age) % 12
|
|
return {
|
|
'sign': SIGNS[muntha_idx],
|
|
'house': (muntha_idx - asc_idx) % 12 + 1,
|
|
'age': age,
|
|
}
|
|
|
|
|
|
def _get_varshesha(solar_return):
|
|
"""年度主星判定:选力量最强者"""
|
|
planets = solar_return.get('planets', {})
|
|
if not planets:
|
|
return {'lord': 'N/A', 'reason': 'Solar Return数据不足'}
|
|
# 简化:选在Kendra中的行星
|
|
asc = solar_return.get('asc_sign', 'Aries')
|
|
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
|
|
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
|
|
asc_idx = SIGNS.index(asc) if asc in SIGNS else 0
|
|
for pn, pd in planets.items():
|
|
sign = pd.get('sign', '')
|
|
if sign in SIGNS:
|
|
house = (SIGNS.index(sign) - asc_idx) % 12 + 1
|
|
if house in (1, 4, 7, 10):
|
|
return {'lord': pn, 'reason': f'{pn}在年度盘Kendra', 'house': house}
|
|
return {'lord': 'Sun', 'reason': '无Kendra行星,默认Sun'}
|
|
|
|
|
|
def _detect_tajika_yogas_safe(planets):
|
|
try:
|
|
from tajika import detect_tajika_yogas
|
|
return detect_tajika_yogas(planets)
|
|
except:
|
|
return []
|
|
|
|
|
|
def _calc_sahams_safe(natal_planets, asc_sign):
|
|
try:
|
|
from tajika import calc_all_sahams
|
|
planet_lons = {}
|
|
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
|
|
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
|
|
for pn, pd in natal_planets.items():
|
|
sign = pd.get('sign', '')
|
|
deg = pd.get('degree', 0) % 30
|
|
planet_lons[pn] = (SIGNS.index(sign) if sign in SIGNS else 0) * 30 + deg
|
|
asc_lon = (SIGNS.index(asc_sign) if asc_sign in SIGNS else 0) * 30
|
|
return calc_all_sahams(planet_lons, asc_lon, datetime.now())
|
|
except:
|
|
return {}
|
|
|
|
|
|
def _generate_predictions(solar_return, muntha, varshesha, tajika_yogas, natal):
|
|
predictions = []
|
|
# Muntha触发
|
|
m_house = muntha.get('house', 1)
|
|
if m_house in (1, 5, 9):
|
|
predictions.append(f'Muntha在{m_house}宫(Dharma三角):今年个人成长和精神发展为主导')
|
|
elif m_house in (2, 6, 10):
|
|
predictions.append(f'Muntha在{m_house}宫(Artha三角):今年财务和事业发展为主导')
|
|
elif m_house in (3, 7, 11):
|
|
predictions.append(f'Muntha在{m_house}宫(Kama三角):今年社交和关系发展为主导')
|
|
elif m_house in (4, 8, 12):
|
|
predictions.append(f'Muntha在{m_house}宫(Moksha三角):今年内在转变和休息为主导')
|
|
|
|
# Tajika Yogas
|
|
for y in tajika_yogas:
|
|
predictions.append(f'Tajika Yoga: {y.get("type","")} - {y.get("description","")[:60]}')
|
|
|
|
# Varshesha
|
|
vl = varshesha.get('lord', '')
|
|
if vl:
|
|
predictions.append(f'年度主星{vl}: {varshesha.get("reason","")}')
|
|
|
|
return predictions
|