KP 改成宫头到宫头判宫,并写明口径(BUG-1319,上游 0337b77f)

本命 Bhava Chalit 表仍按中点,默认宫位不变。传了宫头却不声明就拒绝;没传宫头记整宫代理。报告多一行判宫口径。pytest tests/test_kp_cusp_house_assignment.py 3 通过,tests/test_bhava_chalit.py 仍通过。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-14 00:48:38 +08:00
co-authored by Grok (agent loop)
parent d32ad7b7b1
commit 2748e43871
6 changed files with 111 additions and 3 deletions
+23 -2
View File
@@ -272,6 +272,20 @@ class BhavaChalitCalculator:
# fallback: 最近的宫
return 1
def _planet_bhava_from_cusps(self, planet_lon: float, cusps: List[float]) -> int:
"""KP 宫头到宫头:宫 i(0 起)的范围是 cusp[i] → cusp[(i+1)%12]。
落在下一宫头上的点归下一宫。本命 Bhava Chalit 表不用这条。
"""
for i in range(12):
start = cusps[i]
end = cusps[(i + 1) % 12]
arc = _angular_dist(start, end)
pos = _angular_dist(start, planet_lon)
if arc > 0 and pos < arc:
return i + 1
return 1
def _planet_rashi_house(self, planet_lon: float, asc_lon: float) -> int:
"""整宫制宫位 (Rashi house)。"""
p_si = _sign_idx(planet_lon)
@@ -330,7 +344,8 @@ class BhavaChalitCalculator:
asc_lon: float, mc_lon: float,
house_system: str = 'sripati',
jd: float = None, lat: float = None,
lon: float = None) -> Dict:
lon: float = None,
house_assignment: str = 'sandhi') -> Dict:
"""根据 Bhava 边界重新分配行星宫位。
Parameters
@@ -346,13 +361,18 @@ class BhavaChalitCalculator:
dict with keys:
house_system, boundaries, planets
"""
if house_assignment not in ('sandhi', 'cusp_to_cusp'):
raise ValueError("house_assignment must be 'sandhi' or 'cusp_to_cusp'")
cusps = self.calculate_cusps(asc_lon, mc_lon, house_system,
jd, lat, lon)
sandhis = self.calculate_sandhis(cusps)
planets = {}
for pname, plon in planet_lons.items():
bhava = self._planet_bhava(plon, sandhis)
if house_assignment == 'cusp_to_cusp':
bhava = self._planet_bhava_from_cusps(plon, cusps)
else:
bhava = self._planet_bhava(plon, sandhis)
rashi = self._planet_rashi_house(plon, asc_lon)
si = _sign_idx(plon)
deg_in_sign = plon - si * 30
@@ -380,6 +400,7 @@ class BhavaChalitCalculator:
return {
'house_system': house_system,
'house_system_desc': self.HOUSE_SYSTEMS.get(house_system, ''),
'house_assignment': house_assignment,
'ascendant_lon': round(asc_lon, 4),
'mc_lon': round(mc_lon, 4),
'planets': planets,
+7 -1
View File
@@ -13963,6 +13963,7 @@ def cmd_kp(args):
)
bhava_chart = kp_house_calculator.get_bhava_chalit_chart(
planet_lons, float(asc_lon), 0.0, 'placidus', jd, args.lat, args.lon,
house_assignment='cusp_to_cusp',
)
bhava_planets = bhava_chart.get('planets') if isinstance(bhava_chart.get('planets'), dict) else {}
@@ -13977,7 +13978,12 @@ def cmd_kp(args):
'house': bhava_row.get('bhava_house', pdata.get('house', 1)),
}
result = calc_kp_analysis(planet_positions, asc_sign, house_cusps=boundaries.get('cusps'))
result = calc_kp_analysis(
planet_positions,
asc_sign,
house_cusps=boundaries.get('cusps'),
house_occupancy_basis='cusp_to_cusp',
)
moon_lon = planets.get('Moon', {}).get('degree_raw', planets.get('Moon', {}).get('degree', 0.0)) if isinstance(planets.get('Moon'), dict) else 0.0
try:
birth_moment = datetime(int(args.year), int(args.month), int(args.day), int(args.hour), int(getattr(args, 'minute', 0) or 0))
+7
View File
@@ -371,6 +371,7 @@ def calc_kp_analysis(
planet_positions: Dict,
asc_sign: str = 'Aries',
house_cusps: Optional[List[float]] = None,
house_occupancy_basis: Optional[str] = None,
) -> Dict:
"""
完整KP分析(基于 diliprk/VedicAstro MIT 算法)。
@@ -379,6 +380,7 @@ def calc_kp_analysis(
planet_positions: 行星位置 {planet: {'sign': str, 'degree': float, 'house': int}}
asc_sign: 上升星座名称(无显式宫头时用于 whole-sign 代理)
house_cusps: 可选的 12 个实际宫头黄经。提供时优先用于 KP 宫头与显著星。
house_occupancy_basis: 传了宫头就必须声明判宫口径;没传宫头时记 whole_sign_proxy。
Returns:
完整KP分析结果
@@ -405,8 +407,11 @@ def calc_kp_analysis(
}
explicit_cusps = house_cusps is not None
if explicit_cusps and not house_occupancy_basis:
raise ValueError('house_occupancy_basis is required when house_cusps are provided')
if explicit_cusps and len(house_cusps) != 12:
raise ValueError('house_cusps must contain exactly 12 longitudes')
occupancy_basis = house_occupancy_basis if explicit_cusps else 'whole_sign_proxy'
# 2. 构建宫位信息(含 KP lords)。无实际宫头时保留历史 whole-sign 中点代理。
houses = []
@@ -438,6 +443,7 @@ def calc_kp_analysis(
'source': 'diliprk/VedicAstro MIT License',
'maturity_profile': kp_maturity_profile(),
'house_basis': 'explicit_cusps' if explicit_cusps else 'whole_sign_proxy',
'house_occupancy_basis': occupancy_basis,
'planets': {pname: {'kp_lords': data['kp_lords'], 'significators': planet_sig.get(pname, {})}
for pname, data in kp_planets.items()},
'houses': {h['house']: {'sign': h['sign'], 'cusp_longitude': h['cusp_longitude'], 'kp_lords': h['kp_lords'], 'significators': house_sig.get(h['house'], {})}
@@ -494,6 +500,7 @@ def build_kp_report_pack(
f"- claim_status: `{maturity.get('claim_status')}`",
f"- truth_matrix_allowed: `{maturity.get('truth_matrix_allowed')}`",
f"- closed_oracle_parity_assets: `{', '.join(closed_parity_assets.keys())}`",
f"- House occupancy basis: `{kp_analysis.get('house_occupancy_basis') or 'whole_sign_proxy'}`",
f"- planet_count: `{len(planets)}`",
f"- house_count: `{len(houses)}`",
f"- dba_period_count: `{len(periods)}`",
+1
View File
@@ -2140,6 +2140,7 @@ def render_pl9_parity_markdown(packet: dict) -> str:
'### p123 KP Chart / Cusps',
'',
f"- House basis:{_cell(kp.get('house_basis'))}",
f"- House occupancy basis:{_cell(kp.get('house_occupancy_basis'))}",
'',
'| House | Sign | Cusp longitude | KP lord chain |',
'|-------|------|----------------|---------------|',