jyotish-calc 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Strength Calculation Utilities
3
+ * Helper functions for Shadbala calculations
4
+ */
5
+
6
+ const {
7
+ SUN, MOON, MARS, MERCURY, JUPITER, VENUS, SATURN,
8
+ ODD_SIGNS, EVEN_SIGNS, HOUSE_OWNERS,
9
+ FRIENDLY_PLANETS, ENEMY_PLANETS, NEUTRAL_PLANETS,
10
+ HOUSE_STRENGTHS, COMPOUND_PLANET_RELATIONS,
11
+ _ADHISATHRU_GREATENEMY, _SATHRU_ENEMY, _SAMAM_NEUTRAL,
12
+ _MITHRA_FRIEND, _ADHIMITRA_GREATFRIEND, _OWNER_RULER, _EXALTED_UCCHAM
13
+ } = require('./constants');
14
+
15
+ // ============================================================================
16
+ // BASIC UTILITIES
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Normalize angle to 0-360 range
21
+ */
22
+ function normalizeAngle(angle) {
23
+ return ((angle % 360) + 360) % 360;
24
+ }
25
+
26
+ /**
27
+ * Get sign index (0-11) from longitude
28
+ */
29
+ function getSignFromLongitude(longitude) {
30
+ return Math.floor(normalizeAngle(longitude) / 30);
31
+ }
32
+
33
+ /**
34
+ * Get degree within sign
35
+ */
36
+ function getDegreeInSign(longitude) {
37
+ return normalizeAngle(longitude) % 30;
38
+ }
39
+
40
+ /**
41
+ * Check if sign is odd (masculine)
42
+ */
43
+ function isOddSign(signIndex) {
44
+ return ODD_SIGNS.includes(signIndex % 12);
45
+ }
46
+
47
+ /**
48
+ * Check if sign is even (feminine)
49
+ */
50
+ function isEvenSign(signIndex) {
51
+ return EVEN_SIGNS.includes(signIndex % 12);
52
+ }
53
+
54
+ /**
55
+ * Get house owner (planet) for a sign
56
+ */
57
+ function getHouseOwner(signIndex) {
58
+ return HOUSE_OWNERS[signIndex % 12];
59
+ }
60
+
61
+ /**
62
+ * Calculate Julian Day Number from date/time
63
+ * @param {Array} date - [year, month, day]
64
+ * @param {Array} time - [hour, minute, second]
65
+ * @returns {number} Julian Day Number
66
+ */
67
+ function julianDayNumber(date, time = [0, 0, 0]) {
68
+ const [year, month, day] = date;
69
+ const [hour, minute, second] = time;
70
+
71
+ let y = year;
72
+ let m = month;
73
+
74
+ if (m <= 2) {
75
+ y -= 1;
76
+ m += 12;
77
+ }
78
+
79
+ const A = Math.floor(y / 100);
80
+ const B = 2 - A + Math.floor(A / 4);
81
+
82
+ const jd = Math.floor(365.25 * (y + 4716)) +
83
+ Math.floor(30.6001 * (m + 1)) +
84
+ day + B - 1524.5;
85
+
86
+ const timeDecimal = hour + minute / 60 + second / 3600;
87
+ return jd + timeDecimal / 24;
88
+ }
89
+
90
+ /**
91
+ * Convert Julian Day to Gregorian date
92
+ * @param {number} jd - Julian Day Number
93
+ * @returns {Array} [year, month, day, hourDecimal]
94
+ */
95
+ function jdToGregorian(jd) {
96
+ const Z = Math.floor(jd + 0.5);
97
+ const F = jd + 0.5 - Z;
98
+
99
+ let A;
100
+ if (Z < 2299161) {
101
+ A = Z;
102
+ } else {
103
+ const alpha = Math.floor((Z - 1867216.25) / 36524.25);
104
+ A = Z + 1 + alpha - Math.floor(alpha / 4);
105
+ }
106
+
107
+ const B = A + 1524;
108
+ const C = Math.floor((B - 122.1) / 365.25);
109
+ const D = Math.floor(365.25 * C);
110
+ const E = Math.floor((B - D) / 30.6001);
111
+
112
+ const day = B - D - Math.floor(30.6001 * E);
113
+ const month = E < 14 ? E - 1 : E - 13;
114
+ const year = month > 2 ? C - 4716 : C - 4715;
115
+ const hourDecimal = F * 24;
116
+
117
+ return [year, month, day, hourDecimal];
118
+ }
119
+
120
+ /**
121
+ * Check if year is a leap year
122
+ */
123
+ function isLeapYear(year) {
124
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
125
+ }
126
+
127
+ /**
128
+ * Get day of week (0=Sunday to 6=Saturday)
129
+ */
130
+ function getDayOfWeek(jd) {
131
+ return Math.floor(jd + 1.5) % 7;
132
+ }
133
+
134
+ /**
135
+ * Get Vaara (weekday lord index: 0=Sun, 1=Moon, etc.)
136
+ */
137
+ function getVaara(jd) {
138
+ const dayOfWeek = getDayOfWeek(jd);
139
+ // Sunday=0->Sun, Monday=1->Moon, Tuesday=2->Mars, etc.
140
+ const vaaraOrder = [SUN, MOON, MARS, MERCURY, JUPITER, VENUS, SATURN];
141
+ return vaaraOrder[dayOfWeek];
142
+ }
143
+
144
+ // ============================================================================
145
+ // RELATIONSHIP UTILITIES
146
+ // ============================================================================
147
+
148
+ /**
149
+ * Get natural relationship between two planets
150
+ * @returns {number} Relationship constant
151
+ */
152
+ function getNaturalRelationship(planet1, planet2) {
153
+ if (planet1 === planet2) return _OWNER_RULER;
154
+ if (FRIENDLY_PLANETS[planet1]?.includes(planet2)) return _MITHRA_FRIEND;
155
+ if (ENEMY_PLANETS[planet1]?.includes(planet2)) return _SATHRU_ENEMY;
156
+ return _SAMAM_NEUTRAL;
157
+ }
158
+
159
+ /**
160
+ * Get compound relationship (considering temporal)
161
+ */
162
+ function getCompoundRelationship(planet1, planet2) {
163
+ if (planet1 >= 7 || planet2 >= 7) return _SAMAM_NEUTRAL; // Rahu/Ketu
164
+ return COMPOUND_PLANET_RELATIONS[planet1][planet2];
165
+ }
166
+
167
+ /**
168
+ * Get planet's strength in a sign
169
+ */
170
+ function getPlanetStrengthInSign(planet, signIndex) {
171
+ if (planet >= 7) return _SAMAM_NEUTRAL; // Rahu/Ketu
172
+ return HOUSE_STRENGTHS[planet][signIndex % 12];
173
+ }
174
+
175
+ // ============================================================================
176
+ // KENDRA/PANAPARA/APOKLIMA
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Get Kendra houses from Ascendant
181
+ */
182
+ function getKendras(ascHouse) {
183
+ return [1, 4, 7, 10].map(h => (ascHouse + h - 1) % 12);
184
+ }
185
+
186
+ /**
187
+ * Get Panapara houses from Ascendant
188
+ */
189
+ function getPanapharas(ascHouse) {
190
+ return [2, 5, 8, 11].map(h => (ascHouse + h - 1) % 12);
191
+ }
192
+
193
+ /**
194
+ * Get Apoklima houses from Ascendant
195
+ */
196
+ function getApoklimas(ascHouse) {
197
+ return [3, 6, 9, 12].map(h => (ascHouse + h - 1) % 12);
198
+ }
199
+
200
+ // ============================================================================
201
+ // PLANET POSITION UTILITIES
202
+ // ============================================================================
203
+
204
+ /**
205
+ * Convert planet positions array to planet-to-house dictionary
206
+ * Planet positions format: [[planet, [sign, degree]], ...]
207
+ * @returns {Object} {planet: sign, ...}
208
+ */
209
+ function getPlanetHouseDictionary(planetPositions) {
210
+ const dict = {};
211
+ for (const [planet, [sign, degree]] of planetPositions) {
212
+ dict[planet] = sign;
213
+ }
214
+ return dict;
215
+ }
216
+
217
+ /**
218
+ * Convert planet positions to absolute longitudes
219
+ * @returns {Array} [longitude, ...]
220
+ */
221
+ function getAbsoluteLongitudes(planetPositions) {
222
+ const longs = [];
223
+ for (const [planet, [sign, degree]] of planetPositions) {
224
+ longs.push(sign * 30 + degree);
225
+ }
226
+ return longs;
227
+ }
228
+
229
+ /**
230
+ * Get planet's absolute longitude from position
231
+ */
232
+ function getAbsoluteLongitude(sign, degree) {
233
+ return sign * 30 + degree;
234
+ }
235
+
236
+ // ============================================================================
237
+ // TIME UTILITIES
238
+ // ============================================================================
239
+
240
+ /**
241
+ * Calculate approximate sunrise time (simplified)
242
+ * @param {number} jd - Julian Day
243
+ * @param {number} latitude - Latitude in degrees
244
+ * @returns {number} Sunrise time in decimal hours
245
+ */
246
+ function getSunrise(jd, latitude) {
247
+ // Simplified sunrise calculation (average ~6 AM)
248
+ // In a real implementation, use Swiss Ephemeris
249
+ const dayOfYear = Math.floor(jd % 365.25);
250
+ const declination = 23.45 * Math.sin((360 / 365) * (dayOfYear - 81) * Math.PI / 180);
251
+ const hourAngle = Math.acos(-Math.tan(latitude * Math.PI / 180) * Math.tan(declination * Math.PI / 180));
252
+ const sunrise = 12 - (hourAngle * 180 / Math.PI) / 15;
253
+ return Math.max(4, Math.min(8, sunrise)); // Clamp between 4-8 AM
254
+ }
255
+
256
+ /**
257
+ * Calculate approximate sunset time (simplified)
258
+ */
259
+ function getSunset(jd, latitude) {
260
+ const sunrise = getSunrise(jd, latitude);
261
+ const dayLength = 24 - 2 * sunrise; // Approximate
262
+ return sunrise + dayLength;
263
+ }
264
+
265
+ /**
266
+ * Calculate midnight time
267
+ */
268
+ function getMidnight(jd, place) {
269
+ return 0; // Midnight is 0 hours
270
+ }
271
+
272
+ /**
273
+ * Get tithi (lunar day) 1-30
274
+ */
275
+ function getTithi(sunLong, moonLong) {
276
+ const diff = normalizeAngle(moonLong - sunLong);
277
+ return Math.floor(diff / 12) + 1;
278
+ }
279
+
280
+ // ============================================================================
281
+ // EPOCH CALCULATIONS (for Chesta Bala)
282
+ // ============================================================================
283
+
284
+ const JD_EPOCH = julianDayNumber([1900, 1, 1], [0, 0, 0]);
285
+
286
+ /**
287
+ * Get days from epoch adjusted for longitude
288
+ */
289
+ function getDaysFromEpoch(jd, longitude = 76) {
290
+ return jd - JD_EPOCH + (76 - longitude) / 15 / 24;
291
+ }
292
+
293
+ /**
294
+ * Calculate days elapsed since base year
295
+ */
296
+ function daysElapsedSinceBase(year, baseYear = 1951, baseDays = 174) {
297
+ const totalYears = year - baseYear;
298
+
299
+ let leapYears = 0;
300
+ for (let y = baseYear + 1; y <= year; y++) {
301
+ if (isLeapYear(y)) leapYears++;
302
+ }
303
+
304
+ const nonLeapYears = totalYears - leapYears;
305
+ return baseDays + (leapYears * 366) + (nonLeapYears * 365);
306
+ }
307
+
308
+ module.exports = {
309
+ // Basic utilities
310
+ normalizeAngle,
311
+ getSignFromLongitude,
312
+ getDegreeInSign,
313
+ isOddSign,
314
+ isEvenSign,
315
+ getHouseOwner,
316
+
317
+ // Julian Day
318
+ julianDayNumber,
319
+ jdToGregorian,
320
+ isLeapYear,
321
+ getDayOfWeek,
322
+ getVaara,
323
+
324
+ // Relationships
325
+ getNaturalRelationship,
326
+ getCompoundRelationship,
327
+ getPlanetStrengthInSign,
328
+
329
+ // Kendras
330
+ getKendras,
331
+ getPanapharas,
332
+ getApoklimas,
333
+
334
+ // Planet positions
335
+ getPlanetHouseDictionary,
336
+ getAbsoluteLongitudes,
337
+ getAbsoluteLongitude,
338
+
339
+ // Time
340
+ getSunrise,
341
+ getSunset,
342
+ getMidnight,
343
+ getTithi,
344
+
345
+ // Epoch
346
+ JD_EPOCH,
347
+ getDaysFromEpoch,
348
+ daysElapsedSinceBase
349
+ };
@@ -0,0 +1,58 @@
1
+ const checkFields = (birthDetails) => {
2
+ ['dateString', 'timeString', 'lat', 'lng', 'timezone'].forEach(field => {
3
+ if (!birthDetails.hasOwnProperty(field)) throw new Error(`Please give valid birthDetails fields. Missing ${field} in the given birthDetails`);
4
+ })
5
+ }
6
+
7
+ const getValidatedDate = (dateString) => {
8
+
9
+ // TODO
10
+ // check if the date is correct logically, like if date is greater the number of days of a month or if month number is greater than 12
11
+
12
+ let split = dateString.split('-');
13
+ if (split.length !== 3) throw new Error(`Please enter valid dateString. Expected: dd-mm-yyyy. Given ${dateString}`);
14
+ split = split.map(e => {
15
+ if (isNaN(Number(e))) {
16
+ throw new Error(`Please enter valid dateString. date, month, year should be valid numbers. Converting to Number has resulted into error`);
17
+ }
18
+ return Number(e);
19
+ });
20
+ return {
21
+ year: split[0],
22
+ month: split[1],
23
+ date: split[2],
24
+ };
25
+ };
26
+
27
+ getValidatedTime = (timeString) => {
28
+ let split = timeString.split(':');
29
+ if (split.length !== 3) throw new Error('Please enter timeString in a valid format. Required format HH:MM:SS');
30
+ split = split.map(e => {
31
+ if (isNaN(Number(e))) {
32
+ throw new Error(`Please enter valid timeString. hour, minutes, seconds should be valid numbers. Converting to Number has resulted into error`);
33
+ }
34
+ return Number(e);
35
+ });
36
+ return {
37
+ hour: split[0],
38
+ min: split[1],
39
+ sec: split[2]
40
+ };
41
+ };
42
+
43
+ exports.getValidatedBirthDetails = (birthDetails) => {
44
+
45
+ if (birthDetails === undefined) throw new Error('Please enter birthDetails as the first argument. Entered undefined');
46
+ if (birthDetails.constructor.name !== 'Object') throw new Error(`Data type of the birthDetails has to be object. Entered ${birthDetails.constructor.name}`);
47
+ checkFields(birthDetails);
48
+
49
+ let {date, month, year} = getValidatedDate(birthDetails.dateString);
50
+ let {hour, min, sec} = getValidatedTime(birthDetails.timeString);
51
+
52
+ let [lat, lng, timezone] = ['lat', 'lng', 'timezone'].map(field => {
53
+ if (isNaN(Number(birthDetails[field]))) throw new Error(`${field} should be a valid Number. Given is not a valid number`);
54
+ return Number(birthDetails[field]);
55
+ });
56
+
57
+ return {year, month, date, hour, min, sec, lat, lng, timezone};
58
+ };
@@ -0,0 +1,45 @@
1
+ const getValidatedDegree = (degree) => {
2
+ if (!(typeof degree === 'number' || typeof degree === 'string')) throw new TypeError(`Degree should be either of the type Number or String. Given ${degree.constructor.name}`);
3
+
4
+ if(isNaN(Number(degree))) throw new TypeError(`Degree should a valid Number in either Number data type or Number String. Given degree is Not A Number After Converting To Number Data Type. Entered ${degree}`);
5
+
6
+ return Number(degree);
7
+ };
8
+
9
+ const getValidatedDMS = (DMS) => {
10
+ if (!(DMS.length >= 1 && DMS.length <= 3)) throw new Error(`Array should be of length in between 1 and 3. Entered the Array of the size ${DMS.length}`);
11
+ DMS = DMS.map(unit => getValidatedDegree(unit));
12
+ for (let i = DMS.length; i < 3; i++) {DMS.push(0)};
13
+ return DMS;
14
+ };
15
+
16
+ const getBoundDegree = (degree) => {
17
+ degree = getValidatedDegree(degree);
18
+ if (degree >= 0 && degree < 360) return degree;
19
+ if (degree >= 360) return getBoundDegree(degree -360);
20
+ if (degree < 0) return getBoundDegree(degree + 360);
21
+ };
22
+
23
+ const convertDMSToSec = (DMS) => {
24
+ DMS = getValidatedDMS(DMS);
25
+
26
+ return (DMS[0] * 3600 + DMS[1] * 60 + DMS[2]);
27
+ };
28
+
29
+ const convertDegreeToDMS = (degree) => {
30
+ degree = getValidatedDegree(degree);
31
+
32
+ let D = parseInt(degree);
33
+ let M = ((degree - D) * 60).toFixed(5);
34
+ let S = parseInt((M - parseInt(M)) * 60);
35
+
36
+ return [D, parseInt(M), S];
37
+ };
38
+
39
+ module.exports = {
40
+ getValidatedDegree,
41
+ getValidatedDMS,
42
+ getBoundDegree,
43
+ convertDMSToSec,
44
+ convertDegreeToDMS
45
+ }
@@ -0,0 +1,12 @@
1
+ const birthDetails = require('./birthDetails');
2
+ const sampleData = require('./sampleData');
3
+ const degree = require('./degree');
4
+
5
+
6
+
7
+
8
+ module.exports = {
9
+ birthDetails,
10
+ sampleData,
11
+ degree
12
+ };
@@ -0,0 +1,151 @@
1
+ const sampleData = {
2
+ birthDetails: {
3
+ dateString: '2020-05-05',
4
+ timeString: '15:05:05',
5
+ lat: 25,
6
+ lng: 70,
7
+ timezone: 5
8
+ },
9
+ birthDetailsExpanded: {
10
+ year: 2020,
11
+ month: 5,
12
+ date: 5,
13
+ hour: 15,
14
+ min: 5,
15
+ sec: 5,
16
+ lat: 25,
17
+ lng: 70,
18
+ timezone: 5
19
+ },
20
+ grahasPosition: {
21
+
22
+ Su: {
23
+ longitude: 21.237012359466505,
24
+ latitude: 0.0002053518174398051,
25
+ distance: 1.0086712894247367,
26
+ longitudeSpeed: 0.9681894317387951,
27
+ latitudeSpeed: -0.000013474527710551535,
28
+ distanceSpeed: 0.00024049507863760575,
29
+ rflag: 65860,
30
+ graha: 'Su',
31
+ isRetrograde: false,
32
+ nakshatra: { name: 'Bharani', pada: 3 },
33
+ rashi: 'Ar'
34
+ },
35
+
36
+ Mo: {
37
+ longitude: 172.67779396805847,
38
+ latitude: 4.91636672386553,
39
+ distance: 0.002407098560876014,
40
+ longitudeSpeed: 15.063054407651258,
41
+ latitudeSpeed: -0.3891854991554736,
42
+ distanceSpeed: -0.000008041526872714209,
43
+ rflag: 65860,
44
+ graha: 'Mo',
45
+ isRetrograde: false,
46
+ nakshatra: { name: 'Hasta', pada: 4 },
47
+ rashi: 'Vi'
48
+ },
49
+
50
+ Me: {
51
+ longitude: 21.85436370307817,
52
+ latitude: -0.0171462407503486,
53
+ distance: 1.3230109515151005,
54
+ longitudeSpeed: 2.1638897298760895,
55
+ latitudeSpeed: 0.17597061835688793,
56
+ distanceSpeed: -0.003596676476410021,
57
+ rflag: 65860,
58
+ graha: 'Me',
59
+ isRetrograde: false,
60
+ nakshatra: { name: 'Bharani', pada: 3 },
61
+ rashi: 'Ar'
62
+ },
63
+
64
+ Ve: {
65
+ longitude: 56.54951254515609,
66
+ latitude: 4.710463974371201,
67
+ distance: 0.3998012994174667,
68
+ longitudeSpeed: 0.28519805571765716,
69
+ latitudeSpeed: -0.033488853010295506,
70
+ distanceSpeed: -0.00645730954136935,
71
+ rflag: 65860,
72
+ graha: 'Ve',
73
+ isRetrograde: false,
74
+ nakshatra: { name: 'Mrigashira', pada: 1 },
75
+ rashi: 'Ta'
76
+ },
77
+
78
+ Ma: {
79
+ longitude: 300.54297026521533,
80
+ latitude: -1.7243806578193743,
81
+ distance: 1.1962657174820874,
82
+ longitudeSpeed: 0.6878323992243554,
83
+ latitudeSpeed: -0.02360790483291124,
84
+ distanceSpeed: -0.007328211892773828,
85
+ rflag: 65860,
86
+ graha: 'Ma',
87
+ isRetrograde: false,
88
+ nakshatra: { name: 'Dhanishta', pada: 3 },
89
+ rashi: 'Aq'
90
+ },
91
+
92
+ Ju: {
93
+ longitude: 272.97133481664474,
94
+ latitude: -0.14210814568443875,
95
+ distance: 4.776027652017015,
96
+ longitudeSpeed: 0.02873838747747352,
97
+ latitudeSpeed: -0.0024994888986249906,
98
+ distanceSpeed: -0.015225342337813013,
99
+ rflag: 65860,
100
+ graha: 'Ju',
101
+ isRetrograde: false,
102
+ nakshatra: { name: 'Uttara Aashada', pada: 2 },
103
+ rashi: 'Cp'
104
+ },
105
+
106
+ Sa: {
107
+ longitude: 277.7940907686863,
108
+ latitude: -0.11115634321854116,
109
+ distance: 9.737752956169963,
110
+ longitudeSpeed: 0.009263651156424007,
111
+ latitudeSpeed: -0.0015380117980703118,
112
+ distanceSpeed: -0.01623744432683592,
113
+ rflag: 65860,
114
+ graha: 'Sa',
115
+ isRetrograde: false,
116
+ nakshatra: { name: 'Uttara Aashada', pada: 4 },
117
+ rashi: 'Cp'
118
+ },
119
+
120
+ Ra: {
121
+ longitude: 67.46107585537935,
122
+ latitude: 2.417547070067896e-15,
123
+ distance: 0.0025695552897999907,
124
+ longitudeSpeed: -0.052992017997940524,
125
+ latitudeSpeed: 1.818760271175542e-51,
126
+ distanceSpeed: 3.4416214492528266e-72,
127
+ rflag: 65858,
128
+ graha: 'Ra',
129
+ isRetrograde: true,
130
+ nakshatra: { name: 'Ardra', pada: 1 },
131
+ rashi: 'Ge'
132
+ },
133
+
134
+ Ke: {
135
+ graha: 'Ke',
136
+ isRetrograde: true,
137
+ longitude: 247.46107585537936,
138
+ nakshatra: { name: 'Mula', pada: 3 },
139
+ rashi: 'Sg'
140
+ },
141
+
142
+ La: {
143
+ graha: 'La',
144
+ longitude: 151.33717452637387,
145
+ isRetrograde: true,
146
+ nakshatra: { name: 'Uttara Phalguni', pada: 2 },
147
+ rashi: 'Vi' }
148
+ }
149
+ }
150
+
151
+ module.exports = sampleData;