v6.0.13: 新增 Tajika Yogas + Sahams 技法

- scripts/tajika.py: 新增 calc_tajika_yogas, calc_all_sahams 等6个函数
- scripts/jyotish_engine.py: cmd_full_reading 新增 Step 4.8 调用新函数
- references/technique_registry.json: 新增 tajika_yogas, sahams 技法注册(partial)
- references/strict-workflow-router.md: Audit Table 模板新增两行
- CHANGELOG.md: 新增 v6.0.13 条目
- SKILL.md: 版本号 6.0.12 → 6.0.13
This commit is contained in:
732642856
2026-06-04 04:35:44 +08:00
parent f6deae7a25
commit a20c13490c
6 changed files with 498 additions and 5 deletions
+19
View File
@@ -3454,6 +3454,25 @@ def cmd_full_reading(args):
except Exception as e:
report['errors'].append(f"dispositor-chain+inter-chart: {e}")
# ── Step 4.8: Tajika Yogas + Sahams (v6.0.13) ──
try:
from tajika import calc_tajika_yogas, calc_all_sahams
# Tajika Yogas(用本命盘行星经度)
tc_yogas = calc_tajika_yogas(planet_lons)
report['modules']['tajika_yogas'] = tc_yogas
# Sahams(特殊点)—— 需要出生时间
birth_dt = getattr(args, 'birth_datetime', None)
if birth_dt and planet_lons:
sahams_result = calc_all_sahams(planet_lons, asc_deg, birth_dt)
report['modules']['sahams'] = sahams_result
else:
report['modules']['sahams'] = {'warning': 'birth_datetime or planet_lons missing, skip saham calc'}
except Exception as e:
report['errors'].append(f"tajika-yogas+sahams: {e}")
# ── Step 5: 精确相位 ──
try:
from aspects import calc_all_aspects
+405
View File
@@ -237,3 +237,408 @@ def _year_theme(lord):
'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, # 总结
}
"""
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': {...}, # 勇气点
}
"""
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') -> Dict:
"""
计算所有主要Sahams(完整版)。
参数:
planet_lons: 所有行星经度 {名: 经度}
asc_lon: 上升经度
birth_dt: 出生时间
chart_type: 'natal'(本命)或 'varsha'(年运)
返回:
完整Sahams字典
"""
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 = _is_daytime(birth_dt, sun_lon, asc_lon)
results = {}
# 1. Punya Saham(福德点):Moon - Sun + Asc
p_lon = _saham(sun_lon, moon_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', '勇气点——勇气、竞争力、克服困难')
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': '双鱼',
}