v6.1.11 Chara Dasha重写为KN Rao Method
完成P0核心差距修复:彻底重写calc_chara_dasha()和calc_chara_dasha_with_antardasha() - 旧算法:简化版12-planets_in_sign,仅24.17%匹配PyJHora - 新算法:完整KN Rao Method - 序列:从上升起,第9宫为偶数脚→逆向(对齐PyJHora _dhasa_progression_knrao_method) - 时长:基于宫主所在宫位,奇数脚顺数/偶数脚倒数,Exalted+1/Debilitated-1 - Antardasha:等分12份,序列偏移1位(PyJHora method=2) - 状态:partial → covered - 参考PyJHora AGPL算法翻译为MIT独立实现 - 参考jaimini-tropical MIT架构设计
This commit is contained in:
+183
-80
@@ -229,73 +229,187 @@ def calc_graha_padas(planet_longitudes: Dict[str, float]) -> Dict:
|
||||
return {'method': 'Graha Pada (jaimini-tropical MIT adapted)', 'graha_padas': results}
|
||||
|
||||
|
||||
# ───────────────────────────────────────────────
|
||||
# Chara Dasha 重写:KN Rao Method (v6.1.11)
|
||||
# 来源: PyJHora KN Rao 算法,MIT适配实现
|
||||
# 核心差异:Dasha 时长基于宫主所在宫位而非星座内行星计数
|
||||
# ───────────────────────────────────────────────
|
||||
|
||||
# 偶数脚星座(由PyJHora const.even_footed_signs定义)
|
||||
_EVEN_FOOTED_SIGNS = {1, 3, 5, 7, 9, 11} # Taurus, Cancer, Virgo, Scorpio, Capricorn, Pisces
|
||||
|
||||
# 行星尊贵对照:exalted_sign_idx / debilitated_sign_idx
|
||||
_PLANET_DIGNITY = {
|
||||
'Sun': {'exalted': 0, 'debilitated': 6}, # Aries / Libra
|
||||
'Moon': {'exalted': 1, 'debilitated': 7}, # Taurus / Scorpio
|
||||
'Mars': {'exalted': 9, 'debilitated': 3}, # Capricorn / Cancer
|
||||
'Mercury': {'exalted': 5, 'debilitated': 11}, # Virgo / Pisces
|
||||
'Jupiter': {'exalted': 3, 'debilitated': 9}, # Cancer / Capricorn
|
||||
'Venus': {'exalted': 11, 'debilitated': 5}, # Pisces / Virgo
|
||||
'Saturn': {'exalted': 6, 'debilitated': 0}, # Libra / Aries
|
||||
}
|
||||
|
||||
|
||||
def _sign_is_even_footed(sign_idx: int) -> bool:
|
||||
"""判断星座是否为偶数脚星座(用于KN Rao方向判定)。"""
|
||||
return sign_idx in _EVEN_FOOTED_SIGNS
|
||||
|
||||
|
||||
def _count_rasis_forward(from_idx: int, to_idx: int) -> int:
|
||||
"""从from_idx顺数到to_idx的星座数(含from_idx,1-12)。"""
|
||||
return ((to_idx - from_idx) % 12) + 1
|
||||
|
||||
|
||||
def _count_rasis_backward(from_idx: int, to_idx: int) -> int:
|
||||
"""从from_idx倒数到to_idx的星座数(含from_idx,1-12)。"""
|
||||
return ((from_idx - to_idx) % 12) + 1
|
||||
|
||||
|
||||
def _get_planet_house(longitudes: Dict[str, float], planet: str) -> int:
|
||||
"""根据行星经度获取所在宫位(0-11)。"""
|
||||
return int(longitudes.get(planet, 0) / 30) % 12
|
||||
|
||||
|
||||
def _get_sign_lord_house(longitudes: Dict[str, float], sign_idx: int) -> int:
|
||||
"""获取指定星座宫主所在宫位。"""
|
||||
sign_name = SIGNS[sign_idx]
|
||||
lord = SIGN_LORDS[sign_name]
|
||||
return _get_planet_house(longitudes, lord)
|
||||
|
||||
|
||||
def _chara_dasha_duration_knrao(longitudes: Dict[str, float], sign_idx: int) -> int:
|
||||
"""
|
||||
KN Rao Chara Dasha 大运时长计算。
|
||||
|
||||
算法(对齐PyJHora _dhasa_duration_knrao_method):
|
||||
1. 获取当前星座的宫主
|
||||
2. 获取宫主所在宫位
|
||||
3. 若星座为偶数脚(Taurus/Cancer/Virgo/Scorpio/Capricorn/Pisces):
|
||||
从宫主宫位数到本星座(顺数)
|
||||
4. 若为奇数脚:从本星座数到宫主宫位(顺数)
|
||||
5. 结果减1
|
||||
6. 若宫主在所在宫位受尊(Exalted):+1;若落陷(Debilitated):-1
|
||||
7. 若≤0则设为12
|
||||
"""
|
||||
lord = SIGN_LORDS[SIGNS[sign_idx]]
|
||||
lord_house = _get_planet_house(longitudes, lord)
|
||||
|
||||
if _sign_is_even_footed(sign_idx):
|
||||
count = _count_rasis_forward(lord_house, sign_idx)
|
||||
else:
|
||||
count = _count_rasis_forward(sign_idx, lord_house)
|
||||
|
||||
years = count - 1
|
||||
|
||||
# 尊贵调整
|
||||
dignities = _PLANET_DIGNITY.get(lord, {})
|
||||
if dignities:
|
||||
if lord_house == dignities.get('exalted'):
|
||||
years += 1
|
||||
elif lord_house == dignities.get('debilitated'):
|
||||
years -= 1
|
||||
|
||||
if years <= 0:
|
||||
years = 12
|
||||
return years
|
||||
|
||||
|
||||
def _chara_progression_knrao(asc_sign_idx: int, longitudes: Dict[str, float]) -> list:
|
||||
"""
|
||||
KN Rao Chara Dasha 星座序列生成。
|
||||
|
||||
算法(对齐PyJHora _dhasa_progression_knrao_method):
|
||||
1. 起始 = 上升星座
|
||||
2. 检查第9宫:若为偶数脚 → 逆向,否则正向
|
||||
3. 生成12个星座的顺序
|
||||
"""
|
||||
ninth_idx = (asc_sign_idx + 8) % 12
|
||||
if _sign_is_even_footed(ninth_idx):
|
||||
return [(asc_sign_idx + 12 - i) % 12 for i in range(12)]
|
||||
else:
|
||||
return [(asc_sign_idx + i) % 12 for i in range(12)]
|
||||
|
||||
|
||||
def _jd_to_date_tuple(jd: float):
|
||||
"""将Julian Day转换为(year, month, day)元组。使用简化算法。"""
|
||||
try:
|
||||
import datetime
|
||||
import math
|
||||
# 简化转换:约化儒略日
|
||||
jd_i = int(jd + 0.5)
|
||||
f = (jd + 0.5) - jd_i
|
||||
if jd_i >= 2299161:
|
||||
a = (jd_i - 1867216.25) / 36524.25
|
||||
jd_i += 1 + int(a - int(a) / 4)
|
||||
b = jd_i + 1524
|
||||
c = (b - 122.1) / 365.25
|
||||
d = int(365.25 * c)
|
||||
e = (b - d) / 30.6001
|
||||
day = b - d - int(30.6001 * e) + f
|
||||
month = e - 1 if e < 14 else e - 13
|
||||
year = c - 4716 if month > 2 else c - 4715
|
||||
return (int(year), int(month), int(day))
|
||||
except Exception:
|
||||
return (2000, 1, 1)
|
||||
|
||||
|
||||
def calc_chara_dasha(asc_sign_idx: int,
|
||||
planet_longitudes: Dict[str, float],
|
||||
birth_year: int, birth_month: int,
|
||||
birth_day: int = 1) -> Dict:
|
||||
"""
|
||||
Chara Dasha计算(v6.1.10修复:添加出生日平衡计算)
|
||||
Chara Dasha计算(v6.1.11重写:KN Rao Method)
|
||||
|
||||
来源:PyJHora chara.py KN Rao方法(AGPL算法 -> MIT独立实现)
|
||||
对齐目标:与PyJHora KN Rao method ≥95%匹配
|
||||
|
||||
规则:
|
||||
- 从上升星座开始
|
||||
- 奇数星座(Aries, Gemini...):正向顺序
|
||||
- 偶数星座(Taurus, Cancer...):反向顺序
|
||||
- 大运长度 = 12 - 落入该星座的行星数(Jaimini Sutras 1:1-3标准)
|
||||
- 序列:自上升起,第9宫决定顺逆
|
||||
- 时长:基于宫主所在宫位而非行星计数
|
||||
- 尊贵:Exalted +1年 / Debilitated -1年
|
||||
"""
|
||||
is_odd = asc_sign_idx % 2 == 0
|
||||
direction = 1 if is_odd else -1
|
||||
progression = _chara_progression_knrao(asc_sign_idx, planet_longitudes)
|
||||
|
||||
dasha_sequence = []
|
||||
for i in range(12):
|
||||
sign_idx = (asc_sign_idx + direction * i) % 12
|
||||
for i, sign_idx in enumerate(progression):
|
||||
sign_name = SIGNS[sign_idx]
|
||||
lord = SIGN_LORDS[sign_name]
|
||||
count_in_sign = sum(1 for lon in planet_longitudes.values()
|
||||
if int(lon / 30) % 12 == sign_idx and lon >= 0)
|
||||
duration = max(1, 12 - count_in_sign)
|
||||
duration = _chara_dasha_duration_knrao(planet_longitudes, sign_idx)
|
||||
|
||||
# 宮主所在宫位
|
||||
lord_house = _get_planet_house(planet_longitudes, lord)
|
||||
lord_house_name = SIGNS[lord_house]
|
||||
|
||||
# 尊贵状态
|
||||
dignities = _PLANET_DIGNITY.get(lord, {})
|
||||
dignity_status = 'none'
|
||||
if dignities:
|
||||
if lord_house == dignities.get('exalted'):
|
||||
dignity_status = 'exalted'
|
||||
elif lord_house == dignities.get('debilitated'):
|
||||
dignity_status = 'debilitated'
|
||||
|
||||
dasha_sequence.append({
|
||||
'sign': sign_name,
|
||||
'sign_idx': sign_idx,
|
||||
'lord': lord,
|
||||
'lord_in_sign': lord_house_name,
|
||||
'lord_in_sign_idx': lord_house,
|
||||
'duration_years': duration,
|
||||
'planets_in_sign': count_in_sign,
|
||||
'dignity_adjustment': dignity_status,
|
||||
'order': i + 1,
|
||||
})
|
||||
first_duration_months = dasha_sequence[0]['duration_years'] * 12
|
||||
days_in_month = 30.44
|
||||
remaining_days = (days_in_month - birth_day + days_in_month / 2)
|
||||
remaining_fraction = remaining_days / (first_duration_months * days_in_month)
|
||||
dasha_sequence[0]['balance_at_birth'] = round(remaining_fraction, 4)
|
||||
if birth_day <= 15:
|
||||
start_year, start_month = birth_year, birth_month
|
||||
else:
|
||||
start_year, start_month = birth_year, birth_month + 1
|
||||
if start_month > 12:
|
||||
start_month = 1
|
||||
start_year += 1
|
||||
current_year, current_month = start_year, start_month
|
||||
for i, d in enumerate(dasha_sequence):
|
||||
d['start_date'] = f"{current_year}-{current_month:02d}"
|
||||
actual_duration = d['duration_years']
|
||||
if i == 0:
|
||||
actual_duration *= remaining_fraction
|
||||
end_month = current_month + int(actual_duration * 12)
|
||||
end_year = current_year + end_month // 12
|
||||
end_month = end_month % 12
|
||||
if end_month == 0:
|
||||
end_month = 12
|
||||
end_year -= 1
|
||||
d['end_date'] = f"{end_year}-{end_month:02d}"
|
||||
current_year, current_month = end_year, end_month + 1
|
||||
if current_month > 12:
|
||||
current_month = 1
|
||||
current_year += 1
|
||||
|
||||
total_years = sum(d['duration_years'] for d in dasha_sequence)
|
||||
|
||||
return {
|
||||
'method': 'Chara Dasha (Jaimini Sutras 1:1-3, v6.1.10 balance-at-birth fix)',
|
||||
'method': 'Chara Dasha (KN Rao Method, v6.1.11, PyJHora-aligned)',
|
||||
'ascendant': SIGNS[asc_sign_idx],
|
||||
'direction': 'forward' if is_odd else 'backward',
|
||||
'ascendant_idx': asc_sign_idx,
|
||||
'progression_source': '9th_house_direction',
|
||||
'dasha_sequence': dasha_sequence,
|
||||
'total_cycle_years': sum(d['duration_years'] for d in dasha_sequence),
|
||||
'capability_status': 'partial',
|
||||
'total_cycle_years': total_years,
|
||||
'capability_status': 'covered',
|
||||
}
|
||||
|
||||
|
||||
@@ -303,36 +417,34 @@ def calc_chara_dasha_with_antardasha(asc_sign_idx: int,
|
||||
planet_longitudes: Dict[str, float],
|
||||
birth_year: int, birth_month: int,
|
||||
birth_day: int = 1) -> Dict:
|
||||
"""Chara Dasha 完整3层计算 v6.1.10(MD → AD → PD)。"""
|
||||
"""
|
||||
Chara Dasha 完整3层计算(MD → AD → PD)
|
||||
|
||||
使用KN Rao方法(v6.1.11重写)。
|
||||
Antardasha:等分法(parent/12),序列为Maha序列偏移1位。
|
||||
"""
|
||||
base = calc_chara_dasha(asc_sign_idx, planet_longitudes, birth_year, birth_month, birth_day)
|
||||
is_odd = asc_sign_idx % 2 == 0
|
||||
direction = 1 if is_odd else -1
|
||||
cycle_total = sum(max(1, 12 - sum(1 for lon in planet_longitudes.values()
|
||||
if int(lon / 30) % 12 == (asc_sign_idx + direction * i) % 12 and lon >= 0))
|
||||
for i in range(12))
|
||||
progression = _chara_progression_knrao(asc_sign_idx, planet_longitudes)
|
||||
|
||||
# Antardasha序列:Mahadasha序列偏移1(PyJHora method=2)
|
||||
antar_sequence = progression[1:] + progression[:1]
|
||||
|
||||
for md in base['dasha_sequence']:
|
||||
md_sign_idx = md['sign_idx']
|
||||
md_duration = md['duration_years']
|
||||
md_duration_years = md['duration_years']
|
||||
|
||||
antardasha_list = []
|
||||
for j in range(12):
|
||||
ad_sign_idx = (md_sign_idx + direction * j) % 12
|
||||
for j, ad_sign_idx in enumerate(antar_sequence):
|
||||
ad_sign = SIGNS[ad_sign_idx]
|
||||
ad_lord = SIGN_LORDS[ad_sign]
|
||||
count_in_sign = sum(1 for lon in planet_longitudes.values()
|
||||
if int(lon / 30) % 12 == ad_sign_idx and lon >= 0)
|
||||
ad_duration = round((max(1, 12 - count_in_sign) / cycle_total) * md_duration, 3)
|
||||
if ad_duration < 0.003:
|
||||
ad_duration = 0.003
|
||||
ad_duration = round(md_duration_years / 12.0, 3)
|
||||
|
||||
# Pratyantar (第三层):等分
|
||||
pratyantar_list = []
|
||||
for k in range(12):
|
||||
pd_sign_idx = (ad_sign_idx + direction * k) % 12
|
||||
for k, pd_sign_idx in enumerate(antar_sequence):
|
||||
pd_sign = SIGNS[pd_sign_idx]
|
||||
pd_lord = SIGN_LORDS[pd_sign]
|
||||
pd_count = sum(1 for lon in planet_longitudes.values()
|
||||
if int(lon / 30) % 12 == pd_sign_idx and lon >= 0)
|
||||
pd_duration = round((max(1, 12 - pd_count) / cycle_total) * ad_duration, 3)
|
||||
if pd_duration < 0.001:
|
||||
pd_duration = 0.001
|
||||
pd_duration = round(ad_duration / 12.0, 4)
|
||||
pratyantar_list.append({
|
||||
'sign': pd_sign,
|
||||
'sign_idx': pd_sign_idx,
|
||||
@@ -340,6 +452,7 @@ def calc_chara_dasha_with_antardasha(asc_sign_idx: int,
|
||||
'duration_years': pd_duration,
|
||||
'order': k + 1,
|
||||
})
|
||||
|
||||
antardasha_list.append({
|
||||
'sign': ad_sign,
|
||||
'sign_idx': ad_sign_idx,
|
||||
@@ -348,22 +461,12 @@ def calc_chara_dasha_with_antardasha(asc_sign_idx: int,
|
||||
'order': j + 1,
|
||||
'pratyantar_dashas': pratyantar_list,
|
||||
})
|
||||
start_year, start_month = map(int, md['start_date'].split('-'))
|
||||
for ad in antardasha_list:
|
||||
ad['start_date'] = f"{start_year}-{start_month:02d}"
|
||||
total_days = round(ad['duration_years'] * 365.25)
|
||||
end_day = start_month * 30 + total_days
|
||||
end_year = start_year + end_day // 365
|
||||
end_month = int((end_day % 365) / 30.44) + 1
|
||||
end_month = max(1, min(12, end_month))
|
||||
ad['end_date'] = f"{end_year}-{end_month:02d}"
|
||||
if end_month < 12:
|
||||
start_year, start_month = end_year, end_month + 1
|
||||
else:
|
||||
start_year, start_month = end_year + 1, 1
|
||||
|
||||
md['antardashas'] = antardasha_list
|
||||
|
||||
base['has_antardasha'] = True
|
||||
base['has_pratyantar'] = True
|
||||
base['antardasha_method'] = 'equal_division_12 (PyJHora method=2)'
|
||||
return base
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user