v3.7.0: 新增7大模块 - BPHS十六分盘/精确相位/Jaimini/高级Nakshatra/Argala门闩/Tajika年运/合盘分析
- varga.py: D2-D60 BPHS十六分盘完整计算 - aspects.py: 度数精确相位系统(tight/moderate/loose) - jaimini.py: Chara Karaka 7/8 + Chara Dasha + Karakamsha - nakshatra_advanced.py: Tara Bala + Sub-Lord KP系统 - argala.py: Argala门闩 + Virodha反干预 - tajika.py: Muntha/YearLord/Mudda Dasha/Tri-Pataka - synastry.py: Ashta Koota 36分制 + Mangal Dosha + Papasamya 引擎升级至v3.7.0, 21个子命令全链路验证通过
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
吠陀占星精确相位模块 v1.0
|
||||
基于度数的Drishti(相位)计算系统
|
||||
|
||||
支持:
|
||||
- 标准7宫对冲相位
|
||||
- 特殊相位: Mars(4/7/8), Jupiter(5/7/9), Saturn(3/7/10)
|
||||
- 度数容许度(orb): 紧密/中等/松散三级
|
||||
- 入相位(applying)/出相位(separating)判断
|
||||
- 合相(conjunction)精度分析
|
||||
"""
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
|
||||
# 标准容许度(度)
|
||||
DEFAULT_ORBS = {
|
||||
'Sun': 10, 'Moon': 10, 'Mars': 8, 'Mercury': 7,
|
||||
'Jupiter': 9, 'Venus': 7, 'Saturn': 9, 'Rahu': 8, 'Ketu': 8,
|
||||
'default': 7
|
||||
}
|
||||
ORBIT_CATEGORIES = {
|
||||
'tight': 3, # 紧密相位(最强)
|
||||
'moderate': 6, # 中等相位
|
||||
'loose': 10, # 松散相位(最弱)
|
||||
}
|
||||
|
||||
# 特殊相位规则(从行星所在宫位算起)
|
||||
SPECIAL_ASPECTS = {
|
||||
'Mars': [4, 7, 8],
|
||||
'Jupiter': [5, 7, 9],
|
||||
'Saturn': [3, 7, 10],
|
||||
}
|
||||
# 所有行星都有7宫相位
|
||||
ALL_HAVE_7TH = True
|
||||
|
||||
|
||||
def calc_all_aspects(planet_longitudes: Dict[str, float],
|
||||
ascendant_longitude: float) -> Dict:
|
||||
"""
|
||||
计算所有行星之间的精确相位关系
|
||||
|
||||
参数:
|
||||
planet_longitudes: {'Sun': 12.5, 'Moon': 45.3, ...} 恒星黄道经度
|
||||
ascendant_longitude: 上升点经度
|
||||
|
||||
返回: {
|
||||
'aspects': [每对行星的相位详情],
|
||||
'conjunctions': [合相详情],
|
||||
'asc_aspects': [上升点被哪些星相位],
|
||||
'tight_aspects': [紧密相位(<3°)],
|
||||
'summary': 统计摘要
|
||||
}
|
||||
"""
|
||||
planets = {k: v for k, v in planet_longitudes.items()
|
||||
if k not in ('Ketu',) and isinstance(v, (int, float))}
|
||||
# Ketu = Rahu + 180
|
||||
if 'Rahu' in planet_longitudes and 'Ketu' not in planets:
|
||||
planets['Ketu'] = (planet_longitudes['Rahu'] + 180) % 360
|
||||
|
||||
aspects = []
|
||||
conjunctions = []
|
||||
tight_aspects = []
|
||||
planet_names = list(planets.keys())
|
||||
|
||||
for i, p1 in enumerate(planet_names):
|
||||
for j, p2 in enumerate(planet_names):
|
||||
if j <= i:
|
||||
continue
|
||||
lon1, lon2 = planets[p1], planets[p2]
|
||||
pair_aspects = _calc_pair_aspects(p1, lon1, p2, lon2)
|
||||
for asp in pair_aspects:
|
||||
aspects.append(asp)
|
||||
if asp['type'] == 'conjunction':
|
||||
conjunctions.append(asp)
|
||||
if asp['orb'] <= ORBIT_CATEGORIES['tight']:
|
||||
tight_aspects.append(asp)
|
||||
|
||||
# 上升点相位
|
||||
asc_aspects = []
|
||||
for pname, plon in planets.items():
|
||||
diff = abs(ascendant_longitude - plon) % 360
|
||||
if diff > 180: diff = 360 - diff
|
||||
if diff <= 15: # 上升点容许度
|
||||
asc_aspects.append({
|
||||
'planet': pname, 'degree_diff': round(diff, 2),
|
||||
'orb_category': _orb_category(diff, 15),
|
||||
'applying': plon > ascendant_longitude, # 简化判断
|
||||
})
|
||||
|
||||
return {
|
||||
'aspects': aspects,
|
||||
'conjunctions': conjunctions,
|
||||
'tight_aspects': tight_aspects,
|
||||
'asc_aspects': asc_aspects,
|
||||
'summary': {
|
||||
'total_aspects': len(aspects),
|
||||
'conjunctions': len(conjunctions),
|
||||
'tight_aspects': len(tight_aspects),
|
||||
'by_type': _count_by_type(aspects),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _calc_pair_aspects(p1, lon1, p2, lon2) -> List[Dict]:
|
||||
"""计算两颗行星之间的所有相位"""
|
||||
results = []
|
||||
diff = (lon2 - lon1) % 360
|
||||
if diff > 180:
|
||||
diff = 360 - diff
|
||||
|
||||
# 1. 合相 (conjunction): 同一星座内或跨星座但距离很近
|
||||
max_conj_orb = max(DEFAULT_ORBS.get(p1, 7), DEFAULT_ORBS.get(p2, 7))
|
||||
if diff <= max_conj_orb:
|
||||
results.append({
|
||||
'planet1': p1, 'planet2': p2,
|
||||
'type': 'conjunction', 'aspect_degree': 0,
|
||||
'actual_diff': round(diff, 2), 'orb': round(diff, 2),
|
||||
'orb_category': _orb_category(diff, max_conj_orb),
|
||||
'applying': (lon2 - lon1) % 360 < 180,
|
||||
'strength': _aspect_strength(diff, max_conj_orb),
|
||||
'description': f"{p1}与{p2}合相(差距{diff:.1f}°)"
|
||||
})
|
||||
|
||||
# 2. 对冲 (opposition): 7宫相位
|
||||
opp_diff = abs(diff - 180)
|
||||
opp_orb = max(DEFAULT_ORBS.get(p1, 7), DEFAULT_ORBS.get(p2, 7))
|
||||
if opp_diff <= opp_orb and diff > max_conj_orb:
|
||||
results.append({
|
||||
'planet1': p1, 'planet2': p2,
|
||||
'type': 'opposition', 'aspect_degree': 180,
|
||||
'actual_diff': round(diff, 2), 'orb': round(opp_diff, 2),
|
||||
'orb_category': _orb_category(opp_diff, opp_orb),
|
||||
'applying': (lon2 - lon1) % 360 < 180,
|
||||
'strength': _aspect_strength(opp_diff, opp_orb),
|
||||
'description': f"{p1}对冲{p2}(差距{opp_diff:.1f}°)"
|
||||
})
|
||||
|
||||
# 3. 特殊相位
|
||||
for planet, aspect_degrees in SPECIAL_ASPECTS.items():
|
||||
if planet == p1:
|
||||
for asp_deg in aspect_degrees:
|
||||
if asp_deg == 7:
|
||||
continue # 已在对冲中处理
|
||||
target = asp_deg * 30 # 转换为度数(每宫30°)
|
||||
asp_diff = abs(diff - target)
|
||||
if asp_diff > 180: asp_diff = 360 - asp_diff
|
||||
asp_orb = 8 # 特殊相位容许度
|
||||
if asp_diff <= asp_orb:
|
||||
results.append({
|
||||
'planet1': p1, 'planet2': p2,
|
||||
'type': f'{planet}_aspect_{asp_deg}',
|
||||
'aspect_degree': target,
|
||||
'actual_diff': round(diff, 2), 'orb': round(asp_diff, 2),
|
||||
'orb_category': _orb_category(asp_diff, asp_orb),
|
||||
'applying': (lon2 - lon1) % 360 < 180,
|
||||
'strength': _aspect_strength(asp_diff, asp_orb),
|
||||
'description': f"{p1}({asp_deg}宫相位){p2}(差距{asp_diff:.1f}°)"
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def calc_house_aspects(planet_lon: float, planet_name: str,
|
||||
asc_lon: float) -> Dict:
|
||||
"""计算单颗行星对所有12宫的相位"""
|
||||
planet_sign = int(planet_lon / 30) % 12
|
||||
asc_sign = int(asc_lon / 30) % 12
|
||||
planet_house = ((planet_sign - asc_sign) % 12) + 1
|
||||
|
||||
aspect_houses = [7] # 所有行星都有7宫相位
|
||||
if planet_name in SPECIAL_ASPECTS:
|
||||
aspect_houses = SPECIAL_ASPECTS[planet_name]
|
||||
|
||||
result = {'planet': planet_name, 'house': planet_house, 'aspects_to': {}}
|
||||
for ah in aspect_houses:
|
||||
target_house = ((planet_house - 1 + ah - 1) % 12) + 1
|
||||
result['aspects_to'][target_house] = {
|
||||
'from_house': planet_house,
|
||||
'aspect_type': f'{ah}宫相位',
|
||||
'is_special': ah != 7,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def _orb_category(diff, max_orb):
|
||||
if diff <= ORBIT_CATEGORIES['tight']: return 'tight'
|
||||
if diff <= ORBIT_CATEGORIES['moderate']: return 'moderate'
|
||||
return 'loose'
|
||||
|
||||
|
||||
def _aspect_strength(diff, max_orb):
|
||||
"""相位强度评分 0-100"""
|
||||
if diff == 0: return 100
|
||||
ratio = diff / max_orb
|
||||
return max(0, round(100 * (1 - ratio), 1))
|
||||
|
||||
|
||||
def _count_by_type(aspects):
|
||||
counts = {}
|
||||
for a in aspects:
|
||||
t = a['type']
|
||||
counts[t] = counts.get(t, 0) + 1
|
||||
return counts
|
||||
Reference in New Issue
Block a user