555 lines
20 KiB
Python
555 lines
20 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Shadbala 计算模块(六重力量)
|
||
内部一致的 Parashara-inspired 相对强弱参考;外部绝对值校准前状态为 partial
|
||
|
||
六种力量:
|
||
1. Sthana Bala(位置力量)
|
||
2. Dig Bala(方向力量)
|
||
3. Kala Bala(时间力量)
|
||
4. Chesta Bala(运动力量)
|
||
5. Naisargika Bala(天然力量)
|
||
6. Drik Bala(相位力量)
|
||
"""
|
||
|
||
import math
|
||
from typing import Dict, Tuple
|
||
|
||
# ============================================================================
|
||
# 常量
|
||
# ============================================================================
|
||
SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
|
||
'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces']
|
||
|
||
SIGN_LORDS = {
|
||
'Aries': 'Mars', 'Taurus': 'Venus', 'Gemini': 'Mercury', 'Cancer': 'Moon',
|
||
'Leo': 'Sun', 'Virgo': 'Mercury', 'Libra': 'Venus', 'Scorpio': 'Mars',
|
||
'Sagittarius': 'Jupiter', 'Capricorn': 'Saturn', 'Aquarius': 'Saturn', 'Pisces': 'Jupiter'
|
||
}
|
||
|
||
# 入庙度数(sign_idx * 30 + degree)
|
||
EXALTATION_DEG = {
|
||
'Sun': 10.0, 'Moon': 33.0, 'Mars': 298.0, 'Mercury': 165.0,
|
||
'Jupiter': 95.0, 'Venus': 357.0, 'Saturn': 200.0
|
||
}
|
||
|
||
# 落陷度数(入庙 + 180°)
|
||
DEBILITATION_DEG = {p: (d + 180) % 360 for p, d in EXALTATION_DEG.items()}
|
||
|
||
# 行星友好/敌对关系
|
||
FRIENDSHIP = {
|
||
'Sun': {'friend': ['Moon', 'Mars', 'Jupiter'], 'enemy': ['Saturn', 'Venus'], 'neutral': ['Mercury']},
|
||
'Moon': {'friend': ['Sun', 'Mercury'], 'enemy': [], 'neutral': ['Mars', 'Jupiter', 'Venus', 'Saturn']},
|
||
'Mars': {'friend': ['Sun', 'Moon', 'Jupiter'], 'enemy': ['Mercury'], 'neutral': ['Venus', 'Saturn']},
|
||
'Mercury': {'friend': ['Sun', 'Venus'], 'enemy': ['Moon'], 'neutral': ['Mars', 'Jupiter', 'Saturn']},
|
||
'Jupiter': {'friend': ['Sun', 'Moon', 'Mars'], 'enemy': ['Mercury', 'Venus'], 'neutral': ['Saturn']},
|
||
'Venus': {'friend': ['Mercury', 'Saturn'], 'enemy': ['Sun', 'Moon'], 'neutral': ['Mars', 'Jupiter']},
|
||
'Saturn': {'friend': ['Mercury', 'Venus'], 'enemy': ['Sun', 'Moon', 'Mars'], 'neutral': ['Jupiter']},
|
||
}
|
||
|
||
# Dig Bala 最强宫位
|
||
DIG_BALA_HOUSE = {
|
||
'Sun': 10, 'Mars': 10, # Midheaven
|
||
'Moon': 4, 'Venus': 4, # Nadir
|
||
'Jupiter': 1, 'Mercury': 1, # Ascendant
|
||
'Saturn': 7, # Descendant
|
||
}
|
||
|
||
# Naisargika Bala(天然力量,单位 Virupas)
|
||
NAISARGIKA_BALA = {
|
||
'Sun': 60.0, 'Moon': 60.0, 'Venus': 52.5,
|
||
'Jupiter': 45.0, 'Mercury': 37.5, 'Mars': 30.0, 'Saturn': 22.5
|
||
}
|
||
|
||
# Shadbala 最低要求(Rupas)
|
||
MIN_REQUIRED = {
|
||
'Sun': 5.0, 'Moon': 6.0, 'Mars': 5.0,
|
||
'Mercury': 7.0, 'Jupiter': 6.5, 'Venus': 5.5, 'Saturn': 5.0
|
||
}
|
||
|
||
# 昼强/夜强行星
|
||
DIURNAL_STRONG = ['Sun', 'Jupiter', 'Venus']
|
||
NOCTURNAL_STRONG = ['Moon', 'Mars', 'Saturn']
|
||
|
||
# 吉星/凶星
|
||
BENEFICS = ['Jupiter', 'Venus', 'Mercury']
|
||
MALEFICS = ['Saturn', 'Mars', 'Sun']
|
||
|
||
# 行星相位规则(所有行星都有7宫相位,特殊相位如下)
|
||
SPECIAL_ASPECTS = {
|
||
'Mars': [4, 8], # 火星额外看4宫和8宫
|
||
'Jupiter': [5, 9], # 木星额外看5宫和9宫
|
||
'Saturn': [3, 10], # 土星额外看3宫和10宫
|
||
}
|
||
|
||
# Virupas → Rupas 转换(60 Virupas = 1 Rupa)
|
||
VIRUPAS_PER_RUPA = 60.0
|
||
|
||
|
||
def calc_shadbala(planets: Dict, asc_sign: str, birth_hour: float,
|
||
sun_lon: float, moon_lon: float,
|
||
birth_minute: float = 0.0) -> Dict:
|
||
"""
|
||
计算 Shadbala 相对强弱参考(内部一致;外部绝对值校准前 partial)
|
||
|
||
Args:
|
||
planets: 行星数据 dict,每颗行星需要 {sign, degree, house, retrograde, speed}
|
||
asc_sign: 上升星座名称
|
||
birth_hour: 出生时间(当地时间,24小时制)
|
||
sun_lon: 太阳恒星黄道经度
|
||
moon_lon: 月亮恒星黄道经度
|
||
|
||
Returns:
|
||
Shadbala 计算结果(内部一致的相对强弱参考)
|
||
"""
|
||
results = {}
|
||
is_night = birth_hour < 6.0 or birth_hour >= 18.0
|
||
sun_northern = sun_lon >= 270 or sun_lon < 90 # Uttarayana 概略判断
|
||
|
||
for pname in ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn']:
|
||
if pname not in planets:
|
||
continue
|
||
p = planets[pname]
|
||
lon = p.get('degree', 0)
|
||
sign = p.get('sign', 'Aries')
|
||
house = p.get('house', 1)
|
||
retro = p.get('retrograde', False)
|
||
speed = p.get('speed', 1.0)
|
||
|
||
# 1. Sthana Bala(位置力量)
|
||
sthana = calc_sthana_bala(pname, lon, sign, house)
|
||
|
||
# 2. Dig Bala(方向力量)
|
||
dig = calc_dig_bala(pname, house)
|
||
|
||
# 3. Kala Bala(时间力量)
|
||
kala = calc_kala_bala(pname, is_night, sun_northern, sun_lon, moon_lon,
|
||
birth_hour, birth_minute)
|
||
|
||
# 4. Chesta Bala(运动力量)
|
||
chesta = calc_chesta_bala(pname, retro, speed, sun_lon, moon_lon)
|
||
|
||
# 5. Naisargika Bala(天然力量)
|
||
naisargika = NAISARGIKA_BALA.get(pname, 30.0)
|
||
|
||
# 6. Drik Bala(相位力量)
|
||
drik = calc_drik_bala(pname, sign, house, planets)
|
||
|
||
# 总分(Virupas → Rupas)
|
||
total_virupas = sthana['total'] + dig + kala['total'] + chesta + naisargika + drik
|
||
total_rupas = total_virupas / VIRUPAS_PER_RUPA
|
||
|
||
min_req = MIN_REQUIRED.get(pname, 5.0)
|
||
ishta_bala = (total_rupas / min_req * 100) if min_req > 0 else 0
|
||
|
||
if ishta_bala >= 150:
|
||
strength_level = "极强"
|
||
elif ishta_bala >= 125:
|
||
strength_level = "强"
|
||
elif ishta_bala >= 100:
|
||
strength_level = "充足"
|
||
elif ishta_bala >= 75:
|
||
strength_level = "略弱"
|
||
elif ishta_bala >= 50:
|
||
strength_level = "弱"
|
||
else:
|
||
strength_level = "极弱"
|
||
|
||
results[pname] = {
|
||
'sthana_bala': sthana,
|
||
'dig_bala': round(dig, 2),
|
||
'kala_bala': kala,
|
||
'chesta_bala': round(chesta, 2),
|
||
'naisargika_bala': round(naisargika, 2),
|
||
'drik_bala': round(drik, 2),
|
||
'total_virupas': round(total_virupas, 2),
|
||
'total_rupas': round(total_rupas, 4),
|
||
'min_required': min_req,
|
||
'ishta_bala_pct': round(ishta_bala, 1),
|
||
'strength_level': strength_level,
|
||
# v6.1.10: Ishta/Kashta Phala
|
||
'ishta_phala': round(math.sqrt(max(0, sthana['ucha_bala'] * chesta)), 2),
|
||
'kashta_phala': round(math.sqrt(max(0, (60 - sthana['ucha_bala']) * (60 - chesta))), 2),
|
||
}
|
||
|
||
# 排名
|
||
ranked = sorted(results.items(), key=lambda x: x[1]['total_rupas'], reverse=True)
|
||
for i, (name, _) in enumerate(ranked):
|
||
results[name]['rank'] = i + 1
|
||
|
||
return {
|
||
'method': 'Shadbala六重力量(v6.1.10修复:Nathonnata比例计算+Chesta Sun速度+Ishta/Kashta Phala)',
|
||
'is_night_birth': is_night,
|
||
'sun_uttarayana': sun_northern,
|
||
'planets': results,
|
||
'ranking': [name for name, _ in ranked],
|
||
'strongest': ranked[0][0] if ranked else None,
|
||
'weakest': ranked[-1][0] if ranked else None,
|
||
}
|
||
|
||
|
||
def _dignity_score(pname: str, sign: str) -> float:
|
||
"""计算单层Varga中的尊严分数(BPHS标准)
|
||
Own Sign=45, Exalted=50 (不超过45 unless specifically exalted degrees),
|
||
Great Friend=40, Friend=35, Neutral=25, Enemy=15, Great Enemy=5
|
||
"""
|
||
lord = SIGN_LORDS.get(sign, '')
|
||
if lord == pname:
|
||
# 检查是否在Moolatrikona度数范围内(作为Own Sign的增强)
|
||
return 45.0
|
||
|
||
# 获取行星对该sign lord的关系
|
||
rel = FRIENDSHIP.get(pname, {})
|
||
friends_of_pname = rel.get('friend', [])
|
||
enemies_of_pname = rel.get('enemy', [])
|
||
neutrals_of_pname = rel.get('neutral', [])
|
||
|
||
# 反向关系:sign lord对pname的态度
|
||
lord_rel = FRIENDSHIP.get(lord, {})
|
||
lord_friends = lord_rel.get('friend', [])
|
||
lord_enemies = lord_rel.get('enemy', [])
|
||
|
||
# 双向关系评估(BPHS复合关系)
|
||
# pname likes lord AND lord likes pname → Great Friend = 40
|
||
# pname likes lord OR lord likes pname → Friend = 35
|
||
# one neutral one friend → Neutral-Friend = 30
|
||
# both neutral → Neutral = 25
|
||
# one enemy one neutral → Enemy = 15
|
||
# both enemy → Great Enemy = 5
|
||
pname_likes_lord = lord in friends_of_pname
|
||
lord_likes_pname = pname in lord_friends
|
||
pname_dislikes_lord = lord in enemies_of_pname
|
||
lord_dislikes_pname = pname in lord_enemies
|
||
|
||
if pname_likes_lord and lord_likes_pname:
|
||
return 40.0 # Adhi mitra (Great Friend)
|
||
elif pname_likes_lord or lord_likes_pname:
|
||
if pname_dislikes_lord or lord_dislikes_pname:
|
||
return 25.0 # Mixed → Neutral
|
||
return 35.0 # Mitra (Friend)
|
||
elif pname_dislikes_lord and lord_dislikes_pname:
|
||
return 5.0 # Adhi satru (Great Enemy)
|
||
elif pname_dislikes_lord or lord_dislikes_pname:
|
||
return 15.0 # Satru (Enemy)
|
||
else:
|
||
return 25.0 # Sama (Neutral)
|
||
|
||
|
||
def calc_sthana_bala(pname: str, lon: float, sign: str, house: int) -> Dict:
|
||
"""Sthana Bala(位置力量)
|
||
v4.5.0: 修复Saptavargaja Bala为完整7层计算
|
||
"""
|
||
# A. Ucha Bala(入庙力量)max 60 Virupas
|
||
debilit_deg = DEBILITATION_DEG.get(pname, 0)
|
||
offset = (lon - debilit_deg + 360) % 360
|
||
if offset > 180:
|
||
offset = 360 - offset
|
||
ucha_bala = offset / 180 * 60 # 0-60 Virupas
|
||
|
||
# B. Saptavargaja Bala(七分盘力量)max ~315 Virupas (7 × 45)
|
||
# v4.5.0: 完整7层Varga计算(D1/D2/D3/D4/D7/D9/D12)
|
||
# 每层评估行星在该分盘中的尊严状态,满分45
|
||
sign_idx = SIGNS.index(sign) if sign in SIGNS else 0
|
||
deg_in_sign = lon % 30
|
||
|
||
# D1 (Rashi) — 直接用当前sign
|
||
d1_score = _dignity_score(pname, sign)
|
||
# 检查入庙/落陷覆盖
|
||
exalt_sign = SIGNS[int(EXALTATION_DEG.get(pname, 0) / 30) % 12]
|
||
debilit_sign = SIGNS[int(DEBILITATION_DEG.get(pname, 0) / 30) % 12]
|
||
if sign == exalt_sign:
|
||
d1_score = 50.0 # Exalted 高于 Own Sign(BPHS 标准)
|
||
elif sign == debilit_sign:
|
||
d1_score = 5.0 # Debilitated
|
||
|
||
# D2 (Hora) — 奇数度Leo/Sun rule, 偶数度Cancer/Moon rule
|
||
if deg_in_sign < 15:
|
||
hora_lord = 'Sun'
|
||
else:
|
||
hora_lord = 'Moon'
|
||
# Simplified: evaluate relationship to hora lord
|
||
if hora_lord in FRIENDSHIP.get(pname, {}).get('friend', []):
|
||
d2_score = 35.0
|
||
elif hora_lord in FRIENDSHIP.get(pname, {}).get('enemy', []):
|
||
d2_score = 15.0
|
||
else:
|
||
d2_score = 25.0
|
||
if pname == hora_lord:
|
||
d2_score = 45.0
|
||
|
||
# D3 (Drekkana) — 0-10°: Aries group, 10-20°: Taurus group, 20-30°: Gemini group
|
||
drekkana_idx = int(deg_in_sign / 10) # 0, 1, 2
|
||
drekkana_lord_idx = drekkana_idx # Aries=0/Mars, Taurus=1/Venus, Gemini=2/Mercury
|
||
drekkana_lords = ['Mars', 'Venus', 'Mercury']
|
||
d3_lord = drekkana_lords[drekkana_idx]
|
||
if pname == d3_lord:
|
||
d3_score = 45.0
|
||
elif d3_lord in FRIENDSHIP.get(pname, {}).get('friend', []):
|
||
d3_score = 35.0
|
||
elif d3_lord in FRIENDSHIP.get(pname, {}).get('enemy', []):
|
||
d3_score = 15.0
|
||
else:
|
||
d3_score = 25.0
|
||
|
||
# D4 (Navamsa of Turyamsa) — simplified using navamsa calculation
|
||
try:
|
||
navamsa_part = int(deg_in_sign / (30 / 9))
|
||
el_starts = [0, 9, 6, 3] # Fire=0, Earth=9, Air=6, Water=3
|
||
d4_sign_idx = (el_starts[sign_idx % 4] + navamsa_part) % 12
|
||
d4_sign = SIGNS[d4_sign_idx]
|
||
d4_score = _dignity_score(pname, d4_sign)
|
||
except:
|
||
d4_score = 25.0
|
||
|
||
# D7 (Saptamsa) — 7 parts per sign
|
||
try:
|
||
saptamsa_part = int(deg_in_sign / (30 / 7))
|
||
# D7 sign calculation: for odd signs count forward, even signs backward
|
||
if sign_idx % 2 == 0: # odd sign (Aries=0)
|
||
d7_sign_idx = (sign_idx + saptamsa_part) % 12
|
||
else: # even sign
|
||
d7_sign_idx = (sign_idx - saptamsa_part) % 12
|
||
d7_sign = SIGNS[d7_sign_idx]
|
||
d7_score = _dignity_score(pname, d7_sign)
|
||
except:
|
||
d7_score = 25.0
|
||
|
||
# D9 (Navamsa) — reuse D4 calculation (same navamsa)
|
||
try:
|
||
d9_part = int(deg_in_sign / (30 / 9))
|
||
d9_sign_idx = (el_starts[sign_idx % 4] + d9_part) % 12
|
||
d9_sign = SIGNS[d9_sign_idx]
|
||
d9_score = _dignity_score(pname, d9_sign)
|
||
# Check exaltation/debilitation in D9
|
||
if d9_sign == exalt_sign:
|
||
d9_score = max(d9_score, 45.0)
|
||
elif d9_sign == debilit_sign:
|
||
d9_score = 5.0
|
||
except:
|
||
d9_score = 25.0
|
||
|
||
# D12 (Dwadashamsa) — 12 parts, each 2.5°
|
||
try:
|
||
dwad_part = int(deg_in_sign / 2.5)
|
||
# D12: start from the sign itself, count forward
|
||
d12_sign_idx = (sign_idx + dwad_part) % 12
|
||
d12_sign = SIGNS[d12_sign_idx]
|
||
d12_score = _dignity_score(pname, d12_sign)
|
||
except:
|
||
d12_score = 25.0
|
||
|
||
sapta_score = d1_score + d2_score + d3_score + d4_score + d7_score + d9_score + d12_score
|
||
|
||
# C. Ojayugma Bala(奇偶宫力量)max 15 Virupas
|
||
# v4.5.0: 使用D9宫位精确计算
|
||
try:
|
||
d9_house = ((d9_sign_idx - sign_idx) % 12) + 1 # approximate from Lagna
|
||
if pname in ['Mercury', 'Venus']:
|
||
ojayugma = 15 if d9_house % 2 == 1 else 0 # 奇数宫
|
||
else:
|
||
ojayugma = 15 if d9_house % 2 == 0 else 0 # 偶数宫
|
||
except:
|
||
ojayugma = 0
|
||
|
||
# D. Kendra Bala(角宫力量)max 15 Virupas
|
||
kendra_bala = 15 if house in (1, 4, 7, 10) else 0
|
||
|
||
# E. Drekkana Bala(三分盘力量)max 15 Virupas
|
||
if pname in ['Sun', 'Mars', 'Jupiter']:
|
||
drekkana_bala = 15 if deg_in_sign < 10 else 0
|
||
elif pname in ['Moon', 'Venus']:
|
||
drekkana_bala = 15 if 10 <= deg_in_sign < 20 else 0
|
||
else: # Saturn, Mercury
|
||
drekkana_bala = 15 if deg_in_sign >= 20 else 0
|
||
|
||
total = ucha_bala + sapta_score + ojayugma + kendra_bala + drekkana_bala
|
||
|
||
return {
|
||
'ucha_bala': round(ucha_bala, 2),
|
||
'sapta_d1': round(d1_score, 2),
|
||
'sapta_d2': round(d2_score, 2),
|
||
'sapta_d3': round(d3_score, 2),
|
||
'sapta_d4': round(d4_score, 2),
|
||
'sapta_d7': round(d7_score, 2),
|
||
'sapta_d9': round(d9_score, 2),
|
||
'sapta_d12': round(d12_score, 2),
|
||
'sapta_score': round(sapta_score, 2),
|
||
'ojayugma_bala': ojayugma,
|
||
'kendra_bala': kendra_bala,
|
||
'drekkana_bala': drekkana_bala,
|
||
'total': round(total, 2),
|
||
}
|
||
|
||
|
||
def calc_dig_bala(pname: str, house: int) -> float:
|
||
"""Dig Bala(方向力量),max 60 Virupas"""
|
||
best_house = DIG_BALA_HOUSE.get(pname, 1)
|
||
# 线性插值:最强宫位=60,对宫=0
|
||
diff = abs(house - best_house)
|
||
if diff > 6:
|
||
diff = 12 - diff
|
||
return max(0, (6 - diff) * 10)
|
||
|
||
|
||
def calc_kala_bala(pname: str, is_night: bool, sun_northern: bool,
|
||
sun_lon: float, moon_lon: float, birth_hour: float = 12.0,
|
||
birth_minute: float = 0.0) -> Dict:
|
||
"""Kala Bala(时间力量)
|
||
|
||
v6.1.10: Nathonnata升级为BPHS比例计算(渐变0-60非二值)
|
||
v6.1.10: 添加Abda/Masa/Dina/Hora子项
|
||
"""
|
||
components = {}
|
||
|
||
# A. Nathonnata Bala(昼夜力量)max 60 Virupas
|
||
# BPHS第9章:基于出生时刻距离正午/午夜的时间比例计算
|
||
# 日照性行星(Sun,Jupiter,Venus)= 按出生时间到正午距离的比例
|
||
# 夜行性行星(Moon,Mars,Saturn)= 按出生时间到午夜距离的比例
|
||
# 水星永远获得60(不分昼夜)
|
||
# 2026-06-10修复:从二值(0/60)升级为BPHS渐变比例(0-60)
|
||
if pname == 'Mercury':
|
||
nathonnata = 60.0
|
||
else:
|
||
birth_decimal = birth_hour + birth_minute / 60.0
|
||
if pname in DIURNAL_STRONG:
|
||
# 正午(12:00)距离 → 0小时=60, 6小时=0
|
||
noon_dist = abs(birth_decimal - 12.0)
|
||
noon_dist = min(noon_dist, 24.0 - noon_dist)
|
||
nathonnata = max(0.0, (6.0 - noon_dist) / 6.0 * 60.0)
|
||
else:
|
||
# 午夜(0:00)距离 → 0小时=60, 6小时=0
|
||
midnight_dist = abs(birth_decimal - 0.0)
|
||
midnight_dist = min(midnight_dist, 24.0 - midnight_dist)
|
||
nathonnata = max(0.0, (6.0 - midnight_dist) / 6.0 * 60.0)
|
||
components['nathonnata'] = round(nathonnata, 2)
|
||
|
||
# B. Paksha Bala(月相力量,max 30 Virupas)
|
||
moon_sun_diff = (moon_lon - sun_lon + 360) % 360
|
||
# 归一化到 0-180(月相亮度是对称的)
|
||
phase_angle = moon_sun_diff if moon_sun_diff <= 180 else 360 - moon_sun_diff
|
||
if pname in ['Jupiter', 'Venus', 'Moon']:
|
||
# 望月(phase_angle=180)最强 = 30,朔月(0)= 0
|
||
paksha = phase_angle / 180 * 30
|
||
else:
|
||
# 朔月(phase_angle=0)最强 = 30,望月(180)= 0
|
||
paksha = (180 - phase_angle) / 180 * 30
|
||
components['paksha'] = round(paksha, 2)
|
||
|
||
# C. Tribhaga Bala(三段力量)
|
||
if pname == 'Jupiter':
|
||
tribhaga = 45
|
||
elif pname == 'Venus':
|
||
tribhaga = 45
|
||
elif pname == 'Saturn':
|
||
tribhaga = 45
|
||
else:
|
||
tribhaga = 0
|
||
components['tribhaga'] = tribhaga
|
||
|
||
# D. Ayana Bala(太阳南北行)
|
||
if pname == 'Mercury':
|
||
ayana = 30
|
||
elif sun_northern and pname in ['Sun', 'Mars', 'Moon']:
|
||
ayana = 30
|
||
elif not sun_northern and pname in ['Jupiter', 'Venus', 'Saturn']:
|
||
ayana = 30
|
||
else:
|
||
ayana = 15
|
||
components['ayana'] = ayana
|
||
|
||
total = sum(components.values())
|
||
return {k: v for k, v in components.items()} | {'total': round(total, 2)}
|
||
|
||
|
||
def calc_chesta_bala(pname: str, retro: bool, speed: float,
|
||
sun_lon: float, moon_lon: float) -> float:
|
||
"""Chesta Bala(运动力量),max 60 Virupas
|
||
|
||
v6.1.10修复:Sun不再固定60,改为基于太阳实际速度计算
|
||
BPHS: Sun=60 Chesta仅在太阳以最大速度运行时(春分附近),
|
||
以最小速度运行时(远日点附近)Chesta较低
|
||
"""
|
||
if pname == 'Sun':
|
||
# BPHS: Sun's Chesta = 基于日行度(太阳的实际视速度)
|
||
# 简化:用speed参数,速度越高Chesta越低
|
||
# 标准速度约1.0°/天(慢)→ Chesta=45, 约1.02°/天(快)→ Chesta=15
|
||
abs_speed = abs(speed)
|
||
if abs_speed >= 1.02:
|
||
return 15.0 # 快速(近地点附近)
|
||
elif abs_speed >= 1.015:
|
||
return 25.0
|
||
elif abs_speed >= 1.01:
|
||
return 35.0
|
||
elif abs_speed >= 1.005:
|
||
return 45.0
|
||
else:
|
||
return 55.0 # 最慢(远日点附近,最佳状态)
|
||
|
||
if pname == 'Moon':
|
||
# 月亮根据月相:望月=60,朔月=0
|
||
# BPHS: Chesta Bala 与月相亮面比例成正比
|
||
# diff 取 0-180 范围(>180 时用 360-diff,因为月相是对称的)
|
||
moon_sun_diff = (moon_lon - sun_lon + 360) % 360
|
||
if moon_sun_diff > 180:
|
||
moon_sun_diff = 360 - moon_sun_diff
|
||
return moon_sun_diff / 180 * 60
|
||
|
||
# 其他行星
|
||
if retro:
|
||
return 60.0
|
||
|
||
# 速度判断(简化:用speed的绝对值)
|
||
abs_speed = abs(speed)
|
||
if abs_speed > 1.0: # 快速直行
|
||
return 50.0
|
||
elif abs_speed > 0.5:
|
||
return 35.0
|
||
elif abs_speed > 0.1:
|
||
return 20.0
|
||
else:
|
||
return 10.0 # 接近驻留
|
||
|
||
|
||
def calc_drik_bala(pname: str, sign: str, house: int,
|
||
all_planets: Dict) -> float:
|
||
"""Drik Bala(相位力量),可正可负"""
|
||
drik = 0.0
|
||
p_sign_idx = SIGNS.index(sign) if sign in SIGNS else 0
|
||
|
||
for other_name, other_data in all_planets.items():
|
||
if other_name == pname or other_name == 'Rahu' or other_name == 'Ketu':
|
||
continue
|
||
|
||
other_sign = other_data.get('sign', '')
|
||
if other_sign not in SIGNS:
|
||
continue
|
||
other_sign_idx = SIGNS.index(other_sign)
|
||
|
||
# 计算从other到pname的宫位差
|
||
house_diff = (p_sign_idx - other_sign_idx) % 12 + 1
|
||
|
||
# 检查是否形成相位
|
||
has_aspect = False
|
||
if house_diff == 7 or house_diff == 1: # 7宫相位或合相
|
||
has_aspect = True
|
||
if other_name in SPECIAL_ASPECTS:
|
||
if house_diff in SPECIAL_ASPECTS[other_name]:
|
||
has_aspect = True
|
||
|
||
if has_aspect:
|
||
# 判断吉凶
|
||
aspect_value = 15.0
|
||
if house_diff == 1: # 合相加倍
|
||
aspect_value = 30.0
|
||
|
||
if other_name in BENEFICS:
|
||
drik += aspect_value
|
||
elif other_name in MALEFICS:
|
||
drik -= aspect_value
|
||
else:
|
||
drik += aspect_value * 0.5 # 中性行星
|
||
|
||
# 限制范围
|
||
return max(-60.0, min(60.0, drik))
|