1544 lines
60 KiB
Python
1544 lines
60 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Tajika/Varshaphala年运盘模块 v1.0
|
||
太阳返照盘分析系统
|
||
|
||
支持:
|
||
- Muntha(年度上升点移动)
|
||
- Varshaphala计算(太阳回到出生位置时的星盘)
|
||
- Mudda Dasha(年度大运)
|
||
- Tajika Yoga(年度特殊格局)
|
||
- Tri-Pataka(三旗系统)
|
||
-年度Lord(Year Lord)
|
||
"""
|
||
from typing import Dict, List, Optional
|
||
from datetime import datetime
|
||
import math
|
||
import json
|
||
import os
|
||
|
||
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'}
|
||
CLASSICAL_PLANETS = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn']
|
||
BENEFICS = {'Moon', 'Mercury', 'Jupiter', 'Venus'}
|
||
MALEFICS = {'Sun', 'Mars', 'Saturn'}
|
||
EXALT_SIGN = {'Sun': 0, 'Moon': 1, 'Mars': 9, 'Mercury': 5, 'Jupiter': 3, 'Venus': 11, 'Saturn': 6}
|
||
DEBIL_SIGN = {'Sun': 6, 'Moon': 7, 'Mars': 3, 'Mercury': 11, 'Jupiter': 9, 'Venus': 5, 'Saturn': 0}
|
||
OWN_SIGNS = {'Sun': [4], 'Moon': [3], 'Mars': [0, 7], 'Mercury': [2, 5],
|
||
'Jupiter': [8, 11], 'Venus': [1, 6], 'Saturn': [9, 10]}
|
||
|
||
SAHAM_RULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'references', 'saham_rules.json')
|
||
|
||
|
||
def _load_saham_rules() -> Dict:
|
||
with open(SAHAM_RULES_PATH, 'r', encoding='utf-8') as handle:
|
||
return json.load(handle)
|
||
|
||
|
||
_SAHAM_RULES = _load_saham_rules()
|
||
|
||
|
||
def _resolve_saham_operand(operand: str, planet_lons: Dict[str, float], asc_lon: float, computed: Dict[str, float]) -> float:
|
||
if operand == 'Ascendant':
|
||
return float(asc_lon) % 360
|
||
if operand in computed:
|
||
return float(computed[operand]) % 360
|
||
if operand in planet_lons:
|
||
return float(planet_lons[operand]) % 360
|
||
raise KeyError(f"Unsupported saham operand: {operand}")
|
||
|
||
|
||
def _calc_formula_saham(
|
||
rule_name: str,
|
||
planet_lons: Dict[str, float],
|
||
asc_lon: float,
|
||
is_day: bool,
|
||
computed: Dict[str, float],
|
||
) -> float:
|
||
rule = _SAHAM_RULES['sahams'][rule_name]
|
||
formula = rule['formula_day'] if is_day else rule['formula_night']
|
||
first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed)
|
||
second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed)
|
||
third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed)
|
||
result = (third + (first - second)) % 360
|
||
# references/saham_rules.json: add one sign when Ascendant is not on the
|
||
# forward zodiacal arc from the first formula point to the second.
|
||
ascendant_between_points = (
|
||
first <= asc_lon <= second
|
||
if first <= second
|
||
else asc_lon >= first or asc_lon <= second
|
||
)
|
||
return result if ascendant_between_points else (result + 30.0) % 360
|
||
|
||
|
||
def calc_muntha(birth_asc_idx: int, age: int) -> Dict:
|
||
"""
|
||
Muntha计算:年度上升点
|
||
从出生上升星座开始,每年前进一个星座
|
||
|
||
参数:
|
||
birth_asc_idx: 出生上升星座索引(0=Aries)
|
||
age: 当前年龄(整数)
|
||
"""
|
||
muntha_idx = (birth_asc_idx + age) % 12
|
||
muntha_sign = SIGNS[muntha_idx]
|
||
lord = SIGN_LORDS[muntha_sign]
|
||
|
||
# Muntha落在的宫位(从本命盘上升算起)
|
||
# 需要本命盘上升来计算
|
||
|
||
return {
|
||
'muntha_sign': muntha_sign,
|
||
'muntha_sign_idx': muntha_idx,
|
||
'muntha_lord': lord,
|
||
'age': age,
|
||
'interpretation': _muntha_interp(muntha_sign, lord),
|
||
}
|
||
|
||
|
||
def calc_year_lord(birth_asc_idx: int, age: int) -> Dict:
|
||
"""
|
||
Year Lord(年度守护星)
|
||
基于Muntha位置确定该年的守护行星
|
||
|
||
规则:
|
||
- Muntha落在某星座→该星座守护星为Year Lord
|
||
- Muntha的2/5/9/11宫的守护星为辅助
|
||
"""
|
||
muntha_idx = (birth_asc_idx + age) % 12
|
||
muntha_sign = SIGNS[muntha_idx]
|
||
year_lord = SIGN_LORDS[muntha_sign]
|
||
|
||
# 辅助星(2/5/9/11宫主)
|
||
aux_houses = [2, 5, 9, 11]
|
||
aux_lords = []
|
||
for h in aux_houses:
|
||
h_sign_idx = (muntha_idx + h - 1) % 12
|
||
h_sign = SIGNS[h_sign_idx]
|
||
aux_lords.append({'house': h, 'sign': h_sign, 'lord': SIGN_LORDS[h_sign]})
|
||
|
||
return {
|
||
'age': age,
|
||
'year_lord': year_lord,
|
||
'muntha_sign': muntha_sign,
|
||
'auxiliary_lords': aux_lords,
|
||
'year_theme': _year_theme(year_lord),
|
||
}
|
||
|
||
|
||
def calc_varshaphala(birth_datetime: datetime,
|
||
target_year: int,
|
||
birth_lon: float, birth_lat: float,
|
||
birth_tz: float) -> Dict:
|
||
"""
|
||
Varshaphala(太阳返照盘)计算
|
||
|
||
原理: 太阳回到出生时精确位置的时刻,重新起盘
|
||
这个新的上升星座和行星配置代表该年的运势
|
||
|
||
参数:
|
||
birth_datetime: 出生时间
|
||
target_year: 目标年份
|
||
birth_lon/lat: 出生经纬度
|
||
birth_tz: 出生时区
|
||
"""
|
||
# 简化版:用Muntha + Year Lord + 基本Tajika规则
|
||
# 完整版需要Swiss Ephemeris计算太阳精确返照时间
|
||
|
||
# 出生上升(需要传入,这里用简化版)
|
||
age = target_year - birth_datetime.year
|
||
|
||
return {
|
||
'target_year': target_year,
|
||
'age': age,
|
||
'note': 'Varshaphala完整计算需要Swiss Ephemeris精确返照时间',
|
||
'components': {
|
||
'muntha': calc_muntha(0, age), # 需要实际出生上升
|
||
'year_lord': calc_year_lord(0, age),
|
||
}
|
||
}
|
||
|
||
|
||
def calc_mudda_dasha(asc_sign_idx: int,
|
||
varsha_lord: str,
|
||
birth_month: int) -> Dict:
|
||
"""
|
||
Mudda Dasha(年度大运)
|
||
基于Varshaphala上升的12个月大运系统
|
||
|
||
规则: 从年度守护星开始,按Vimshottari顺序排列
|
||
每个大运按比例分配12个月
|
||
"""
|
||
DASHA_ORDER = ["Ketu","Venus","Sun","Moon","Mars","Rahu","Jupiter","Saturn","Mercury"]
|
||
DASHA_YEARS = {"Ketu":7,"Venus":20,"Sun":6,"Moon":10,"Mars":7,"Rahu":18,"Jupiter":16,"Saturn":19,"Mercury":17}
|
||
|
||
start_idx = DASHA_ORDER.index(varsha_lord) if varsha_lord in DASHA_ORDER else 0
|
||
|
||
sequence = []
|
||
remaining_months = 12.0
|
||
|
||
for i in range(9):
|
||
lord = DASHA_ORDER[(start_idx + i) % 9]
|
||
years = DASHA_YEARS[lord]
|
||
months = years * 12.0 / 120.0 # 按比例分配
|
||
|
||
if months > remaining_months:
|
||
months = remaining_months
|
||
remaining_months -= months
|
||
|
||
sequence.append({
|
||
'lord': lord,
|
||
'months': round(months, 2),
|
||
'order': i + 1,
|
||
})
|
||
|
||
if remaining_months <= 0:
|
||
break
|
||
|
||
return {
|
||
'varsha_lord': varsha_lord,
|
||
'dasha_sequence': sequence,
|
||
'total_months': 12,
|
||
}
|
||
|
||
|
||
def calc_tri_pataka(planet_lons: Dict[str, float],
|
||
varsha_lord: str,
|
||
muntha_sign_idx: int) -> Dict:
|
||
"""
|
||
Tri-Pataka(三旗系统)
|
||
Tajika占星中判断年度吉凶的重要技法
|
||
|
||
三旗:
|
||
1. Dasha Lord(大运守护星)的强度
|
||
2. Muntha Lord(Muntha守护星)的强度
|
||
3. Year Lord(年度守护星)的强度
|
||
|
||
三者都强→大吉年;三者都弱→凶年
|
||
"""
|
||
muntha_lord = SIGN_LORDS.get(SIGNS[muntha_sign_idx], '')
|
||
|
||
# 评估各守护星强度(简化版)
|
||
def _strength(planet, lons):
|
||
if planet not in lons:
|
||
return 'unknown'
|
||
lon = lons[planet]
|
||
si = int(lon / 30) % 12
|
||
# 简化:在角宫(1/4/7/10)=强,三方(5/9)=中,其他=弱
|
||
house_from_asc = ((si - muntha_sign_idx) % 12) + 1
|
||
if house_from_asc in (1, 4, 7, 10): return 'strong'
|
||
if house_from_asc in (5, 9): return 'moderate'
|
||
return 'weak'
|
||
|
||
dl_strength = _strength(varsha_lord, planet_lons)
|
||
ml_strength = _strength(muntha_lord, planet_lons)
|
||
yl_strength = _strength(varsha_lord, planet_lons)
|
||
|
||
strong_count = sum(1 for s in [dl_strength, ml_strength, yl_strength] if s == 'strong')
|
||
weak_count = sum(1 for s in [dl_strength, ml_strength, yl_strength] if s == 'weak')
|
||
|
||
if strong_count >= 2: verdict = 'excellent'
|
||
elif weak_count >= 2: verdict = 'challenging'
|
||
else: verdict = 'mixed'
|
||
|
||
return {
|
||
'dasha_lord': {'planet': varsha_lord, 'strength': dl_strength},
|
||
'muntha_lord': {'planet': muntha_lord, 'strength': ml_strength},
|
||
'year_lord': {'planet': varsha_lord, 'strength': yl_strength},
|
||
'verdict': verdict,
|
||
'interpretation': {
|
||
'excellent': '三旗中两旗以上强旺,年度运势极佳',
|
||
'mixed': '三旗力量参差不齐,年度运势起伏',
|
||
'challenging': '三旗中两旗以上衰弱,年度运势挑战较大',
|
||
}.get(verdict, ''),
|
||
}
|
||
|
||
|
||
def calc_tajika_strength_layers(
|
||
planet_lons: Dict[str, float],
|
||
asc_lon: float = 0.0,
|
||
year_lord: Optional[str] = None,
|
||
) -> Dict:
|
||
"""Block the legacy private-Varga strength proxy pending parity evidence."""
|
||
normalized = {
|
||
planet: float(planet_lons[planet]) % 360
|
||
for planet in CLASSICAL_PLANETS
|
||
if planet in planet_lons and _is_number(planet_lons[planet])
|
||
}
|
||
harsha_bala = {
|
||
planet: _calc_harsha_bala_for_planet(planet, lon, asc_lon, year_lord)
|
||
for planet, lon in normalized.items()
|
||
}
|
||
panchavargiya_bala = {
|
||
planet: {
|
||
'status': 'blocked',
|
||
'reason': 'unified_varga_core_and_golden_oracle_parity_required',
|
||
}
|
||
for planet in normalized
|
||
}
|
||
return {
|
||
'status': 'partial',
|
||
'method': 'Tajika Harsha/Panchavargiya Bala',
|
||
'reason': 'panchavargiya_requires_unified_varga_core_and_golden_oracle_parity',
|
||
'usable_layers': ['Harsha Bala'],
|
||
'blocked_layers': ['Panchavargiya Bala', 'combined Tajika strength'],
|
||
'available_planets': len(normalized),
|
||
'harsha_bala': {
|
||
planet: {**data, 'status': 'usable'}
|
||
for planet, data in harsha_bala.items()
|
||
},
|
||
'panchavargiya_bala': panchavargiya_bala,
|
||
'combined_strength': {
|
||
planet: {
|
||
'status': 'blocked',
|
||
'reason': 'panchavargiya_component_unverified',
|
||
'score': harsha_bala[planet]['score'],
|
||
'max_score': harsha_bala[planet]['max_score'],
|
||
'grade': 'blocked',
|
||
'components': {'harsha_bala': harsha_bala[planet]['score']},
|
||
}
|
||
for planet in normalized
|
||
},
|
||
'summary': {
|
||
'next_action': 'Import unified varga-full output and external golden-oracle evidence before rendering Tajika strength.',
|
||
},
|
||
}
|
||
|
||
# Legacy local Varga proxy retained below only for source history.
|
||
normalized = {
|
||
planet: float(planet_lons[planet]) % 360
|
||
for planet in CLASSICAL_PLANETS
|
||
if planet in planet_lons and _is_number(planet_lons[planet])
|
||
}
|
||
harsha_bala = {}
|
||
panchavargiya_bala = {}
|
||
combined_strength = {}
|
||
|
||
for planet, lon in normalized.items():
|
||
harsha = _calc_harsha_bala_for_planet(planet, lon, asc_lon, year_lord)
|
||
panchavargiya = _calc_panchavargiya_bala_for_planet(planet, lon)
|
||
total = round(harsha['score'] + panchavargiya['score'], 2)
|
||
max_score = harsha['max_score'] + panchavargiya['max_score']
|
||
grade = _strength_grade(total, max_score)
|
||
harsha_bala[planet] = harsha
|
||
panchavargiya_bala[planet] = panchavargiya
|
||
combined_strength[planet] = {
|
||
'score': total,
|
||
'max_score': max_score,
|
||
'grade': grade,
|
||
'components': {
|
||
'harsha_bala': harsha['score'],
|
||
'panchavargiya_bala': panchavargiya['score'],
|
||
},
|
||
'interpretation': _strength_interpretation(planet, grade),
|
||
}
|
||
|
||
ranked = sorted(
|
||
(
|
||
{'planet': planet, **data}
|
||
for planet, data in combined_strength.items()
|
||
),
|
||
key=lambda item: item['score'],
|
||
reverse=True,
|
||
)
|
||
|
||
return {
|
||
'method': 'Tajika Harsha/Panchavargiya Bala',
|
||
'source': '本地 Tajika 强度模型:Harsha Bala + Panchavargiya 五分盘尊贵度代理,用于年度盘证据链与用户端排序。',
|
||
'available_planets': len(normalized),
|
||
'year_lord': year_lord or '',
|
||
'ascendant_longitude': round(float(asc_lon or 0) % 360, 4),
|
||
'harsha_bala': harsha_bala,
|
||
'panchavargiya_bala': panchavargiya_bala,
|
||
'combined_strength': combined_strength,
|
||
'summary': {
|
||
'strongest_planets': ranked[:3],
|
||
'weakest_planets': list(reversed(ranked[-3:])) if ranked else [],
|
||
'headline': _strength_headline(ranked),
|
||
'next_action': '用最强星解释年度可主动推进的主题,用最弱星标注需要 Dasha/Transit 二次确认的风险点。',
|
||
},
|
||
}
|
||
|
||
|
||
def _is_number(value) -> bool:
|
||
try:
|
||
float(value)
|
||
return True
|
||
except (TypeError, ValueError):
|
||
return False
|
||
|
||
|
||
def _sign_idx_from_lon(lon: float) -> int:
|
||
return int((float(lon) % 360) / 30) % 12
|
||
|
||
|
||
def _house_from_asc(lon: float, asc_lon: float) -> int:
|
||
return ((_sign_idx_from_lon(lon) - _sign_idx_from_lon(asc_lon)) % 12) + 1
|
||
|
||
|
||
def _dignity_points(planet: str, sign_idx: int) -> float:
|
||
if EXALT_SIGN.get(planet) == sign_idx:
|
||
return 5.0
|
||
if sign_idx in OWN_SIGNS.get(planet, []):
|
||
return 4.0
|
||
if DEBIL_SIGN.get(planet) == sign_idx:
|
||
return 0.5
|
||
sign_lord = SIGN_LORDS.get(SIGNS[sign_idx])
|
||
if sign_lord == planet:
|
||
return 4.0
|
||
return 2.5
|
||
|
||
|
||
def _strength_grade(score: float, max_score: float) -> str:
|
||
if max_score <= 0:
|
||
return 'unknown'
|
||
ratio = score / max_score
|
||
if ratio >= 0.78:
|
||
return 'excellent'
|
||
if ratio >= 0.62:
|
||
return 'strong'
|
||
if ratio >= 0.42:
|
||
return 'moderate'
|
||
return 'weak'
|
||
|
||
|
||
def _calc_harsha_bala_for_planet(planet: str, lon: float, asc_lon: float, year_lord: Optional[str]) -> Dict:
|
||
house = _house_from_asc(lon, asc_lon)
|
||
sign_idx = _sign_idx_from_lon(lon)
|
||
house_joy = _harsha_house_points(planet, house)
|
||
dignity = _dignity_points(planet, sign_idx)
|
||
year_lord_bonus = 3.0 if year_lord == planet else 0.0
|
||
angular_support = 2.0 if house in (1, 4, 7, 10) else 1.0 if house in (5, 9, 11) else 0.0
|
||
score = round(house_joy + dignity + year_lord_bonus + angular_support, 2)
|
||
max_score = 15.0
|
||
return {
|
||
'score': min(score, max_score),
|
||
'max_score': max_score,
|
||
'grade': _strength_grade(score, max_score),
|
||
'components': {
|
||
'house': house,
|
||
'sign': SIGNS[sign_idx],
|
||
'house_joy': house_joy,
|
||
'dignity': dignity,
|
||
'year_lord_bonus': year_lord_bonus,
|
||
'angular_support': angular_support,
|
||
},
|
||
}
|
||
|
||
|
||
def _harsha_house_points(planet: str, house: int) -> float:
|
||
if planet in MALEFICS:
|
||
if house in (3, 6, 10, 11):
|
||
return 5.0
|
||
if house in (1, 4, 7):
|
||
return 2.5
|
||
return 1.0
|
||
if planet in BENEFICS:
|
||
if house in (1, 2, 4, 5, 7, 9, 10, 11):
|
||
return 5.0
|
||
if house in (3, 6):
|
||
return 2.0
|
||
return 1.0
|
||
return 2.5
|
||
|
||
|
||
def _calc_panchavargiya_bala_for_planet(planet: str, lon: float) -> Dict:
|
||
components = {
|
||
'rasi': _varga_dignity_component(planet, lon, 1),
|
||
'hora': _varga_dignity_component(planet, lon, 2),
|
||
'drekkana': _varga_dignity_component(planet, lon, 3),
|
||
'navamsa': _varga_dignity_component(planet, lon, 9),
|
||
'dwadasamsa': _varga_dignity_component(planet, lon, 12),
|
||
}
|
||
score = round(sum(item['points'] for item in components.values()), 2)
|
||
max_score = 25.0
|
||
return {
|
||
'score': score,
|
||
'max_score': max_score,
|
||
'grade': _strength_grade(score, max_score),
|
||
'components': components,
|
||
}
|
||
|
||
|
||
def _varga_dignity_component(planet: str, lon: float, div: int) -> Dict:
|
||
if div == 1:
|
||
sign_idx = _sign_idx_from_lon(lon)
|
||
else:
|
||
sign_idx = _calc_varga_sign_idx(lon, div)
|
||
points = _dignity_points(planet, sign_idx)
|
||
return {
|
||
'division': f'D{div}',
|
||
'sign': SIGNS[sign_idx],
|
||
'points': points,
|
||
'dignity': _dignity_label(planet, sign_idx),
|
||
}
|
||
|
||
|
||
def _calc_varga_sign_idx(lon: float, div: int) -> int:
|
||
try:
|
||
from varga import calc_varga
|
||
return int(calc_varga(lon, div).get('sign_idx', _fallback_varga_sign_idx(lon, div))) % 12
|
||
except Exception:
|
||
return _fallback_varga_sign_idx(lon, div)
|
||
|
||
|
||
def _fallback_varga_sign_idx(lon: float, div: int) -> int:
|
||
sign_idx = _sign_idx_from_lon(lon)
|
||
degree_in_sign = (float(lon) % 30)
|
||
part_index = int(degree_in_sign / (30.0 / div))
|
||
if div == 2:
|
||
return 4 if part_index == 0 else 3
|
||
if div == 3:
|
||
return (sign_idx + part_index * 4) % 12
|
||
if div == 9:
|
||
start = sign_idx if sign_idx % 3 == 0 else (sign_idx + 4) % 12 if sign_idx % 3 == 1 else (sign_idx + 8) % 12
|
||
return (start + part_index) % 12
|
||
if div == 12:
|
||
return (sign_idx + part_index) % 12
|
||
return sign_idx
|
||
|
||
|
||
def _dignity_label(planet: str, sign_idx: int) -> str:
|
||
if EXALT_SIGN.get(planet) == sign_idx:
|
||
return 'exalted'
|
||
if sign_idx in OWN_SIGNS.get(planet, []):
|
||
return 'own'
|
||
if DEBIL_SIGN.get(planet) == sign_idx:
|
||
return 'debilitated'
|
||
return 'neutral'
|
||
|
||
|
||
def _strength_interpretation(planet: str, grade: str) -> str:
|
||
planet_topics = {
|
||
'Sun': '权威、目标感、父亲/上级',
|
||
'Moon': '情绪、安全感、公众反馈',
|
||
'Mars': '行动、竞争、执行压力',
|
||
'Mercury': '沟通、交易、学习',
|
||
'Jupiter': '机会、导师、财富增长',
|
||
'Venus': '关系、审美、享受资源',
|
||
'Saturn': '责任、结构、长期压力',
|
||
}
|
||
topic = planet_topics.get(planet, planet)
|
||
if grade in ('excellent', 'strong'):
|
||
return f'{planet} 强,年度可主动使用“{topic}”作为推进点。'
|
||
if grade == 'moderate':
|
||
return f'{planet} 中等,“{topic}”需结合 Dasha/Transit 再确认。'
|
||
return f'{planet} 偏弱,“{topic}”宜作为风险提醒和补救重点。'
|
||
|
||
|
||
def _strength_headline(ranked: List[Dict]) -> str:
|
||
if not ranked:
|
||
return '缺少可用行星经度,暂无法生成 Tajika 强度摘要。'
|
||
top = ranked[0]
|
||
bottom = ranked[-1]
|
||
return f"年度最可用行星为 {top['planet']}({top['grade']}),最需复核行星为 {bottom['planet']}({bottom['grade']})。"
|
||
|
||
|
||
def _muntha_interp(sign, lord):
|
||
"""Muntha在12星座的基本解读"""
|
||
interps = {
|
||
'Aries': '年度主题:新开始、冒险、独立行动',
|
||
'Taurus': '年度主题:财务稳定、物质积累、感官享受',
|
||
'Gemini': '年度主题:学习、沟通、多元发展',
|
||
'Cancer': '年度主题:家庭、情感、内在安全感',
|
||
'Leo': '年度主题:创造力、领导力、自我表达',
|
||
'Virgo': '年度主题:健康、服务、细节完善',
|
||
'Libra': '年度主题:关系、合作、美学追求',
|
||
'Scorpio': '年度主题:转化、深层变革、隐藏事物',
|
||
'Sagittarius': '年度主题:远方旅行、哲学、高等教育',
|
||
'Capricorn': '年度主题:事业成就、社会地位、长期规划',
|
||
'Aquarius': '年度主题:社交网络、创新、人道主义',
|
||
'Pisces': '年度主题:灵性成长、隐退、创意灵感',
|
||
}
|
||
return interps.get(sign, '')
|
||
|
||
|
||
def _year_theme(lord):
|
||
"""年度守护星主题"""
|
||
themes = {
|
||
'Sun': '权威、政府、父亲、领导力',
|
||
'Moon': '公众、母亲、情感、直觉',
|
||
'Mars': '行动、竞争、房地产、手术',
|
||
'Mercury': '沟通、商业、学习、旅行',
|
||
'Jupiter': '智慧、子女、宗教、财富',
|
||
'Venus': '爱情、艺术、奢侈品、婚姻',
|
||
'Saturn': '纪律、长寿、建筑、责任',
|
||
}
|
||
return themes.get(lord, '')
|
||
|
||
|
||
# ============================================================================
|
||
# Tajika Yogas(年运盘特殊格局)—— v6.0.13 新增
|
||
# ============================================================================
|
||
|
||
def calc_tajika_yogas(planet_lons: Dict[str, float],
|
||
planet_lats: Optional[Dict[str, float]] = None,
|
||
chart_type: str = 'varsha') -> Dict:
|
||
"""
|
||
计算 Tajika Yogas(年运盘特殊格局)。
|
||
|
||
参数:
|
||
planet_lons: 行星经度字典 {行星名: 经度(0-360)}
|
||
planet_lats: 行星纬度字典(可选,用于部分Yoga)
|
||
chart_type: 'varsha'(年运盘)或 'natal'(本命盘)
|
||
|
||
返回:
|
||
{
|
||
'yogas': [...], # 检测到的Yogas列表
|
||
'ithasala': [...], # Ithasala连接瑜伽
|
||
'easarapha': [...], # Easarapha分离瑜伽
|
||
'nakta': None, # Nakta夜间瑜伽
|
||
'yamaya': [...], # Yamaya双重瑜伽
|
||
'manahoo': [...], # Manahoo特殊组合
|
||
'graha_yuddha': [...], # Graha Yuddha行星战争
|
||
'summary': str, # 总结
|
||
}
|
||
"""
|
||
from tajika_kernel import calculate_tajika_interactions
|
||
if not all(isinstance(value, dict) for value in planet_lons.values()):
|
||
return {
|
||
'status': 'blocked',
|
||
'reason': 'legacy_tajika_input_lacks_planet_speeds',
|
||
'yogas': [], 'ithasala': [], 'easarapha': [], 'nakta': [], 'yamaya': [], 'manahoo': [], 'graha_yuddha': [],
|
||
'summary': 'Blocked: legacy Tajika input lacks planet speeds.',
|
||
'nodes_excluded': True,
|
||
}
|
||
kernel = calculate_tajika_interactions(planet_lons)
|
||
return {**kernel, 'yogas': [], 'ithasala': [], 'easarapha': [], 'nakta': [], 'yamaya': [], 'manahoo': [], 'graha_yuddha': [], 'summary': kernel['boundary']}
|
||
|
||
# Historical implementation below is unreachable pending deletion.
|
||
if planet_lats is None:
|
||
planet_lats = {}
|
||
|
||
Planet_LIST = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu']
|
||
results = {
|
||
'yogas': [],
|
||
'ithasala': [],
|
||
'easarapha': [],
|
||
'nakta': None,
|
||
'yamaya': [],
|
||
'manahoo': [],
|
||
'graha_yuddha': [],
|
||
'summary': '',
|
||
}
|
||
|
||
# ── 1. Ithasala Yoga(连接瑜伽)──
|
||
# 定义:两行星同星座,快行星追慢行星(apply),度数差<=3度
|
||
for i, p1 in enumerate(Planet_LIST):
|
||
if p1 not in planet_lons:
|
||
continue
|
||
for p2 in Planet_LIST[i+1:]:
|
||
if p2 not in planet_lons:
|
||
continue
|
||
lon1, lon2 = planet_lons[p1], planet_lons[p2]
|
||
sign1 = int(lon1 / 30) % 12
|
||
sign2 = int(lon2 / 30) % 12
|
||
if sign1 != sign2:
|
||
continue # 不同星座
|
||
|
||
diff = (lon2 - lon1) % 360
|
||
if diff > 180:
|
||
diff = 360 - diff
|
||
|
||
# 判断谁快谁慢(简化:按行星自然速度)
|
||
speed_order = ['Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Sun']
|
||
# Rahu/Ketu逆行,速度特殊
|
||
fast = p1 if _is_faster(p1, p2) else p2
|
||
slow = p2 if fast == p1 else p1
|
||
|
||
fast_lon = planet_lons[fast]
|
||
slow_lon = planet_lons[slow]
|
||
fast_diff = (fast_lon - slow_lon) % 360
|
||
if fast_diff > 180:
|
||
fast_diff = 360 - fast_diff
|
||
|
||
# Ithasala:快追慢,差<=3度
|
||
if fast_diff <= 3.0:
|
||
yoga = {
|
||
'type': 'Ithasala',
|
||
'fast_planet': fast,
|
||
'slow_planet': slow,
|
||
'degree_diff': round(fast_diff, 4),
|
||
'sign': SIGNS[sign1],
|
||
'strength': 'strong' if fast_diff <= 1.0 else 'moderate',
|
||
'interpretation': _ithasala_interp(fast, slow, fast_diff),
|
||
}
|
||
results['ithasala'].append(yoga)
|
||
results['yogas'].append(yoga)
|
||
|
||
# ── 2. Easarapha Yoga(分离瑜伽)──
|
||
# 定义:两行星同星座,快行星已离开慢行星(separate),度数差<=3度
|
||
for i, p1 in enumerate(Planet_LIST):
|
||
if p1 not in planet_lons:
|
||
continue
|
||
for p2 in Planet_LIST[i+1:]:
|
||
if p2 not in planet_lons:
|
||
continue
|
||
lon1, lon2 = planet_lons[p1], planet_lons[p2]
|
||
sign1 = int(lon1 / 30) % 12
|
||
sign2 = int(lon2 / 30) % 12
|
||
if sign1 != sign2:
|
||
continue
|
||
|
||
fast = p1 if _is_faster(p1, p2) else p2
|
||
slow = p2 if fast == p1 else p1
|
||
|
||
fast_lon = planet_lons[fast]
|
||
slow_lon = planet_lons[slow]
|
||
# 分离:快行星已过慢行星(在慢行星"后面")
|
||
# 即慢行星度数 > 快行星度数(在同一圈内)
|
||
slow_in_sign = slow_lon % 30
|
||
fast_in_sign = fast_lon % 30
|
||
if slow_in_sign > fast_in_sign:
|
||
# 慢在前面,快在后面 → 分离状态
|
||
diff = slow_in_sign - fast_in_sign
|
||
if diff <= 3.0:
|
||
yoga = {
|
||
'type': 'Easarapha',
|
||
'fast_planet': fast,
|
||
'slow_planet': slow,
|
||
'degree_diff': round(diff, 4),
|
||
'sign': SIGNS[sign1],
|
||
'strength': 'strong' if diff <= 1.0 else 'moderate',
|
||
'interpretation': _easarapha_interp(fast, slow, diff),
|
||
}
|
||
results['easarapha'].append(yoga)
|
||
results['yogas'].append(yoga)
|
||
|
||
# ── 3. Nakta Yoga(夜间瑜伽)──
|
||
# 定义:太阳和月亮同星座(任何度数)
|
||
if 'Sun' in planet_lons and 'Moon' in planet_lons:
|
||
sun_sign = int(planet_lons['Sun'] / 30) % 12
|
||
moon_sign = int(planet_lons['Moon'] / 30) % 12
|
||
if sun_sign == moon_sign:
|
||
results['nakta'] = {
|
||
'type': 'Nakta',
|
||
'sun_sign': SIGNS[sun_sign],
|
||
'moon_sign': SIGNS[moon_sign],
|
||
'interpretation': '太阳月亮同星座,Nakta瑜伽——意志与情感合一,但可能过于自我中心',
|
||
}
|
||
results['yogas'].append(results['nakta'])
|
||
|
||
# ── 4. Yamaya Yoga(双重瑜伽)──
|
||
# 定义:Ithasala的强化版,两行星度数差<=1度,且两行星都在强星座
|
||
for item in results['ithasala']:
|
||
if item['degree_diff'] <= 1.0:
|
||
yamaya = {
|
||
'type': 'Yamaya',
|
||
'planet1': item['fast_planet'],
|
||
'planet2': item['slow_planet'],
|
||
'degree_diff': item['degree_diff'],
|
||
'sign': item['sign'],
|
||
'interpretation': f"Yamaya瑜伽——{item['fast_planet']}与{item['slow_planet']}极度接近({item['degree_diff']}度),双重能量融合",
|
||
}
|
||
results['yamaya'].append(yamaya)
|
||
results['yogas'].append(yamaya)
|
||
|
||
# ── 5. Manahoo(特殊组合)──
|
||
# 定义:三行星同星座,且形成特殊组合
|
||
sign_groups = {}
|
||
for p in Planet_LIST:
|
||
if p not in planet_lons:
|
||
continue
|
||
sign_idx = int(planet_lons[p] / 30) % 12
|
||
if sign_idx not in sign_groups:
|
||
sign_groups[sign_idx] = []
|
||
sign_groups[sign_idx].append(p)
|
||
|
||
for sign_idx, planets in sign_groups.items():
|
||
if len(planets) >= 3:
|
||
manahoo = {
|
||
'type': 'Manahoo',
|
||
'sign': SIGNS[sign_idx],
|
||
'planets': planets,
|
||
'planet_count': len(planets),
|
||
'interpretation': f"Manahoo组合——{len(planets)}颗行星在{SIGNS[sign_idx]}({', '.join(planets)}),能量高度集中",
|
||
}
|
||
results['manahoo'].append(manahoo)
|
||
results['yogas'].append(manahoo)
|
||
|
||
# ── 6. Graha Yuddha(行星战争)──
|
||
# 定义:两行星度数差<=1度(极近距离)
|
||
for i, p1 in enumerate(Planet_LIST):
|
||
if p1 not in planet_lons or p1 in ('Rahu', 'Ketu'):
|
||
continue
|
||
for p2 in Planet_LIST[i+1:]:
|
||
if p2 not in planet_lons or p2 in ('Rahu', 'Ketu'):
|
||
continue
|
||
lon1, lon2 = planet_lons[p1], planet_lons[p2]
|
||
diff = abs(lon1 - lon2) % 360
|
||
if diff > 180:
|
||
diff = 360 - diff
|
||
if diff <= 1.0:
|
||
winner = p1 if _is_faster(p1, p2) else p2
|
||
loser = p2 if winner == p1 else p1
|
||
yuddha = {
|
||
'type': 'Graha Yuddha',
|
||
'planet1': p1,
|
||
'planet2': p2,
|
||
'degree_diff': round(diff, 4),
|
||
'winner': winner,
|
||
'loser': loser,
|
||
'sign': SIGNS[int(lon1 / 30) % 12],
|
||
'interpretation': f"Graha Yuddha——{p1}与{p2}极近({diff}度),{winner}胜,{loser}受损",
|
||
}
|
||
results['graha_yuddha'].append(yuddha)
|
||
results['yogas'].append(yuddha)
|
||
|
||
# ── 总结 ──
|
||
total = len(results['yogas'])
|
||
results['summary'] = (
|
||
f"Tajika Yogas检测:共{total}个格局。"
|
||
f"Ithasala(连接){len(results['ithasala'])}个,"
|
||
f"Easarapha(分离){len(results['easarapha'])}个,"
|
||
f"Nakta(日月同度){'1个' if results['nakta'] else '无'},"
|
||
f"Yamaya(双重){len(results['yamaya'])}个,"
|
||
f"Manahoo(三行星集中){len(results['manahoo'])}个,"
|
||
f"Graha Yuddha(行星战争){len(results['graha_yuddha'])}个。"
|
||
)
|
||
|
||
return results
|
||
|
||
|
||
def _is_faster(p1: str, p2: str) -> bool:
|
||
"""判断p1是否比p2快(按自然速度)"""
|
||
speeds = {
|
||
'Moon': 13.176, 'Mercury': 4.092, 'Venus': 1.602,
|
||
'Mars': 0.524, 'Jupiter': 0.083, 'Saturn': 0.034,
|
||
'Sun': 0.986, 'Rahu': -0.053, 'Ketu': -0.053,
|
||
}
|
||
return abs(speeds.get(p1, 0)) > abs(speeds.get(p2, 0))
|
||
|
||
|
||
def _ithasala_interp(fast: str, slow: str, diff: float) -> str:
|
||
"""Ithasala瑜伽解读"""
|
||
templates = [
|
||
f"{fast}与{slow}形成Ithasala(连接瑜伽),两星能量融合,事件将要发生(差{diff}度)",
|
||
f"预示:两星所主管领域将有合作、连接、顺利推进之象",
|
||
]
|
||
return ';'.join(templates)
|
||
|
||
|
||
def _easarapha_interp(fast: str, slow: str, diff: float) -> str:
|
||
"""Easarapha瑜伽解读"""
|
||
templates = [
|
||
f"{fast}与{slow}形成Easarapha(分离瑜伽),两星能量分离,事件已过或受阻(差{diff}度)",
|
||
f"预示:两星所主管领域将有分离、结束、阻碍之象",
|
||
]
|
||
return ';'.join(templates)
|
||
|
||
|
||
# ============================================================================
|
||
# Sahams(特殊点计算)—— v6.0.13 新增
|
||
# ============================================================================
|
||
|
||
def calc_sahams(birth_dt: datetime,
|
||
sun_lon: float, moon_lon: float,
|
||
asc_lon: float,
|
||
yoga_point: float) -> Dict:
|
||
"""
|
||
计算各种Sahams(特殊点)。
|
||
|
||
参数:
|
||
birth_dt: 出生时间(datetime)
|
||
sun_lon: 太阳经度(0-360)
|
||
moon_lon: 月亮经度(0-360)
|
||
asc_lon: 上升经度(0-360)
|
||
yoga_point: Yoga Point经度(0-360)
|
||
|
||
返回:
|
||
{
|
||
'punya_saham': {...}, # 福德点
|
||
'karya_saham': {...}, # 事业点
|
||
'vivah_saham': {...}, # 婚姻点
|
||
'rajya_saham': {...}, # 王权点
|
||
'shatru_saham': {...}, # 敌人点
|
||
'labha_saham': {...}, # 收益点
|
||
'parakrama_saham': {...}, # 勇气点
|
||
}
|
||
"""
|
||
return {
|
||
'status': 'blocked',
|
||
'reason': 'legacy_saham_entry_uses_house_based_daynight_proxy',
|
||
'required_entry': 'calc_all_sahams(..., lat=..., lon=..., tz=...)',
|
||
'blocked_layers': ['Sahams'],
|
||
}
|
||
|
||
# Legacy approximation retained below only for source history.
|
||
results = {}
|
||
|
||
# 通用Saham计算公式(Tajika系统):
|
||
# Saham Lon = Asc Lon + (Planet2 Lon - Planet1 Lon)
|
||
# 如果结果>=360,减去360
|
||
|
||
def _calc_saham(p1_lon: float, p2_lon: float, asc: float) -> float:
|
||
"""计算Saham经度"""
|
||
return (asc + (p2_lon - p1_lon)) % 360
|
||
|
||
# Punya Saham(福德点):Sun - Moon + Asc
|
||
punya_lon = _calc_saham(moon_lon, sun_lon, asc_lon)
|
||
results['punya_saham'] = {
|
||
'longitude': round(punya_lon, 4),
|
||
'sign': SIGNS[int(punya_lon / 30) % 12],
|
||
'degree_in_sign': round(punya_lon % 30, 4),
|
||
'interpretation': 'Punya Saham(福德点)——指示人生福德、善业积累、精神成长领域',
|
||
}
|
||
|
||
# Karya Saham(事业点):Moon - Mars + Asc(日间)或 Mars - Moon + Asc(夜间)
|
||
is_daytime = _is_daytime(birth_dt, sun_lon, asc_lon)
|
||
if is_daytime:
|
||
karya_lon = _calc_saham(mars_lon:=0, moon_lon, asc_lon) # 需要Mars经度
|
||
# 暂时用0占位,实际需要从外部传入
|
||
results['karya_saham'] = {'note': '需要Mars经度,请从外部传入'}
|
||
else:
|
||
results['karya_saham'] = {'note': '夜间盘,需要Mars-Moon,请从外部传入'}
|
||
|
||
# 简化版:直接返回结构,具体计算由调用方完成
|
||
results['_note'] = 'Saham计算需要完整的行星经度,请在jyotish_engine.py中调用calc_all_sahams()'
|
||
|
||
return results
|
||
|
||
|
||
def _is_daytime(birth_dt: datetime, sun_lon: float, asc_lon: float) -> bool:
|
||
"""判断出生时是白天还是夜间(简化:太阳在1-6宫=白天)"""
|
||
sun_house = (int(sun_lon / 30) - int(asc_lon / 30)) % 12 + 1
|
||
return sun_house in (1, 2, 3, 4, 5, 6)
|
||
|
||
|
||
def calc_all_sahams(planet_lons: Dict[str, float],
|
||
asc_lon: float,
|
||
birth_dt: datetime,
|
||
chart_type: str = 'natal',
|
||
lat: float | None = None,
|
||
lon: float | None = None,
|
||
tz: float | None = None) -> Dict:
|
||
"""
|
||
计算所有主要Sahams(完整版)。
|
||
|
||
参数:
|
||
planet_lons: 所有行星经度 {名: 经度}
|
||
asc_lon: 上升经度
|
||
birth_dt: 出生时间
|
||
chart_type: 'natal'(本命)或 'varsha'(年运)
|
||
|
||
返回:
|
||
完整Sahams字典
|
||
"""
|
||
if lat is None or lon is None or tz is None:
|
||
return {
|
||
'status': 'blocked',
|
||
'reason': 'saham_daynight_requires_wgs84_location_and_timezone',
|
||
'boundary': 'No solar-house day/night proxy is permitted in production.',
|
||
}
|
||
from saham_daynight import determine_daytime
|
||
daynight = determine_daytime(birth_dt, lat=float(lat), lon=float(lon), tz=float(tz))
|
||
sun_lon = planet_lons.get('Sun', 0)
|
||
moon_lon = planet_lons.get('Moon', 0)
|
||
mars_lon = planet_lons.get('Mars', 0)
|
||
jupiter_lon = planet_lons.get('Jupiter', 0)
|
||
venus_lon = planet_lons.get('Venus', 0)
|
||
saturn_lon = planet_lons.get('Saturn', 0)
|
||
|
||
def _saham(p1_lon, p2_lon):
|
||
return (asc_lon + (p2_lon - p1_lon)) % 360
|
||
|
||
is_day = daynight['is_daytime']
|
||
|
||
results = {'status': 'partial', 'daynight_evidence': daynight}
|
||
computed_formula_sahams: Dict[str, float] = {}
|
||
|
||
# 1. Punya Saham(福德点):Moon - Sun + Asc
|
||
p_lon = _calc_formula_saham('Punya_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||
computed_formula_sahams['Punya_Saham'] = p_lon
|
||
results['punya_saham'] = _saham_dict(p_lon, 'Punya Saham', '福德点——善业、精神成长、父系福德')
|
||
|
||
# 2. Karya Saham(事业点):
|
||
# 日间:Mars - Moon + Asc;夜间:Moon - Mars + Asc
|
||
if is_day:
|
||
k_lon = _saham(moon_lon, mars_lon)
|
||
else:
|
||
k_lon = _saham(mars_lon, moon_lon)
|
||
results['karya_saham'] = _saham_dict(k_lon, 'Karya Saham', '事业点——行动、努力、工作成果')
|
||
|
||
# 3. Vivah Saham(婚姻点):
|
||
# 日间:Venus - Mars + Asc(男性)或 Mars - Venus + Asc(女性)
|
||
# 简化:用Venus - Mars
|
||
v_lon = _saham(mars_lon, venus_lon)
|
||
results['vivah_saham'] = _saham_dict(v_lon, 'Vivah Saham', '婚姻点——婚姻、伴侣、情感关系')
|
||
|
||
# 4. Rajya Saham(王权点):Sun - Jupiter + Asc
|
||
r_lon = _saham(jupiter_lon, sun_lon)
|
||
results['rajya_saham'] = _saham_dict(r_lon, 'Rajya Saham', '王权点——地位、权威、社会认可')
|
||
|
||
# 5. Shatru Saham(敌人点):Mars - Saturn + Asc
|
||
sh_lon = _saham(saturn_lon, mars_lon)
|
||
results['shatru_saham'] = _saham_dict(sh_lon, 'Shatru Saham', '敌人点——竞争对手、障碍、冲突来源')
|
||
|
||
# 6. Labha Saham(收益点):Jupiter - Moon + Asc
|
||
l_lon = _saham(moon_lon, jupiter_lon)
|
||
results['labha_saham'] = _saham_dict(l_lon, 'Labha Saham', '收益点——收获、增益、扩张')
|
||
|
||
# 7. Parakrama Saham(勇气点):Mars - Sun + Asc
|
||
pa_lon = _saham(sun_lon, mars_lon)
|
||
results['parakrama_saham'] = _saham_dict(pa_lon, 'Parakrama Saham', '勇气点——勇气、竞争力、克服困难')
|
||
|
||
# 8-36. 扩展Sahams(Tajika系统36种)
|
||
# 8. Putra Saham(子女点):Jupiter - Moon + Asc
|
||
putra_lon = _saham(moon_lon, jupiter_lon)
|
||
results['putra_saham'] = _saham_dict(putra_lon, 'Putra Saham', '子女点——子女、后代、创造力')
|
||
|
||
# 9. Jnana Saham(教育点):Jupiter - Mercury + Asc
|
||
mercury_lon = planet_lons.get('Mercury', 0)
|
||
jnana_lon = _saham(mercury_lon, jupiter_lon)
|
||
results['jnana_saham'] = _saham_dict(jnana_lon, 'Jnana Saham', '教育点——学习、知识、智慧')
|
||
|
||
# 10. Raja Saham(王权):Sun - Moon + Asc
|
||
raja_lon = _saham(moon_lon, sun_lon)
|
||
results['raja_saham'] = _saham_dict(raja_lon, 'Raja Saham', '王权点——领导力、权威')
|
||
|
||
# 11. Yasha Saham(名声点):Jupiter - Sun + Asc
|
||
try:
|
||
yasha_lon = _calc_formula_saham('Yashas_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||
computed_formula_sahams['Yashas_Saham'] = yasha_lon
|
||
except KeyError:
|
||
yasha_lon = _saham(sun_lon, jupiter_lon)
|
||
results['yasha_saham'] = _saham_dict(yasha_lon, 'Yasha Saham', '名声点——声誉、社会地位')
|
||
|
||
# 12. Karma Saham(业力点):Saturn - Mars + Asc
|
||
try:
|
||
karma_lon = _calc_formula_saham('Karma_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||
computed_formula_sahams['Karma_Saham'] = karma_lon
|
||
except KeyError:
|
||
karma_lon = _saham(mars_lon, saturn_lon)
|
||
results['karma_saham'] = _saham_dict(karma_lon, 'Karma Saham', '业力点——前世因果、责任')
|
||
|
||
# 13. Bandhu Saham(兄弟点):Mars - Mercury + Asc
|
||
bandhu_lon = _saham(mercury_lon, mars_lon)
|
||
results['bandhu_saham'] = _saham_dict(bandhu_lon, 'Bandhu Saham', '兄弟点——兄弟姐妹、同辈')
|
||
|
||
# 14. Matri Saham(母亲点):Moon - Venus + Asc
|
||
matri_lon = _saham(venus_lon, moon_lon)
|
||
results['matri_saham'] = _saham_dict(matri_lon, 'Matri Saham', '母亲点——母亲、养育、情感滋养')
|
||
|
||
# 15. Pitri Saham(父亲点):Sun - Saturn + Asc
|
||
pitri_lon = _saham(saturn_lon, sun_lon)
|
||
results['pitri_saham'] = _saham_dict(pitri_lon, 'Pitri Saham', '父亲点——父亲、权威、传承')
|
||
|
||
# 16. Janma Saham(出生点):出生时刻敏感点
|
||
j_lon = _saham(sun_lon, asc_lon) # Special: Asc based
|
||
results['janma_saham'] = _saham_dict(j_lon, 'Janma Saham', '出生点——生命起点、先天禀赋')
|
||
|
||
# 17. Mrityu Saham(死亡点):Saturn出生 - 8宫主 + Asc
|
||
m_lon = _saham(mars_lon, saturn_lon) # 近似
|
||
results['mrityu_saham'] = _saham_dict(m_lon, 'Mrityu Saham', '死亡点——寿命、终结、转变')
|
||
|
||
# 18. Rogha Saham(疾病点):Mercury - Saturn + Asc
|
||
rogha_lon = _saham(saturn_lon, mercury_lon)
|
||
results['rogha_saham'] = _saham_dict(rogha_lon, 'Rogha Saham', '疾病点——健康、疾病倾向')
|
||
|
||
# 19. Aarogya Saham(健康点):Jupiter - Saturn + Asc
|
||
aarogya_lon = _saham(saturn_lon, jupiter_lon)
|
||
results['aarogya_saham'] = _saham_dict(aarogya_lon, 'Aarogya Saham', '健康点——康复、旺盛精力')
|
||
|
||
# 20. Bhraatri Saham(同辈点):Mars - Moon + Asc
|
||
bhratr_lon = _saham(moon_lon, mars_lon)
|
||
results['bhraatri_saham'] = _saham_dict(bhratr_lon, 'Bhraatri Saham', '同辈点——手足、社交圈')
|
||
|
||
# 21. Ghataka Saham(冲突点):Rahu - Mars + Asc
|
||
rahu_lon = planet_lons.get('Rahu', 0)
|
||
ghataka_lon = _saham(mars_lon, rahu_lon)
|
||
results['ghataka_saham'] = _saham_dict(ghataka_lon, 'Ghataka Saham', '冲突点——意外、冲击、突袭')
|
||
|
||
# 22. Paradesa Saham(海外点):Saturn - Moon + Asc
|
||
paradesa_lon = _saham(moon_lon, saturn_lon)
|
||
results['paradesa_saham'] = _saham_dict(paradesa_lon, 'Paradesa Saham', '海外点——出国、异域、远方')
|
||
|
||
# 23. Parabharya Saham(配偶点):Venus - Mars + Asc(女性盘)
|
||
parab_lon = _saham(mars_lon, venus_lon)
|
||
results['parabharya_saham'] = _saham_dict(parab_lon, 'Parabharya Saham', '配偶点——伴侣特质')
|
||
|
||
# 24. Dhan Saham(财富点):Jupiter - Venus + Asc
|
||
dhan_lon = _saham(venus_lon, jupiter_lon)
|
||
results['dhan_saham'] = _saham_dict(dhan_lon, 'Dhan Saham', '财富点——金钱、资产、物质')
|
||
|
||
# 25. Maya Saham(幻象点):Rahu - Ketu + Asc
|
||
ketu_lon = planet_lons.get('Ketu', 0)
|
||
maya_lon = _saham(ketu_lon, rahu_lon)
|
||
results['maya_saham'] = _saham_dict(maya_lon, 'Maya Saham', '幻象点——迷惑、欺骗、直觉')
|
||
|
||
# 26. Moksha Saham(解脱点):Ketu - Jupiter + Asc
|
||
moksha_lon = _saham(jupiter_lon, ketu_lon)
|
||
results['moksha_saham'] = _saham_dict(moksha_lon, 'Moksha Saham', '解脱点——灵性、开悟、超越')
|
||
|
||
# 27. Buddha Saham(智慧点):Mercury - Jupiter + Asc
|
||
buddha_lon = _saham(jupiter_lon, mercury_lon)
|
||
results['buddha_saham'] = _saham_dict(buddha_lon, 'Buddha Saham', '智慧点——智力、逻辑、语言')
|
||
|
||
# 28. Shastra Saham(武器点):Mars - Ketu + Asc
|
||
shastra_lon = _saham(ketu_lon, mars_lon)
|
||
results['shastra_saham'] = _saham_dict(shastra_lon, 'Shastra Saham', '武器点——攻击性、防御、技术')
|
||
|
||
# 29. Kala Saham(时间点):Saturn - Sun + Asc
|
||
kala_lon = _saham(sun_lon, saturn_lon)
|
||
results['kala_saham'] = _saham_dict(kala_lon, 'Kala Saham', '时间点——时机、节奏、耐心')
|
||
|
||
# 30. Shakti Saham(力量点):Venus - Moon + Asc
|
||
shakti_lon = _saham(moon_lon, venus_lon)
|
||
results['shakti_saham'] = _saham_dict(shakti_lon, 'Shakti Saham', '力量点——女性力量、魅力、创造力')
|
||
|
||
# 31. Bhrigu Saham(先知点):Venus - Jupiter + Asc
|
||
bhrigu_lon = _saham(jupiter_lon, venus_lon)
|
||
results['bhrigu_saham'] = _saham_dict(bhrigu_lon, 'Bhrigu Saham', '先知点——直觉、预见、智慧传承')
|
||
|
||
# 32. Sundara Saham(美点):Venus - Mercury + Asc
|
||
sundara_lon = _saham(mercury_lon, venus_lon)
|
||
results['sundara_saham'] = _saham_dict(sundara_lon, 'Sundara Saham', '美点——艺术、美感、和谐')
|
||
|
||
# 33. Jnati Saham(亲戚点):Mars - Saturn + Asc
|
||
jnati_lon = _saham(saturn_lon, mars_lon)
|
||
results['jnati_saham'] = _saham_dict(jnati_lon, 'Jnati Saham', '亲戚点——宗族、家族关系')
|
||
|
||
# 34. Artha Saham(财富点):Sun - Venus + Asc
|
||
artha_lon = _saham(venus_lon, sun_lon)
|
||
results['artha_saham'] = _saham_dict(artha_lon, 'Artha Saham', '财富点——物质繁荣、经济')
|
||
|
||
# 35. Dharma Saham(正法点):Jupiter - Saturn + Asc
|
||
dharma_lon = _saham(saturn_lon, jupiter_lon)
|
||
results['dharma_saham'] = _saham_dict(dharma_lon, 'Dharma Saham', '正法点——正义、道德、人生使命')
|
||
|
||
# 36. Sangrama Saham(战斗点):Mars - Rahu + Asc
|
||
sangrama_lon = _saham(rahu_lon, mars_lon)
|
||
results['sangrama_saham'] = _saham_dict(sangrama_lon, 'Sangrama Saham', '战斗点——竞争、挑战、胜利')
|
||
|
||
return results
|
||
|
||
|
||
def _saham_dict(lon: float, name: str, interp: str) -> Dict:
|
||
"""构建Saham字典"""
|
||
return {
|
||
'longitude': round(lon, 4),
|
||
'sign': SIGNS[int(lon / 30) % 12],
|
||
'sign_cn': SIGNS_CN.get(SIGNS[int(lon / 30) % 12], ''),
|
||
'degree_in_sign': round(lon % 30, 4),
|
||
'name': name,
|
||
'interpretation': interp,
|
||
}
|
||
|
||
|
||
# 补充SIGNS_CN(中文星座名)
|
||
SIGNS_CN = {
|
||
'Aries': '白羊', 'Taurus': '金牛', 'Gemini': '双子',
|
||
'Cancer': '巨蟹', 'Leo': '狮子', 'Virgo': '处女',
|
||
'Libra': '天秤', 'Scorpio': '天蝎', 'Sagittarius': '射手',
|
||
'Capricorn': '摩羯', 'Aquarius': '水瓶', 'Pisces': '双鱼',
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# Tajika Yogas 完整检测(v7.0 complete)
|
||
# 基于 BPHS Tajika + PyJHora tajika/yogas.py 算法翻译
|
||
# 10种年度Yoga + 完整Vedha阻碍逻辑 + Tajika相位规则
|
||
# =============================================================================
|
||
|
||
# Tajika相位表(不同于Parashara!Tajika使用西方式7种相位)
|
||
# 行=相位类型,列=度数范围
|
||
TAJIKA_ASPECT_DEGREES = {0, 30, 60, 90, 120, 150, 180}
|
||
|
||
# Tajika 容许度(orb)—— 各行星的标准容许度
|
||
TAJIKA_ORBS = {
|
||
'Sun': 15, 'Moon': 12, 'Mars': 8, 'Mercury': 7,
|
||
'Jupiter': 9, 'Venus': 7, 'Saturn': 9,
|
||
}
|
||
|
||
# Vedha 阻碍点表(Tajika经典)
|
||
# 每对行星间有固定的Vedha敏感度数位置
|
||
# 格式: (planet1_deg, planet2_deg) → 如果第三方行星在这个度数,则形成Vedha
|
||
VEDHA_TABLE = {
|
||
# 从Ithasala点出发的度数偏移
|
||
1: 7, 2: 5, 3: 9, 4: 3, 5: 8, 6: 2, 7: 10, 8: 4, 9: 6, 10: 1,
|
||
11: 9, 12: 3, 13: 7, 14: 5, 15: 2, 16: 8, 17: 4, 18: 6, 19: 1, 20: 10,
|
||
21: 3, 22: 9, 23: 5, 24: 7, 25: 2, 26: 8, 27: 4, 28: 6, 29: 1, 30: 10,
|
||
}
|
||
|
||
|
||
def detect_tajika_yogas(varsha_planets: Dict, year_lord: str = None) -> List[Dict]:
|
||
"""
|
||
检测Tajika Yogas(年度Yoga)—— 完整版 v7.0。
|
||
|
||
10种Yoga分类:
|
||
1. Itasala (Ithasala) — 连接瑜伽(快追慢,orb≤行星容许度)
|
||
2. Ishkavala — 单向相位瑜伽(一星与多星形成Ithasala)
|
||
3. Vasala — 无效相位瑜伽(落陷星形成Ithasala)
|
||
4. Tambira — 阻碍瑜伽(凶星在Ithasala之间)
|
||
5. Kambira — 双重阻碍瑜伽(两凶星同时阻碍)
|
||
6. Dakshina — 右向瑜伽(快星在慢星右侧)
|
||
7. Vama — 左向瑜伽(快星在慢星左侧)
|
||
8. Ubhaya — 双向瑜伽(两对Ithasala互相支持)
|
||
9. Vedha — 穿刺阻碍(第三方在Vedha敏感点)
|
||
10. Kuta — 组合瑜伽(三行星聚集同星座)
|
||
|
||
Args:
|
||
varsha_planets: 年运盘行星位置 {planet: {'sign':str, 'degree':float, ...}}
|
||
或 {planet: longitude_float}
|
||
year_lord: 年度主星
|
||
|
||
Returns:
|
||
检测到的Tajika Yoga列表
|
||
"""
|
||
yogas = []
|
||
SEVEN = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn']
|
||
MALEFICS = {'Saturn', 'Mars', 'Sun', 'Rahu', 'Ketu'}
|
||
BENEFICS = {'Jupiter', 'Venus', 'Mercury', 'Moon'}
|
||
|
||
# 落陷星座表
|
||
DEBILITATION = {
|
||
'Sun': 'Libra', 'Moon': 'Scorpio', 'Mars': 'Cancer',
|
||
'Mercury': 'Pisces', 'Jupiter': 'Capricorn',
|
||
'Venus': 'Virgo', 'Saturn': 'Aries',
|
||
}
|
||
|
||
def _get_longitude(pname):
|
||
pd = varsha_planets.get(pname, {})
|
||
if isinstance(pd, (int, float)):
|
||
return float(pd)
|
||
sign = pd.get('sign', '')
|
||
deg = pd.get('degree', 0) % 30
|
||
if sign in SIGNS:
|
||
return SIGNS.index(sign) * 30 + deg
|
||
return pd.get('longitude', pd.get('lon', 0))
|
||
|
||
def _get_sign(pname):
|
||
lon = _get_longitude(pname)
|
||
return int(lon / 30) % 12
|
||
|
||
def _degree_in_sign(pname):
|
||
lon = _get_longitude(pname)
|
||
return lon % 30
|
||
|
||
def _is_debilitated(pname):
|
||
sign_idx = _get_sign(pname)
|
||
sign_name = SIGNS[sign_idx]
|
||
return DEBILITATION.get(pname) == sign_name
|
||
|
||
def _is_faster(p1, p2):
|
||
speeds = {
|
||
'Moon': 13.176, 'Mercury': 4.092, 'Venus': 1.602,
|
||
'Sun': 0.986, 'Mars': 0.524, 'Jupiter': 0.083, 'Saturn': 0.034,
|
||
}
|
||
return speeds.get(p1, 0) > speeds.get(p2, 0)
|
||
|
||
def _orb_between(p1, p2):
|
||
d = abs(_get_longitude(p1) - _get_longitude(p2))
|
||
return min(d, 360 - d)
|
||
|
||
def _effective_orb(p1, p2):
|
||
"""计算两星间的有效容许度(取较小者)"""
|
||
return min(TAJIKA_ORBS.get(p1, 7), TAJIKA_ORBS.get(p2, 7))
|
||
|
||
# ── 1. Ithasala Yoga(连接瑜伽)完整版 ──
|
||
# 条件:快星追赶慢星(applying),orb ≤ 有效容许度
|
||
ithasala_pairs = []
|
||
checked = set()
|
||
for p1 in SEVEN:
|
||
for p2 in SEVEN:
|
||
if p1 >= p2:
|
||
continue
|
||
if (p1, p2) in checked:
|
||
continue
|
||
checked.add((p1, p2))
|
||
|
||
l1 = _get_longitude(p1)
|
||
l2 = _get_longitude(p2)
|
||
orb = _orb_between(p1, p2)
|
||
eff_orb = _effective_orb(p1, p2)
|
||
|
||
if orb > eff_orb:
|
||
continue
|
||
|
||
fast = p1 if _is_faster(p1, p2) else p2
|
||
slow = p2 if fast == p1 else p1
|
||
fast_lon = _get_longitude(fast)
|
||
slow_lon = _get_longitude(slow)
|
||
|
||
# 判断是否applying(快追慢)
|
||
# 快星度数 < 慢星度数(同一方向)= applying
|
||
applying = (fast_lon % 30) < (slow_lon % 30)
|
||
|
||
if applying or orb <= 3.0: # 3°内视为紧密连接
|
||
ithasala_pairs.append({
|
||
'fast': fast, 'slow': slow, 'orb': orb,
|
||
'fast_lon': fast_lon, 'slow_lon': slow_lon,
|
||
})
|
||
|
||
for pair in ithasala_pairs:
|
||
yogas.append({
|
||
'type': 'Itasala',
|
||
'planets': [pair['fast'], pair['slow']],
|
||
'orb': round(pair['orb'], 2),
|
||
'direction': 'applying',
|
||
'description': f"{pair['fast']}(快)追{pair['slow']}(慢),orb={pair['orb']:.1f}°,形成Itasala连接瑜伽",
|
||
})
|
||
|
||
# ── 2. Ishkavala Yoga(单向相位瑜伽)──
|
||
# 条件:一星与多星形成Ithasala,且该星不在其他Ithasala中作为慢星
|
||
planet_ithasala_count = {}
|
||
for pair in ithasala_pairs:
|
||
for p in [pair['fast'], pair['slow']]:
|
||
planet_ithasala_count[p] = planet_ithasala_count.get(p, 0) + 1
|
||
|
||
for p, count in planet_ithasala_count.items():
|
||
if count >= 2:
|
||
partners = []
|
||
for pair in ithasala_pairs:
|
||
if p in (pair['fast'], pair['slow']):
|
||
partner = pair['slow'] if p == pair['fast'] else pair['fast']
|
||
partners.append(partner)
|
||
yogas.append({
|
||
'type': 'Ishkavala',
|
||
'planets': [p] + partners,
|
||
'description': f'{p}与{", ".join(partners)}形成多个Ithasala,Ishkavala单向相位瑜伽',
|
||
})
|
||
|
||
# ── 3. Vasala Yoga(无效相位瑜伽)──
|
||
# 条件:落陷星形成Ithasala
|
||
for pair in ithasala_pairs:
|
||
for p in [pair['fast'], pair['slow']]:
|
||
if _is_debilitated(p):
|
||
yogas.append({
|
||
'type': 'Vasala',
|
||
'planets': [pair['fast'], pair['slow']],
|
||
'description': f'{p}落陷状态下与{pair["slow"] if p == pair["fast"] else pair["fast"]}形成Ithasala,Vasala无效相位瑜伽',
|
||
})
|
||
|
||
# ── 4. Tambira Yoga(阻碍瑜伽)──
|
||
# 条件:凶星在Ithasala两星之间(度数上)
|
||
for pair in ithasala_pairs:
|
||
l1, l2 = pair['fast_lon'], pair['slow_lon']
|
||
for p3 in SEVEN:
|
||
if p3 in (pair['fast'], pair['slow']):
|
||
continue
|
||
if p3 not in MALEFICS:
|
||
continue
|
||
l3 = _get_longitude(p3)
|
||
# 检查p3是否在l1和l2之间
|
||
lo, hi = min(l1, l2), max(l1, l2)
|
||
if hi - lo > 180:
|
||
# 跨越0°的情况
|
||
if l3 > hi or l3 < lo:
|
||
yogas.append({
|
||
'type': 'Tambira',
|
||
'planets': [pair['fast'], pair['slow'], p3],
|
||
'description': f'凶星{p3}在{pair["fast"]}-{pair["slow"]}之间阻碍,Tambira阻碍瑜伽',
|
||
})
|
||
break
|
||
else:
|
||
if lo < l3 < hi:
|
||
yogas.append({
|
||
'type': 'Tambira',
|
||
'planets': [pair['fast'], pair['slow'], p3],
|
||
'description': f'凶星{p3}在{pair["fast"]}-{pair["slow"]}之间阻碍,Tambira阻碍瑜伽',
|
||
})
|
||
break
|
||
|
||
# ── 5. Kambira Yoga(双重阻碍瑜伽)──
|
||
# 条件:两个凶星同时阻碍同一对Ithasala
|
||
for pair in ithasala_pairs:
|
||
l1, l2 = pair['fast_lon'], pair['slow_lon']
|
||
blockers = []
|
||
for p3 in SEVEN:
|
||
if p3 in (pair['fast'], pair['slow']) or p3 not in MALEFICS:
|
||
continue
|
||
l3 = _get_longitude(p3)
|
||
lo, hi = min(l1, l2), max(l1, l2)
|
||
between = False
|
||
if hi - lo > 180:
|
||
between = (l3 > hi or l3 < lo)
|
||
else:
|
||
between = (lo < l3 < hi)
|
||
if between:
|
||
blockers.append(p3)
|
||
if len(blockers) >= 2:
|
||
yogas.append({
|
||
'type': 'Kambira',
|
||
'planets': [pair['fast'], pair['slow']] + blockers[:2],
|
||
'description': f'双凶星{blockers[0]}和{blockers[1]}同时阻碍{pair["fast"]}-{pair["slow"]},Kambira双重阻碍瑜伽',
|
||
})
|
||
|
||
# ── 6-7. Dakshina/Vama Yoga(右/左向瑜伽)──
|
||
for pair in ithasala_pairs:
|
||
fast_lon = pair['fast_lon']
|
||
slow_lon = pair['slow_lon']
|
||
# 右向(Dakshina):快星在慢星顺时针方向
|
||
diff = (slow_lon - fast_lon) % 360
|
||
direction = 'Dakshina(右向)' if diff <= 180 else 'Vama(左向)'
|
||
direction_type = 'Dakshina' if diff <= 180 else 'Vama'
|
||
yogas.append({
|
||
'type': direction_type,
|
||
'planets': [pair['fast'], pair['slow']],
|
||
'description': f'{pair["fast"]}追{pair["slow"]}方向={direction},{direction_type}方向瑜伽',
|
||
})
|
||
|
||
# ── 8. Ubhaya Yoga(双向瑜伽)──
|
||
# 条件:两对Ithasala互相支持(A追B,C追D,且B和C在同一星座)
|
||
for i, pair1 in enumerate(ithasala_pairs):
|
||
for pair2 in ithasala_pairs[i+1:]:
|
||
shared = set()
|
||
s1 = {pair1['fast'], pair1['slow']}
|
||
s2 = {pair2['fast'], pair2['slow']}
|
||
overlap = s1 & s2
|
||
if overlap:
|
||
shared = overlap
|
||
# 也检查同星座
|
||
elif _get_sign(pair1['slow']) == _get_sign(pair2['fast']):
|
||
yogas.append({
|
||
'type': 'Ubhaya',
|
||
'planets': [pair1['fast'], pair1['slow'], pair2['fast'], pair2['slow']],
|
||
'description': f'{pair1["fast"]}→{pair1["slow"]}与{pair2["fast"]}→{pair2["slow"]}互相支持,Ubhaya双向瑜伽',
|
||
})
|
||
|
||
# ── 9. Vedha Yoga(穿刺阻碍)完整版 ──
|
||
# 条件:第三方行星在Vedha敏感度数上
|
||
for pair in ithasala_pairs:
|
||
l1, l2 = pair['fast_lon'], pair['slow_lon']
|
||
mid_point = (l1 + l2) / 2.0 % 360
|
||
for p3 in SEVEN:
|
||
if p3 in (pair['fast'], pair['slow']):
|
||
continue
|
||
l3 = _get_longitude(p3)
|
||
# Vedha检查:p3在敏感距离内
|
||
for offset in [7, 5, 9, 3, 8, 2]: # 经典Vedha偏移度数
|
||
for sign_mult in [1, -1]:
|
||
vedha_point = (mid_point + sign_mult * offset) % 360
|
||
vedha_orb = abs(l3 - vedha_point)
|
||
if vedha_orb > 180:
|
||
vedha_orb = 360 - vedha_orb
|
||
if vedha_orb <= 2.0: # 2°容许度
|
||
yogas.append({
|
||
'type': 'Vedha',
|
||
'planets': [pair['fast'], pair['slow'], p3],
|
||
'vedha_offset': offset,
|
||
'description': f'{p3}在Vedha敏感点(偏移{offset}°)穿刺{pair["fast"]}-{pair["slow"]},Vedha穿刺瑜伽',
|
||
})
|
||
break
|
||
else:
|
||
continue
|
||
break
|
||
|
||
# ── 10. Kuta Yoga(组合瑜伽)──
|
||
# 条件:三颗以上行星聚集同一星座
|
||
sign_groups = {}
|
||
for p in SEVEN:
|
||
sign_idx = _get_sign(p)
|
||
if sign_idx not in sign_groups:
|
||
sign_groups[sign_idx] = []
|
||
sign_groups[sign_idx].append(p)
|
||
|
||
for sign_idx, planets in sign_groups.items():
|
||
if len(planets) >= 3:
|
||
yogas.append({
|
||
'type': 'Kuta',
|
||
'planets': planets,
|
||
'description': f'{len(planets)}颗行星({", ".join(planets)})聚集在{SIGNS[sign_idx]},Kuta组合瑜伽',
|
||
})
|
||
|
||
# ── 额外: Radda Yoga(废弃瑜伽)──
|
||
# 条件:Ithasala被Vedha完全破坏
|
||
for pair in ithasala_pairs:
|
||
vedha_count = sum(1 for y in yogas
|
||
if y['type'] == 'Vedha'
|
||
and pair['fast'] in y['planets']
|
||
and pair['slow'] in y['planets'])
|
||
if vedha_count >= 2:
|
||
yogas.append({
|
||
'type': 'Radda',
|
||
'planets': [pair['fast'], pair['slow']],
|
||
'description': f'{pair["fast"]}-{pair["slow"]}的Ithasala被多重Vedha破坏,Radda废弃瑜伽',
|
||
})
|
||
|
||
return yogas
|
||
|
||
|
||
def detect_vedha(varsha_planets: Dict) -> List[Dict]:
|
||
"""
|
||
专门检测Vedha(穿刺阻碍)—— 完整版 v7.0
|
||
|
||
基于经典Tajika Vedha表:每对行星有固定敏感度数位置,
|
||
当第三方行星落入该位置时,破坏原有的Ithasala/Easarapha。
|
||
|
||
Args:
|
||
varsha_planets: 年运盘行星数据
|
||
|
||
Returns:
|
||
Vedha列表
|
||
"""
|
||
vedhas = []
|
||
SEVEN = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn']
|
||
|
||
def _get_lon(pname):
|
||
pd = varsha_planets.get(pname, {})
|
||
if isinstance(pd, (int, float)):
|
||
return float(pd)
|
||
sign = pd.get('sign', '')
|
||
deg = pd.get('degree', 0) % 30
|
||
if sign in SIGNS:
|
||
return SIGNS.index(sign) * 30 + deg
|
||
return pd.get('longitude', pd.get('lon', 0))
|
||
|
||
# Vedha敏感度数(经典Tajika规则)
|
||
# 对于度数差N(1-30),Vedha在特定偏移处
|
||
VEDHA_OFFSETS = {
|
||
1: 7, 2: 5, 3: 9, 4: 3, 5: 8, 6: 2, 7: 10, 8: 4,
|
||
9: 6, 10: 1, 11: 9, 12: 3, 13: 7, 14: 5, 15: 2,
|
||
}
|
||
|
||
for p1 in SEVEN:
|
||
for p2 in SEVEN:
|
||
if p1 >= p2:
|
||
continue
|
||
|
||
l1 = _get_lon(p1)
|
||
l2 = _get_lon(p2)
|
||
|
||
# 计算两星间度数差
|
||
diff = abs(l1 - l2)
|
||
if diff > 180:
|
||
diff = 360 - diff
|
||
|
||
if diff > 15: # 超出Vedha表范围
|
||
continue
|
||
|
||
# 查找Vedha偏移
|
||
diff_key = int(diff) + 1 # 1-based
|
||
if diff_key not in VEDHA_OFFSETS:
|
||
continue
|
||
|
||
offset = VEDHA_OFFSETS[diff_key]
|
||
|
||
# 计算Vedha敏感点
|
||
mid = (l1 + l2) / 2.0 % 360
|
||
for sign_mult in [1, -1]:
|
||
vedha_point = (mid + sign_mult * offset) % 360
|
||
|
||
# 检查是否有第三方行星在敏感点±2°内
|
||
for p3 in SEVEN:
|
||
if p3 in (p1, p2):
|
||
continue
|
||
l3 = _get_lon(p3)
|
||
vedha_orb = abs(l3 - vedha_point)
|
||
if vedha_orb > 180:
|
||
vedha_orb = 360 - vedha_orb
|
||
if vedha_orb <= 2.0:
|
||
vedhas.append({
|
||
'planets': [p1, p2, p3],
|
||
'vedha_degree': round(vedha_point, 2),
|
||
'offset': offset,
|
||
'orb': round(vedha_orb, 2),
|
||
'description': f'{p3}在Vedha敏感点({offset}°偏移)穿刺{p1}-{p2}',
|
||
})
|
||
|
||
return vedhas
|