0f0c5dbdfa
New modules (from Chinese reference articles): - darakaraka_reader.py: DK spouse deep analysis (planet/sign/house/aspects) - rashi_tulya_navamsa.py: RTN mapping engine (D9->D1, Gunas, curse detection) - curse_yoga_detector.py: Curse yoga naming (Yama/Preta/Rakshasa/Pisacha) - spouse_status_yoga.py: High-status spouse detection (7 principles) - pancha_pakshi.py: Five-bird electional timing (daily schedule) - tithi_analyzer.py: Tithi lord emotional pattern analysis Infrastructure: - technique_registry.py: Unified technique registry (31 techniques tracked) - reading_orchestrator.py: Theme-based reading orchestrator (5 themes) All 6 modules tested and runnable. Current baseline: Precision 94.66%, Recall 92.95%, F1 93.80%
507 lines
18 KiB
Python
507 lines
18 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
rashi_tulya_navamsa.py — Rashi Tulya Navamsa (RTN) 映射引擎
|
||
===========================================================
|
||
将D9行星位置映射到D1,揭示隐藏的力量结构
|
||
|
||
来源: 《Dhruva Nadi》《Deva Keralam》
|
||
版本: v1.0 | 2026-06-07
|
||
"""
|
||
|
||
from typing import Dict, List, Optional, Tuple
|
||
from dataclasses import dataclass, field
|
||
|
||
|
||
# RTN宫位名称
|
||
RTN_HOUSE_NAMES = {
|
||
1: "Lagnamsa", # 性格和本质
|
||
2: "Dhanamsa", # 财富和积累
|
||
3: "Vikramsa", # 勇气、努力和兄弟
|
||
4: "Sukhamsa", # 家庭幸福、母亲
|
||
5: "Putramsa", # 子女、智力
|
||
6: "Ariamsa", # 敌人、障碍、健康
|
||
7: "Kalatramsa", # 婚姻、配偶、合伙
|
||
8: "Randhramsa", # 突变、意外、转变
|
||
9: "Bhagyamsa", # 命运、宗教、好运
|
||
10: "Karmamsa", # 事业、成就、公共生活
|
||
11: "Labhamsa", # 收益、朋友、社交圈
|
||
12: "Vyayamsa", # 支出、隐秘、精神追求
|
||
}
|
||
|
||
# Gunas分类
|
||
GUNAS = {
|
||
"Rajas": ["Venus", "Mercury"], # 创造
|
||
"Sattva": ["Jupiter", "Sun", "Moon"], # 维持
|
||
"Tamas": ["Saturn", "Mars", "Rahu", "Ketu"], # 毁灭
|
||
}
|
||
|
||
# 凶星合相命名
|
||
CURSE_YOGAS = {
|
||
("Mars", "Saturn"): {
|
||
"name": "Yama Yoga",
|
||
"name_cn": "阎魔瑜伽",
|
||
"meaning": "死亡之神 sitting,等待被激活。激活Dasha/Transit可触发危险疾病或事故。",
|
||
"remedy": "需要上师保护,唱诵Guru Vandana",
|
||
"severity": "critical",
|
||
},
|
||
("Saturn", "Rahu"): {
|
||
"name": "Preta Yoga",
|
||
"name_cn": "亡灵瑜伽",
|
||
"meaning": "过早死亡的风险,慢性疾病,可能来自祖先未解决的业力。",
|
||
"remedy": "湿婆能量,唱诵湿婆咒语",
|
||
"severity": "critical",
|
||
},
|
||
("Saturn", "Ketu"): {
|
||
"name": "Preta Yoga",
|
||
"name_cn": "亡灵瑜伽",
|
||
"meaning": "过早死亡的风险,慢性疾病,可能来自祖先未解决的业力。",
|
||
"remedy": "湿婆能量,唱诵湿婆咒语",
|
||
"severity": "critical",
|
||
},
|
||
("Mars", "Rahu"): {
|
||
"name": "Rakshasa Yoga",
|
||
"name_cn": "恶魔瑜伽",
|
||
"meaning": "暴力事件,粗俗或微妙的攻击,神经系统受损。",
|
||
"remedy": "杜尔迦女神,唱诵Durga咒语",
|
||
"severity": "high",
|
||
},
|
||
("Mars", "Ketu"): {
|
||
"name": "Pisacha Yoga",
|
||
"name_cn": "鬼魅瑜伽",
|
||
"meaning": "与有毒的人接触,被鬼魂困扰,天蝎座凶星激发。",
|
||
"remedy": "毗湿奴,唱诵Vishnu咒语",
|
||
"severity": "high",
|
||
},
|
||
}
|
||
|
||
|
||
@dataclass
|
||
class RTNPlanet:
|
||
"""RTN中的行星"""
|
||
planet: str
|
||
d9_sign: str
|
||
d9_house: int
|
||
d1_sign: str # 映射到D1的星座
|
||
d1_house: int # 映射到D1的宫位
|
||
rtn_house_name: str # RTN宫位名称
|
||
dignity_d9: str # D9中的庙旺状态
|
||
dignity_d1: str # D1中的庙旺状态
|
||
|
||
# 与D1行星的合相
|
||
d1_conjunctions: List[str] = field(default_factory=list)
|
||
d1_aspects: List[str] = field(default_factory=list)
|
||
|
||
# Gunas
|
||
guna: str = ""
|
||
|
||
# 解读
|
||
narrative: str = ""
|
||
|
||
|
||
@dataclass
|
||
class RTNAnalysis:
|
||
"""RTN分析结果"""
|
||
# 基础信息
|
||
lagna_sign: str
|
||
planets: List[RTNPlanet] = field(default_factory=list)
|
||
|
||
# 宫位映射
|
||
house_mapping: Dict[int, List[str]] = field(default_factory=dict)
|
||
|
||
# 关键发现
|
||
exalted_cancelled: List[str] = field(default_factory=list) # 耀升被取消
|
||
debilitated_cancelled: List[str] = field(default_factory=list) # 落陷被取消
|
||
|
||
# 凶星合相
|
||
curse_yogas: List[Dict] = field(default_factory=list)
|
||
|
||
# Gunas分析
|
||
guna_balance: Dict[str, int] = field(default_factory=dict)
|
||
guna_narrative: str = ""
|
||
|
||
# 综合评估
|
||
strength_score: float = 0.5
|
||
weakness_score: float = 0.5
|
||
|
||
# 叙事
|
||
full_narrative: str = ""
|
||
|
||
|
||
class RashiTulyaNavamsa:
|
||
"""RTN映射引擎"""
|
||
|
||
def __init__(self):
|
||
self.house_names = RTN_HOUSE_NAMES
|
||
self.gunas = GUNAS
|
||
self.curse_yogas = CURSE_YOGAS
|
||
|
||
def analyze(self, chart_data: Dict) -> RTNAnalysis:
|
||
"""
|
||
执行RTN分析
|
||
|
||
Args:
|
||
chart_data: 标准星盘数据(含context.navamsa_planets)
|
||
|
||
Returns:
|
||
RTNAnalysis对象
|
||
"""
|
||
# Step 1: 获取D9行星位置
|
||
d9_planets = self._get_d9_planets(chart_data)
|
||
|
||
# Step 2: 获取D1信息
|
||
d1_planets = chart_data.get("planets", {})
|
||
asc_sign = chart_data.get("ascendant", {}).get("sign", "Aries")
|
||
asc_house = 1 # 上升永远在第1宫
|
||
|
||
# Step 3: 创建RTN分析对象
|
||
analysis = RTNAnalysis(lagna_sign=asc_sign)
|
||
|
||
# Step 4: 映射每个D9行星到D1
|
||
for planet_name, d9_data in d9_planets.items():
|
||
if planet_name in ["Ascendant", "Lagna"]:
|
||
continue
|
||
|
||
rtn_planet = self._map_planet(
|
||
planet_name, d9_data, d1_planets, asc_sign
|
||
)
|
||
analysis.planets.append(rtn_planet)
|
||
|
||
# 记录宫位映射
|
||
house = rtn_planet.d1_house
|
||
if house not in analysis.house_mapping:
|
||
analysis.house_mapping[house] = []
|
||
analysis.house_mapping[house].append(planet_name)
|
||
|
||
# Step 5: 检测耀升/落陷取消
|
||
self._check_dignity_cancellation(analysis)
|
||
|
||
# Step 6: 检测凶星合相
|
||
self._detect_curse_yogas(analysis)
|
||
|
||
# Step 7: Gunas分析
|
||
self._analyze_gunas(analysis)
|
||
|
||
# Step 8: 综合评估
|
||
self._comprehensive_assessment(analysis)
|
||
|
||
# Step 9: 生成叙事
|
||
analysis.full_narrative = self._generate_narrative(analysis)
|
||
|
||
return analysis
|
||
|
||
def _get_d9_planets(self, chart_data: Dict) -> Dict:
|
||
"""从chart_data中提取D9行星位置"""
|
||
context = chart_data.get("context", {})
|
||
|
||
# 优先从context获取
|
||
if "navamsa_planets" in context:
|
||
return context["navamsa_planets"]
|
||
|
||
# 否则尝试从varga计算
|
||
# 简化:返回空
|
||
return {}
|
||
|
||
def _map_planet(self, planet_name: str, d9_data: Dict,
|
||
d1_planets: Dict, asc_sign: str) -> RTNPlanet:
|
||
"""将单个D9行星映射到D1"""
|
||
d9_sign = d9_data.get("sign", "")
|
||
d9_house = d9_data.get("house", 0)
|
||
|
||
# 在D1中找到相同的星座
|
||
d1_sign = d9_sign
|
||
d1_house = self._sign_to_house(d9_sign, asc_sign)
|
||
|
||
# RTN宫位名称
|
||
rtn_name = self.house_names.get(d1_house, f"House_{d1_house}")
|
||
|
||
# 检测D1中的庙旺状态
|
||
d1_dignity = self._check_dignity_in_chart(planet_name, d9_sign, d1_planets)
|
||
d9_dignity = d9_data.get("dignity", "")
|
||
|
||
# 检测与D1行星的合相
|
||
d1_conjs = self._find_d1_conjunctions(planet_name, d1_sign, d1_planets)
|
||
|
||
# Gunas
|
||
guna = self._get_guna(planet_name)
|
||
|
||
return RTNPlanet(
|
||
planet=planet_name,
|
||
d9_sign=d9_sign,
|
||
d9_house=d9_house,
|
||
d1_sign=d1_sign,
|
||
d1_house=d1_house,
|
||
rtn_house_name=rtn_name,
|
||
dignity_d9=d9_dignity,
|
||
dignity_d1=d1_dignity,
|
||
d1_conjunctions=d1_conjs,
|
||
guna=guna,
|
||
)
|
||
|
||
def _sign_to_house(self, sign: str, asc_sign: str) -> int:
|
||
"""根据上升星座计算某星座是第几宫"""
|
||
SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
||
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
|
||
|
||
try:
|
||
asc_idx = SIGNS.index(asc_sign)
|
||
sign_idx = SIGNS.index(sign)
|
||
house = ((sign_idx - asc_idx) % 12) + 1
|
||
return house
|
||
except ValueError:
|
||
return 1
|
||
|
||
def _check_dignity_in_chart(self, planet: str, sign: str,
|
||
d1_planets: Dict) -> str:
|
||
"""检查行星在某星座的庙旺状态"""
|
||
# 简化实现
|
||
EXALTATION = {
|
||
"Sun": "Aries", "Moon": "Taurus", "Mars": "Capricorn",
|
||
"Mercury": "Virgo", "Jupiter": "Cancer", "Venus": "Pisces",
|
||
"Saturn": "Libra",
|
||
}
|
||
DEBILITATION = {
|
||
"Sun": "Libra", "Moon": "Scorpio", "Mars": "Cancer",
|
||
"Mercury": "Pisces", "Jupiter": "Capricorn", "Venus": "Virgo",
|
||
"Saturn": "Aries",
|
||
}
|
||
OWN_SIGN = {
|
||
"Sun": "Leo", "Moon": "Cancer", "Mars": ["Aries", "Scorpio"],
|
||
"Mercury": ["Gemini", "Virgo"], "Jupiter": ["Sagittarius", "Pisces"],
|
||
"Venus": ["Taurus", "Libra"], "Saturn": ["Capricorn", "Aquarius"],
|
||
}
|
||
|
||
if EXALTATION.get(planet) == sign:
|
||
return "exalted"
|
||
elif DEBILITATION.get(planet) == sign:
|
||
return "debilitated"
|
||
elif sign in (OWN_SIGN.get(planet) or []):
|
||
return "own"
|
||
return "neutral"
|
||
|
||
def _find_d1_conjunctions(self, planet_name: str, sign: str,
|
||
d1_planets: Dict) -> List[str]:
|
||
"""找到D1中同星座的其他行星"""
|
||
conjs = []
|
||
for pname, pdata in d1_planets.items():
|
||
if pname == planet_name:
|
||
continue
|
||
if isinstance(pdata, dict) and pdata.get("sign") == sign:
|
||
conjs.append(pname)
|
||
return conjs
|
||
|
||
def _get_guna(self, planet: str) -> str:
|
||
"""获取行星的Gunas"""
|
||
for guna, planets in self.gunas.items():
|
||
if planet in planets:
|
||
return guna
|
||
return "Tamas" # Rahu/Ketu默认Tamas
|
||
|
||
def _check_dignity_cancellation(self, analysis: RTNAnalysis):
|
||
"""检测耀升/落陷取消"""
|
||
for p in analysis.planets:
|
||
# 如果在D1中耀升但D9映射后落在凶星星座/宫位
|
||
if p.dignity_d1 == "exalted" and p.d1_conjunctions:
|
||
malefics = ["Saturn", "Mars", "Rahu", "Ketu"]
|
||
if any(m in p.d1_conjunctions for m in malefics):
|
||
analysis.exalted_cancelled.append(p.planet)
|
||
|
||
# 如果在D1中落陷但D9映射后与吉星合相
|
||
if p.dignity_d1 == "debilitated" and p.d1_conjunctions:
|
||
benefics = ["Jupiter", "Venus", "Mercury", "Moon"]
|
||
if any(b in p.d1_conjunctions for b in benefics):
|
||
analysis.debilitated_cancelled.append(p.planet)
|
||
|
||
def _detect_curse_yogas(self, analysis: RTNAnalysis):
|
||
"""检测凶星合相命名"""
|
||
# 按宫位检查
|
||
for house, planets in analysis.house_mapping.items():
|
||
if len(planets) < 2:
|
||
continue
|
||
|
||
# 检查所有行星对
|
||
for i, p1 in enumerate(planets):
|
||
for p2 in planets[i+1:]:
|
||
# 标准化顺序
|
||
pair = tuple(sorted([p1, p2]))
|
||
|
||
if pair in self.curse_yogas:
|
||
curse = self.curse_yogas[pair].copy()
|
||
curse["house"] = house
|
||
curse["house_name"] = self.house_names.get(house, "")
|
||
curse["planets"] = list(pair)
|
||
analysis.curse_yogas.append(curse)
|
||
|
||
def _analyze_gunas(self, analysis: RTNAnalysis):
|
||
"""分析Gunas平衡"""
|
||
counts = {"Rajas": 0, "Sattva": 0, "Tamas": 0}
|
||
|
||
for p in analysis.planets:
|
||
if p.guna in counts:
|
||
counts[p.guna] += 1
|
||
|
||
analysis.guna_balance = counts
|
||
|
||
# 生成叙事
|
||
total = sum(counts.values())
|
||
if total == 0:
|
||
return
|
||
|
||
dominant = max(counts, key=counts.get)
|
||
ratio = counts[dominant] / total
|
||
|
||
if ratio > 0.5:
|
||
if dominant == "Sattva":
|
||
analysis.guna_narrative = "Gunas以Sattva(纯善)为主导,吉星力量较强,灵性倾向明显。"
|
||
elif dominant == "Rajas":
|
||
analysis.guna_narrative = "Gunas以Rajas(活动)为主导,创造性能量活跃,追求成就。"
|
||
else:
|
||
analysis.guna_narrative = "Gunas以Tamas(惯性)为主导,毁灭力量较强,需要特别注意凶星影响。"
|
||
else:
|
||
analysis.guna_narrative = "Gunas相对平衡,三种力量相互制衡。"
|
||
|
||
def _comprehensive_assessment(self, analysis: RTNAnalysis):
|
||
"""综合评估"""
|
||
# 计算力量得分
|
||
strength = 0
|
||
weakness = 0
|
||
|
||
for p in analysis.planets:
|
||
if p.dignity_d1 in ["exalted", "own"]:
|
||
strength += 1
|
||
elif p.dignity_d1 == "debilitated":
|
||
weakness += 1
|
||
|
||
# 凶星合相增加弱点
|
||
if p.planet in ["Saturn", "Mars", "Rahu", "Ketu"] and p.d1_conjunctions:
|
||
weakness += 0.5
|
||
|
||
total = len(analysis.planets) or 1
|
||
analysis.strength_score = min(strength / total, 1.0)
|
||
analysis.weakness_score = min(weakness / total, 1.0)
|
||
|
||
def _generate_narrative(self, analysis: RTNAnalysis) -> str:
|
||
"""生成RTN解读叙事"""
|
||
parts = []
|
||
|
||
parts.append("### Rashi Tulya Navamsa (RTN) 深度分析\n")
|
||
parts.append("将Navamsa (D9) 行星映射到本命盘 (D1),揭示隐藏的力量结构。\n")
|
||
|
||
# 宫位映射概览
|
||
parts.append("\n#### RTN宫位映射概览\n")
|
||
for house in sorted(analysis.house_mapping.keys()):
|
||
planets = analysis.house_mapping[house]
|
||
name = self.house_names.get(house, "")
|
||
parts.append(f"**{name}** (第{house}宫): {', '.join(planets)}\n")
|
||
|
||
# 关键发现
|
||
if analysis.exalted_cancelled:
|
||
parts.append(f"\n⚠️ **耀升被取消**: {', '.join(analysis.exalted_cancelled)}")
|
||
parts.append("这些行星表面强大但根基不稳,效果被削弱。\n")
|
||
|
||
if analysis.debilitated_cancelled:
|
||
parts.append(f"\n✨ **落陷被取消**: {', '.join(analysis.debilitated_cancelled)}")
|
||
parts.append("这些行星表面弱势但有隐藏支持,效果被增强。\n")
|
||
|
||
# 凶星合相
|
||
if analysis.curse_yogas:
|
||
parts.append("\n#### 凶星合相警示\n")
|
||
for curse in analysis.curse_yogas:
|
||
parts.append(f"\n🔥 **{curse['name']} ({curse['name_cn']})**")
|
||
parts.append(f" 行星: {', '.join(curse['planets'])}")
|
||
parts.append(f" 宫位: 第{curse['house']}宫 ({curse['house_name']})")
|
||
parts.append(f" 含义: {curse['meaning']}")
|
||
parts.append(f" 补救: {curse['remedy']}\n")
|
||
|
||
# Gunas
|
||
if analysis.guna_narrative:
|
||
parts.append(f"\n#### Gunas能量分析\n")
|
||
parts.append(f"{analysis.guna_narrative}\n")
|
||
parts.append(f"Rajas: {analysis.guna_balance.get('Rajas', 0)} | ")
|
||
parts.append(f"Sattva: {analysis.guna_balance.get('Sattva', 0)} | ")
|
||
parts.append(f"Tamas: {analysis.guna_balance.get('Tamas', 0)}\n")
|
||
|
||
# 综合评估
|
||
parts.append(f"\n#### 综合评估\n")
|
||
parts.append(f"力量指数: {analysis.strength_score:.0%}\n")
|
||
parts.append(f"弱点指数: {analysis.weakness_score:.0%}\n")
|
||
|
||
if analysis.strength_score > analysis.weakness_score:
|
||
parts.append("\n总体评估:吉星力量占优,积极因素可以克服挑战。\n")
|
||
else:
|
||
parts.append("\n总体评估:凶星影响较强,需要特别注意相关领域。\n")
|
||
|
||
return "\n".join(parts)
|
||
|
||
def to_dict(self, analysis: RTNAnalysis) -> Dict:
|
||
"""转换为字典"""
|
||
return {
|
||
"lagna_sign": analysis.lagna_sign,
|
||
"planets": [
|
||
{
|
||
"planet": p.planet,
|
||
"d9_sign": p.d9_sign,
|
||
"d1_house": p.d1_house,
|
||
"rtn_house_name": p.rtn_house_name,
|
||
"dignity_d9": p.dignity_d9,
|
||
"dignity_d1": p.dignity_d1,
|
||
"d1_conjunctions": p.d1_conjunctions,
|
||
"guna": p.guna,
|
||
}
|
||
for p in analysis.planets
|
||
],
|
||
"house_mapping": analysis.house_mapping,
|
||
"exalted_cancelled": analysis.exalted_cancelled,
|
||
"debilitated_cancelled": analysis.debilitated_cancelled,
|
||
"curse_yogas": analysis.curse_yogas,
|
||
"guna_balance": analysis.guna_balance,
|
||
"guna_narrative": analysis.guna_narrative,
|
||
"strength_score": round(analysis.strength_score, 2),
|
||
"weakness_score": round(analysis.weakness_score, 2),
|
||
"narrative": analysis.full_narrative,
|
||
}
|
||
|
||
|
||
# ============================================================================
|
||
# 便捷函数
|
||
# ============================================================================
|
||
|
||
def analyze_rtn(chart_data: Dict) -> Dict:
|
||
"""便捷函数:RTN分析"""
|
||
engine = RashiTulyaNavamsa()
|
||
analysis = engine.analyze(chart_data)
|
||
return engine.to_dict(analysis)
|
||
|
||
|
||
# ============================================================================
|
||
# CLI 调试
|
||
# ============================================================================
|
||
|
||
if __name__ == "__main__":
|
||
mock_chart = {
|
||
"ascendant": {"sign": "Capricorn"},
|
||
"planets": {
|
||
"Moon": {"sign": "Aquarius", "house": 2},
|
||
"Saturn": {"sign": "Aquarius", "house": 2},
|
||
"Mars": {"sign": "Aquarius", "house": 2},
|
||
"Rahu": {"sign": "Aquarius", "house": 2},
|
||
"Jupiter": {"sign": "Leo", "house": 8},
|
||
},
|
||
"context": {
|
||
"navamsa_planets": {
|
||
"Moon": {"sign": "Scorpio", "house": 10, "dignity": "debilitated"},
|
||
"Saturn": {"sign": "Scorpio", "house": 10, "dignity": "neutral"},
|
||
"Mars": {"sign": "Scorpio", "house": 10, "dignity": "own"},
|
||
"Rahu": {"sign": "Scorpio", "house": 10, "dignity": "neutral"},
|
||
"Jupiter": {"sign": "Cancer", "house": 7, "dignity": "exalted"},
|
||
}
|
||
}
|
||
}
|
||
|
||
print("=" * 60)
|
||
print("Rashi Tulya Navamsa (RTN) 映射引擎")
|
||
print("=" * 60)
|
||
|
||
result = analyze_rtn(mock_chart)
|
||
print(result["narrative"])
|