diff --git a/README.md b/README.md index 17157b4d..4b089fa9 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/) -[![Techniques](https://img.shields.io/badge/techniques-77-blueviolet)](references/technique_registry.json) -[![Covered](https://img.shields.io/badge/covered-68-green)](references/technique_registry.json) +[![Techniques](https://img.shields.io/badge/techniques-79-blueviolet)](references/technique_registry.json) +[![Covered](https://img.shields.io/badge/covered-70-green)](references/technique_registry.json) [![Complete](https://img.shields.io/badge/complete-9-brightgreen)](references/technique_registry.json) [![Partial](https://img.shields.io/badge/partial-0-lightgrey)](references/technique_registry.json) @@ -31,7 +31,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, auditable full-chart readings. It is NOT a simple ephemeris calculator — it is a multi-stage interpretive pipeline that: 1. **Computes** divisional charts (D1/D9/D10/...) via Swiss Ephemeris -2. **Runs** 77 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) +2. **Runs** 79 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) 3. **Routes** the analysis through strict workflow paths depending on question type (career / relationship / wealth / timing) 4. **Audits** every technique used — declaring what was called, what is complete/covered, and which limitations affect confidence 5. **Degrades gracefully** — limitations are labeled, not silently over-promising @@ -45,7 +45,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, audit | Technique Audit Table (confidence labeling) | ✅ | ❌ | ❌ | ❌ | | Capability degradation (limits are explicit) | ✅ | ❌ | ❌ | ❌ | | MEVG external verification gates | ✅ | ❌ | ❌ | ❌ | -| 77 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | +| 79 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | | Traditional algorithm benchmarked | ✅ mixed depth | ✅ | ✅ | ✅ | | Docker / MCP Server | ✅ | ❌ | ✅ | ❌ | | English docs / PyPI package | ✅ in progress | ✅ | ✅ | ✅ | diff --git a/references/technique_registry.json b/references/technique_registry.json index 550b38ad..b24c347f 100644 --- a/references/technique_registry.json +++ b/references/technique_registry.json @@ -554,6 +554,48 @@ "audit_label": "22nd Drekkana", "missing_impact": "A classical danger-sensitive Drekkana trigger is absent, weakening accident and adversity screening." }, + "bhrigu_bindu": { + "name": "Bhrigu Bindu", + "domains": [ + "sensitive_point", + "timing", + "destiny" + ], + "status": "covered", + "knowledge_refs": [ + "references/bhrigu-pada-dasha-marriage-counting.md", + "references/feature-gap-matrix-2026.md" + ], + "commands": [ + "scripts/varga.py" + ], + "output_paths": [ + "scripts.varga.calc_bhrigu_bindu" + ], + "audit_label": "Bhrigu Bindu", + "missing_impact": "A commonly used Moon-Rahu destiny trigger is unavailable, weakening event-sensitive reading depth." + }, + "sarpa_drekkana": { + "name": "Sarpa Drekkana", + "domains": [ + "varga", + "toxicity", + "crisis" + ], + "status": "covered", + "knowledge_refs": [ + "references/translated-articles/3印度占星_preview.txt", + "references/feature-gap-matrix-2026.md" + ], + "commands": [ + "scripts/varga.py" + ], + "output_paths": [ + "scripts.varga.calc_sarpa_drekkana" + ], + "audit_label": "Sarpa Drekkana", + "missing_impact": "A classical toxic-crisis Drekkana marker is absent, thinning advanced risk and temperament screening." + }, "bhava_chalit": { "name": "Bhava Chalit", "domains": [ diff --git a/scripts/varga.py b/scripts/varga.py index a86e393a..9f03ff47 100644 --- a/scripts/varga.py +++ b/scripts/varga.py @@ -126,6 +126,47 @@ def calc_22nd_drekkana(lagna_lon: float) -> Dict: 'lord': SIGN_LORDS.get(_sn(sign_idx), ''), } + +def calc_bhrigu_bindu(moon_lon: float, rahu_lon: float) -> Dict: + """Compute Bhrigu Bindu as the midpoint on the Rahu->Moon forward arc.""" + moon = moon_lon % 360 + rahu = rahu_lon % 360 + arc = (moon - rahu) % 360 + longitude = (rahu + arc / 2.0) % 360 + sign_idx = _si(longitude) + degree_in_sign = round(longitude - sign_idx * 30, 4) + return { + 'longitude': round(longitude, 4), + 'sign_idx': sign_idx, + 'sign': _sn(sign_idx), + 'degree_in_sign': degree_in_sign, + 'arc_mode': 'forward_rahu_to_moon', + 'lord': SIGN_LORDS.get(_sn(sign_idx), ''), + } + + +SARPA_DREKKANA_SIGNS = {'Cancer': 2, 'Scorpio': 1, 'Pisces': 3} + + +def calc_sarpa_drekkana(lon: float) -> Dict: + """Classify Sarpa Drekkana using the classical water-sign sensitive drekkanas.""" + base = calc_varga(lon, 3) + sign = _sn(_si(lon)) + degree_in_sign = round((lon % 30), 4) + drekkana_number = int(degree_in_sign / 10) + 1 + expected = SARPA_DREKKANA_SIGNS.get(sign) + return { + 'longitude': round(lon % 360, 4), + 'sign': sign, + 'sign_idx': _si(lon), + 'degree_in_sign': degree_in_sign, + 'drekkana_number': drekkana_number, + 'd3_sign': base['sign'], + 'd3_sign_idx': base['sign_idx'], + 'is_sarpa_drekkana': bool(expected == drekkana_number), + 'definition': 'Cancer-2, Scorpio-1, Pisces-3', + } + def dignity(planet, sign_idx): if planet in EXALT_SIGN and sign_idx==EXALT_SIGN[planet]: return 'Exalted' if planet in DEBIL_SIGN and sign_idx==DEBIL_SIGN[planet]: return 'Debilitated' diff --git a/tests/test_varga_bphs.py b/tests/test_varga_bphs.py index 25f81bd8..481754ec 100644 --- a/tests/test_varga_bphs.py +++ b/tests/test_varga_bphs.py @@ -7,8 +7,10 @@ from hypothesis import given from hypothesis import strategies as st from varga import ( SIGNS, + calc_bhrigu_bindu, calc_22nd_drekkana, calc_64th_navamsa, + calc_sarpa_drekkana, calc_varga, varga_map, ) @@ -106,3 +108,38 @@ def test_22nd_drekkana_counts_forward_from_lagna_drekkana() -> None: assert result["sign_idx"] == expected_sign_idx assert result["sign"] == SIGNS[expected_sign_idx] assert result["offset_from_lagna_drekkana"] == 22 + + +def test_bhrigu_bindu_uses_rahu_to_moon_arc_midpoint() -> None: + result = calc_bhrigu_bindu(270.5, 160.2333333333) + assert result["longitude"] == 215.3667 + assert result["sign"] == "Scorpio" + assert result["degree_in_sign"] == 5.3667 + + +def test_bhrigu_bindu_handles_wraparound_across_zero_aries() -> None: + result = calc_bhrigu_bindu(10.0, 350.0) + assert result["longitude"] == 0.0 + assert result["sign"] == "Aries" + assert result["degree_in_sign"] == 0.0 + + +def test_sarpa_drekkana_detects_classical_sensitive_ranges() -> None: + cancer_second = calc_sarpa_drekkana(105.0) + scorpio_first = calc_sarpa_drekkana(215.0) + pisces_third = calc_sarpa_drekkana(355.0) + safe_aries = calc_sarpa_drekkana(15.0) + + assert cancer_second["is_sarpa_drekkana"] is True + assert cancer_second["sign"] == "Cancer" + assert cancer_second["drekkana_number"] == 2 + + assert scorpio_first["is_sarpa_drekkana"] is True + assert scorpio_first["sign"] == "Scorpio" + assert scorpio_first["drekkana_number"] == 1 + + assert pisces_third["is_sarpa_drekkana"] is True + assert pisces_third["sign"] == "Pisces" + assert pisces_third["drekkana_number"] == 3 + + assert safe_aries["is_sarpa_drekkana"] is False