UNPKG

astrology-insights

Version:

Comprehensive Vedic astrology engine for Node.js — Panchang, birth charts (Kundli), Vimshottari Dasha, divisional charts, dosha analysis, and planetary remedies. Swiss Ephemeris precision, validated against Drik Panchang.

300 lines (299 loc) 11.9 kB
"use strict"; /** * Shad Bala — Six-fold Planetary Strength * * Measures a planet's ability to deliver results through 6 components: * 1. Sthana Bala (Positional strength) * 2. Dig Bala (Directional strength) * 3. Kala Bala (Temporal strength) * 4. Chesta Bala (Motional strength) * 5. Naisargika Bala (Natural strength — fixed) * 6. Drik Bala (Aspectual strength) * * Simplification note: * Full Shad Bala calculation requires very precise astronomical data. * This implementation uses simplified but correct formulas that give * directionally accurate results. For professional-grade accuracy, * Swiss Ephemeris planetary speeds and house cusps should be used. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateShadBala = void 0; const constants_1 = require("../core/constants"); // ── Naisargika Bala (Natural Strength — fixed values) ──────────────────────── const NAISARGIKA_BALA = { Sun: 60, Moon: 51.43, Mars: 17.14, Mercury: 25.71, Jupiter: 34.29, Venus: 42.86, Saturn: 8.57, Rahu: 8, Ketu: 8, }; // ── Required minimums (Ishta Phala thresholds) ────────────────────────────── const REQUIRED_STRENGTH = { Sun: 390, Moon: 360, Mars: 300, Mercury: 420, Jupiter: 390, Venus: 330, Saturn: 300, Rahu: 250, Ketu: 250, }; // ── Dig Bala: strongest house for each planet ──────────────────────────────── const DIG_BALA_STRONGEST_HOUSE = { Jupiter: 1, Mercury: 1, Sun: 10, Mars: 10, Saturn: 7, Moon: 4, Venus: 4, Rahu: 10, Ketu: 4, // approximate }; // ── Helpers ────────────────────────────────────────────────────────────────── /** Minimum distance between two house numbers on a 1-12 wheel. */ function houseDistance(from, to) { const diff = Math.abs(from - to); return Math.min(diff, 12 - diff); } /** Convert absolute longitude from sign+degree to 0-360. */ function absoluteLongitude(signNumber, degreeInSign) { return (signNumber - 1) * 30 + degreeInSign; } // ── 1. Sthana Bala (Positional Strength) ──────────────────────────────────── function calcUchchaBala(name, signNumber, degreeInSign) { const exalt = constants_1.EXALTATION_TABLE[name]; if (!exalt) return 0; const planetLon = absoluteLongitude(signNumber, degreeInSign); const exaltLon = (exalt.sign - 1) * 30 + exalt.peakDegree; let distance = Math.abs(planetLon - exaltLon); if (distance > 180) distance = 360 - distance; return 60 * (1 - distance / 180); } function calcSaptavargajaBala(dignity) { switch (dignity) { case 'own_sign': return 30; case 'peak_exalted': case 'exalted': return 25; case 'moolatrikona': return 20; // friendly is not a current Dignity enum value, but for future extensibility case 'friendly': return 15; case 'neutral': return 10; case 'enemy': return 5; case 'peak_debilitated': case 'debilitated': return 0; default: return 10; } } function calcOjayugmarasiBala(name, signNumber) { const isOddSign = signNumber % 2 === 1; const malePlanets = ['Sun', 'Mars', 'Jupiter']; const femalePlanets = ['Moon', 'Venus']; if (name === 'Mercury') return 7.5; if (malePlanets.includes(name) && isOddSign) return 15; if (femalePlanets.includes(name) && !isOddSign) return 15; return 0; } function calcKendradiBala(house) { const angular = [1, 4, 7, 10]; const succedent = [2, 5, 8, 11]; // cadent = [3, 6, 9, 12] if (angular.includes(house)) return 60; if (succedent.includes(house)) return 30; return 15; } function calcDrekkanaBala(name, degreeInSign) { const malePlanets = ['Sun', 'Mars', 'Jupiter']; const femalePlanets = ['Moon', 'Venus']; const drekkana = degreeInSign < 10 ? 1 : degreeInSign < 20 ? 2 : 3; if (malePlanets.includes(name) && drekkana === 1) return 15; if (femalePlanets.includes(name) && drekkana === 2) return 15; if (name === 'Mercury' && drekkana === 3) return 15; // Saturn — traditionally neutral/male, assign 1st drekkana if (name === 'Saturn' && drekkana === 1) return 15; // Rahu/Ketu — approximate as neutral if ((name === 'Rahu' || name === 'Ketu') && drekkana === 3) return 15; return 0; } /** * Sthana Bala — sum of 5 sub-components, normalized to 0-100. * Raw max is approximately 60+30+15+60+15 = 180. */ function calcSthanaBala(name, signNumber, degreeInSign, house, dignity) { const uchcha = calcUchchaBala(name, signNumber, degreeInSign); const saptavargaja = calcSaptavargajaBala(dignity); const ojayugma = calcOjayugmarasiBala(name, signNumber); const kendradi = calcKendradiBala(house); const drekkana = calcDrekkanaBala(name, degreeInSign); const raw = uchcha + saptavargaja + ojayugma + kendradi + drekkana; const maxRaw = 180; // theoretical max return Math.round((raw / maxRaw) * 100 * 100) / 100; } // ── 2. Dig Bala (Directional Strength) ────────────────────────────────────── function calcDigBala(name, house) { const strongestHouse = DIG_BALA_STRONGEST_HOUSE[name]; if (strongestHouse === undefined) return 0; const dist = houseDistance(house, strongestHouse); // Max distance on a 12-house wheel is 6 return Math.round(60 * (1 - dist / 6) * 100) / 100; } // ── 3. Kala Bala (Temporal Strength) — simplified ─────────────────────────── function calcKalaBala(name, birthHour) { // Default to day-born if no hour provided const hour = birthHour ?? 12; const isDayBorn = hour >= 6 && hour < 18; const dayPlanets = ['Sun', 'Jupiter', 'Venus']; const nightPlanets = ['Moon', 'Mars', 'Saturn']; if (name === 'Mercury') return 30; // neutral if (name === 'Rahu') return isDayBorn ? 20 : 40; if (name === 'Ketu') return isDayBorn ? 40 : 20; if (isDayBorn && dayPlanets.includes(name)) return 60; if (!isDayBorn && nightPlanets.includes(name)) return 60; // Opposite time — reduced strength if (isDayBorn && nightPlanets.includes(name)) return 10; if (!isDayBorn && dayPlanets.includes(name)) return 10; return 30; } // ── 4. Chesta Bala (Motional Strength) ────────────────────────────────────── function calcChestaBala(name, retrograde, speed) { // Sun and Moon never retrograde — fixed value if (name === 'Sun' || name === 'Moon') return 30; // Rahu/Ketu are always retrograde technically — fixed if (name === 'Rahu' || name === 'Ketu') return 30; if (retrograde) return 60; // Speed-based scoring for direct-motion planets const absSpeed = Math.abs(speed); if (absSpeed > 1.0) return 45; // fast if (absSpeed > 0.3) return 30; // average return 15; // slow } // ── 5. Naisargika Bala ────────────────────────────────────────────────────── function calcNaisargikaBala(name) { return NAISARGIKA_BALA[name] ?? 0; } // ── 6. Drik Bala (Aspectual Strength) — simplified ────────────────────────── const BENEFICS = ['Jupiter', 'Venus', 'Mercury']; const MALEFICS = ['Saturn', 'Mars', 'Sun', 'Rahu', 'Ketu']; // Moon is benefic when waxing — simplified: treat as mildly benefic function calcDrikBala(name, house, allPlanets) { let score = 30; // start at midpoint for (const other of allPlanets) { if (other.name === name) continue; // Check if other planet aspects this planet's house. // Simplified: all planets aspect the 7th house from their position. // Special aspects: Mars (4,8), Jupiter (5,9), Saturn (3,10). const aspectedHouses = getAspectedHouses(other.name, other.house); if (aspectedHouses.includes(house)) { if (BENEFICS.includes(other.name)) { score += 10; } else if (MALEFICS.includes(other.name)) { score -= 10; } else if (other.name === 'Moon') { // Moon — mildly benefic score += 5; } } } return Math.max(0, Math.min(60, Math.round(score * 100) / 100)); } /** Return the house numbers (1-12) that a planet aspects from its house. */ function getAspectedHouses(planetName, fromHouse) { const houses = []; // All planets aspect 7th from their position houses.push(wrapHouse(fromHouse + 6)); switch (planetName) { case 'Mars': houses.push(wrapHouse(fromHouse + 3)); // 4th aspect houses.push(wrapHouse(fromHouse + 7)); // 8th aspect break; case 'Jupiter': houses.push(wrapHouse(fromHouse + 4)); // 5th aspect houses.push(wrapHouse(fromHouse + 8)); // 9th aspect break; case 'Saturn': houses.push(wrapHouse(fromHouse + 2)); // 3rd aspect houses.push(wrapHouse(fromHouse + 9)); // 10th aspect break; case 'Rahu': houses.push(wrapHouse(fromHouse + 4)); // 5th aspect houses.push(wrapHouse(fromHouse + 8)); // 9th aspect break; case 'Ketu': houses.push(wrapHouse(fromHouse + 4)); // 5th aspect houses.push(wrapHouse(fromHouse + 8)); // 9th aspect break; } return houses; } /** Wrap house number to 1-12 range. */ function wrapHouse(h) { return ((h - 1) % 12 + 12) % 12 + 1; } // ── Main Calculation ──────────────────────────────────────────────────────── /** * Calculate Shad Bala (six-fold planetary strength) for all planets. * * @param planets - Array of planet positions with house, dignity, speed info * @param lagnaSignNumber - Sign number (1-12) of the ascendant * @param birthHour - Hour of birth (0-23) for Kala Bala; defaults to 12 (day) */ function calculateShadBala(planets, lagnaSignNumber, birthHour) { return planets.map((p) => { const sthana = calcSthanaBala(p.name, p.signNumber, p.degreeInSign, p.house, p.dignity); const dig = calcDigBala(p.name, p.house); const kala = calcKalaBala(p.name, birthHour); const chesta = calcChestaBala(p.name, p.retrograde, p.speed); const naisargika = calcNaisargikaBala(p.name); const drik = calcDrikBala(p.name, p.house, planets); const total = Math.round((sthana + dig + kala + chesta + naisargika + drik) * 100) / 100; const required = REQUIRED_STRENGTH[p.name] ?? 300; const ratio = Math.round((total / required) * 100) / 100; return { planet: p.name, sthana, dig, kala, chesta, naisargika, drik, total, required, ratio, isStrong: ratio >= 1, }; }); } exports.calculateShadBala = calculateShadBala;