jyotish-calc 1.2.0 → 1.3.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.
- package/README.md +25 -0
- package/index.js +4 -1
- package/package.json +2 -2
- package/src/panchanga/constants.js +266 -0
- package/src/panchanga/index.js +330 -0
- package/src/panchanga/karana.js +185 -0
- package/src/panchanga/lunar-calendar.js +207 -0
- package/src/panchanga/muhurtas.js +370 -0
- package/src/panchanga/nakshatra.js +180 -0
- package/src/panchanga/solar-calendar.js +301 -0
- package/src/panchanga/sunrise-sunset.js +400 -0
- package/src/panchanga/tithi.js +234 -0
- package/src/panchanga/utils.js +320 -0
- package/src/panchanga/vaara.js +182 -0
- package/src/panchanga/yogam.js +196 -0
- package/get-full-results.js +0 -140
- package/test-chennai-1998.js +0 -162
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Muhurta (Auspicious/Inauspicious Time Periods)
|
|
3
|
+
* Various time divisions used in Vedic astrology
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const sunriseSunset = require('./sunrise-sunset');
|
|
7
|
+
const vaaraModule = require('./vaara');
|
|
8
|
+
const utils = require('./utils');
|
|
9
|
+
const {
|
|
10
|
+
RAAHU_KAALAM_OFFSETS, GULIKAI_OFFSETS, YAMAGANDAM_OFFSETS,
|
|
11
|
+
DURMUHURTAM_OFFSETS, GAURI_CHOGHADIYA_DAY, GAURI_CHOGHADIYA_NIGHT,
|
|
12
|
+
CHOGHADIYA_NAMES, DAY_RULERS, NIGHT_RULERS
|
|
13
|
+
} = require('./constants');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Calculate Trikalam (Raahu Kaalam, Yamagandam, Gulikai)
|
|
17
|
+
* @param {number} jd - Julian Day Number
|
|
18
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
19
|
+
* @param {string} type - 'raahu', 'yamagandam', or 'gulikai'
|
|
20
|
+
* @returns {object} {startTime, endTime, startString, endString}
|
|
21
|
+
*/
|
|
22
|
+
function trikalam(jd, place, type = 'raahu') {
|
|
23
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
24
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
25
|
+
const weekday = vaaraModule.vaara(jd).number;
|
|
26
|
+
|
|
27
|
+
if (!rise) return null;
|
|
28
|
+
|
|
29
|
+
let offsets;
|
|
30
|
+
switch (type.toLowerCase()) {
|
|
31
|
+
case 'raahu':
|
|
32
|
+
case 'raahukaalam':
|
|
33
|
+
offsets = RAAHU_KAALAM_OFFSETS;
|
|
34
|
+
break;
|
|
35
|
+
case 'gulikai':
|
|
36
|
+
case 'gulikaikaalam':
|
|
37
|
+
offsets = GULIKAI_OFFSETS;
|
|
38
|
+
break;
|
|
39
|
+
case 'yamagandam':
|
|
40
|
+
case 'yamagandakaalam':
|
|
41
|
+
offsets = YAMAGANDAM_OFFSETS;
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
offsets = RAAHU_KAALAM_OFFSETS;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const startTime = rise.hours + dayDur * offsets[weekday];
|
|
48
|
+
const endTime = startTime + 0.125 * dayDur; // 1/8 of day
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
startTime,
|
|
52
|
+
endTime,
|
|
53
|
+
startString: utils.formatTime(startTime),
|
|
54
|
+
endString: utils.formatTime(endTime),
|
|
55
|
+
duration: endTime - startTime
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Convenience functions
|
|
60
|
+
const raahuKaalam = (jd, place) => trikalam(jd, place, 'raahu');
|
|
61
|
+
const yamagandaKaalam = (jd, place) => trikalam(jd, place, 'yamagandam');
|
|
62
|
+
const gulikaiKaalam = (jd, place) => trikalam(jd, place, 'gulikai');
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Calculate Abhijit Muhurta (most auspicious - 8th muhurta around noon)
|
|
66
|
+
* @param {number} jd - Julian Day Number
|
|
67
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
68
|
+
* @returns {object} {startTime, endTime}
|
|
69
|
+
*/
|
|
70
|
+
function abhijitMuhurta(jd, place) {
|
|
71
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
72
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
73
|
+
|
|
74
|
+
if (!rise) return null;
|
|
75
|
+
|
|
76
|
+
// Abhijit = 7/15 to 8/15 of day (8th of 15 day muhurtas)
|
|
77
|
+
const startTime = rise.hours + (7 / 15) * dayDur;
|
|
78
|
+
const endTime = rise.hours + (8 / 15) * dayDur;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
startTime,
|
|
82
|
+
endTime,
|
|
83
|
+
startString: utils.formatTime(startTime),
|
|
84
|
+
endString: utils.formatTime(endTime),
|
|
85
|
+
duration: endTime - startTime
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Calculate Durmuhurtam (inauspicious muhurta)
|
|
91
|
+
* @param {number} jd - Julian Day Number
|
|
92
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
93
|
+
* @returns {array} List of durmuhurtam periods
|
|
94
|
+
*/
|
|
95
|
+
function durmuhurtam(jd, place) {
|
|
96
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
97
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
98
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
99
|
+
const nightDur = sunriseSunset.nightLength(jd, place);
|
|
100
|
+
const weekday = vaaraModule.vaara(jd).number;
|
|
101
|
+
|
|
102
|
+
if (!rise || !set) return [];
|
|
103
|
+
|
|
104
|
+
const offsets = DURMUHURTAM_OFFSETS[weekday];
|
|
105
|
+
const results = [];
|
|
106
|
+
|
|
107
|
+
// Duration of one durmuhurtam = 0.8/12 of day
|
|
108
|
+
const durDuration = dayDur * 0.8 / 12;
|
|
109
|
+
|
|
110
|
+
for (let i = 0; i < offsets.length; i++) {
|
|
111
|
+
if (offsets[i] === 0.0) continue;
|
|
112
|
+
|
|
113
|
+
let base, dur;
|
|
114
|
+
// Tuesday's second durmuhurtam uses night duration
|
|
115
|
+
if (weekday === 2 && i === 1) {
|
|
116
|
+
base = set.hours;
|
|
117
|
+
dur = nightDur;
|
|
118
|
+
} else {
|
|
119
|
+
base = rise.hours;
|
|
120
|
+
dur = dayDur;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const startTime = base + dur * offsets[i] / 12;
|
|
124
|
+
const endTime = startTime + durDuration;
|
|
125
|
+
|
|
126
|
+
results.push({
|
|
127
|
+
startTime,
|
|
128
|
+
endTime,
|
|
129
|
+
startString: utils.formatTime(startTime),
|
|
130
|
+
endString: utils.formatTime(endTime)
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Calculate Brahma Muhurta (pre-dawn auspicious period)
|
|
139
|
+
* 2 muhurtas before sunrise
|
|
140
|
+
* @param {number} jd - Julian Day Number
|
|
141
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
142
|
+
* @returns {object} {startTime, endTime}
|
|
143
|
+
*/
|
|
144
|
+
function brahmaMuhurta(jd, place) {
|
|
145
|
+
const nightDur = sunriseSunset.nightLength(jd - 1, place);
|
|
146
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
147
|
+
|
|
148
|
+
if (!rise) return null;
|
|
149
|
+
|
|
150
|
+
const muhurtaDur = nightDur / 15; // One night muhurta
|
|
151
|
+
const startTime = rise.hours - 2 * muhurtaDur;
|
|
152
|
+
const endTime = rise.hours - muhurtaDur;
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
startTime: startTime < 0 ? startTime + 24 : startTime,
|
|
156
|
+
endTime: endTime < 0 ? endTime + 24 : endTime,
|
|
157
|
+
startString: utils.formatTime(startTime < 0 ? startTime + 24 : startTime),
|
|
158
|
+
endString: utils.formatTime(endTime < 0 ? endTime + 24 : endTime)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Calculate Godhuli Muhurta (cow-dust time around sunset)
|
|
164
|
+
* @param {number} jd - Julian Day Number
|
|
165
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
166
|
+
* @returns {object} {startTime, endTime}
|
|
167
|
+
*/
|
|
168
|
+
function godhuliMuhurta(jd, place) {
|
|
169
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
170
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
171
|
+
const nightDur = sunriseSunset.nightLength(jd, place);
|
|
172
|
+
|
|
173
|
+
if (!set) return null;
|
|
174
|
+
|
|
175
|
+
const dayMuhurta = dayDur / 15;
|
|
176
|
+
const nightMuhurta = nightDur / 15;
|
|
177
|
+
|
|
178
|
+
const startTime = set.hours - 0.25 * dayMuhurta;
|
|
179
|
+
const endTime = set.hours + 0.25 * nightMuhurta;
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
startTime,
|
|
183
|
+
endTime,
|
|
184
|
+
startString: utils.formatTime(startTime),
|
|
185
|
+
endString: utils.formatTime(endTime)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Calculate Vijaya Muhurta (victory time - around noon and midnight)
|
|
191
|
+
* @param {number} jd - Julian Day Number
|
|
192
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
193
|
+
* @returns {object} {day, night} - both periods
|
|
194
|
+
*/
|
|
195
|
+
function vijayaMuhurta(jd, place) {
|
|
196
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
197
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
198
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
199
|
+
const nightDur = sunriseSunset.nightLength(jd, place);
|
|
200
|
+
|
|
201
|
+
if (!rise || !set) return null;
|
|
202
|
+
|
|
203
|
+
const dayGhati = dayDur / 30;
|
|
204
|
+
const nightGhati = nightDur / 30;
|
|
205
|
+
|
|
206
|
+
const noon = rise.hours + dayDur / 2;
|
|
207
|
+
const midnight = set.hours + nightDur / 2;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
day: {
|
|
211
|
+
startTime: noon - dayGhati,
|
|
212
|
+
endTime: noon + dayGhati,
|
|
213
|
+
startString: utils.formatTime(noon - dayGhati),
|
|
214
|
+
endString: utils.formatTime(noon + dayGhati)
|
|
215
|
+
},
|
|
216
|
+
night: {
|
|
217
|
+
startTime: midnight - nightGhati,
|
|
218
|
+
endTime: midnight + nightGhati,
|
|
219
|
+
startString: utils.formatTime((midnight - nightGhati) % 24),
|
|
220
|
+
endString: utils.formatTime((midnight + nightGhati) % 24)
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Calculate Nishita Kaala (8th muhurta of night)
|
|
227
|
+
* @param {number} jd - Julian Day Number
|
|
228
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
229
|
+
* @returns {object} {startTime, endTime}
|
|
230
|
+
*/
|
|
231
|
+
function nishitaKaala(jd, place) {
|
|
232
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
233
|
+
const nightDur = sunriseSunset.nightLength(jd, place);
|
|
234
|
+
|
|
235
|
+
if (!set) return null;
|
|
236
|
+
|
|
237
|
+
const nightGhati = nightDur / 30;
|
|
238
|
+
const startTime = set.hours + 7 * nightGhati;
|
|
239
|
+
const endTime = set.hours + 8 * nightGhati;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
startTime: startTime % 24,
|
|
243
|
+
endTime: endTime % 24,
|
|
244
|
+
startString: utils.formatTime(startTime % 24),
|
|
245
|
+
endString: utils.formatTime(endTime % 24)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Calculate Gauri Choghadiya (North Indian time system)
|
|
251
|
+
* 8 periods during day, 8 during night
|
|
252
|
+
* @param {number} jd - Julian Day Number
|
|
253
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
254
|
+
* @returns {array} List of choghadiya periods
|
|
255
|
+
*/
|
|
256
|
+
function gauriChoghadiya(jd, place) {
|
|
257
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
258
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
259
|
+
const nextRise = sunriseSunset.sunrise(jd + 1, place);
|
|
260
|
+
const weekday = vaaraModule.vaara(jd).number;
|
|
261
|
+
|
|
262
|
+
if (!rise || !set || !nextRise) return [];
|
|
263
|
+
|
|
264
|
+
const dayDur = (set.hours - rise.hours) / 8;
|
|
265
|
+
const nightDur = (24 + nextRise.hours - set.hours) / 8;
|
|
266
|
+
|
|
267
|
+
const periods = [];
|
|
268
|
+
|
|
269
|
+
// Day periods
|
|
270
|
+
for (let i = 0; i < 8; i++) {
|
|
271
|
+
const typeIndex = GAURI_CHOGHADIYA_DAY[weekday][i];
|
|
272
|
+
const startTime = rise.hours + i * dayDur;
|
|
273
|
+
const endTime = rise.hours + (i + 1) * dayDur;
|
|
274
|
+
|
|
275
|
+
periods.push({
|
|
276
|
+
period: i + 1,
|
|
277
|
+
type: CHOGHADIYA_NAMES[typeIndex],
|
|
278
|
+
typeIndex,
|
|
279
|
+
startTime,
|
|
280
|
+
endTime,
|
|
281
|
+
startString: utils.formatTime(startTime),
|
|
282
|
+
endString: utils.formatTime(endTime),
|
|
283
|
+
isDay: true,
|
|
284
|
+
isAuspicious: [2, 3, 5].includes(typeIndex) // Labh, Amrit, Shubh
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Night periods
|
|
289
|
+
for (let i = 0; i < 8; i++) {
|
|
290
|
+
const typeIndex = GAURI_CHOGHADIYA_NIGHT[weekday][i];
|
|
291
|
+
const startTime = set.hours + i * nightDur;
|
|
292
|
+
const endTime = set.hours + (i + 1) * nightDur;
|
|
293
|
+
|
|
294
|
+
periods.push({
|
|
295
|
+
period: i + 9,
|
|
296
|
+
type: CHOGHADIYA_NAMES[typeIndex],
|
|
297
|
+
typeIndex,
|
|
298
|
+
startTime: startTime % 24,
|
|
299
|
+
endTime: endTime % 24,
|
|
300
|
+
startString: utils.formatTime(startTime % 24),
|
|
301
|
+
endString: utils.formatTime(endTime % 24),
|
|
302
|
+
isDay: false,
|
|
303
|
+
isAuspicious: [2, 3, 5].includes(typeIndex)
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return periods;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get Amrit Kaalam (most auspicious choghadiya period)
|
|
312
|
+
* @param {number} jd - Julian Day Number
|
|
313
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
314
|
+
* @returns {array} List of Amrit periods
|
|
315
|
+
*/
|
|
316
|
+
function amritKaalam(jd, place) {
|
|
317
|
+
const choghadiyas = gauriChoghadiya(jd, place);
|
|
318
|
+
return choghadiyas.filter(c => c.type === 'Amrit');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Calculate Sandhya periods (twilight - important for prayers)
|
|
323
|
+
* @param {number} jd - Julian Day Number
|
|
324
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
325
|
+
* @returns {object} {pratah, madhyahna, sayam} - three sandhya periods
|
|
326
|
+
*/
|
|
327
|
+
function sandhyaPeriods(jd, place) {
|
|
328
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
329
|
+
const set = sunriseSunset.sunset(jd, place);
|
|
330
|
+
const dayDur = sunriseSunset.dayLength(jd, place);
|
|
331
|
+
|
|
332
|
+
if (!rise || !set) return null;
|
|
333
|
+
|
|
334
|
+
const ghati = dayDur / 30;
|
|
335
|
+
const noon = rise.hours + dayDur / 2;
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
pratah: {
|
|
339
|
+
startTime: rise.hours - 2 * ghati,
|
|
340
|
+
endTime: rise.hours + ghati,
|
|
341
|
+
description: '2 ghatis before and 1 ghati after sunrise'
|
|
342
|
+
},
|
|
343
|
+
madhyahna: {
|
|
344
|
+
startTime: noon - 1.5 * ghati,
|
|
345
|
+
endTime: noon + 1.5 * ghati,
|
|
346
|
+
description: '1.5 ghatis before and after noon'
|
|
347
|
+
},
|
|
348
|
+
sayam: {
|
|
349
|
+
startTime: set.hours - ghati,
|
|
350
|
+
endTime: set.hours + 2 * ghati,
|
|
351
|
+
description: '1 ghati before and 2 ghatis after sunset'
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
module.exports = {
|
|
357
|
+
trikalam,
|
|
358
|
+
raahuKaalam,
|
|
359
|
+
yamagandaKaalam,
|
|
360
|
+
gulikaiKaalam,
|
|
361
|
+
abhijitMuhurta,
|
|
362
|
+
durmuhurtam,
|
|
363
|
+
brahmaMuhurta,
|
|
364
|
+
godhuliMuhurta,
|
|
365
|
+
vijayaMuhurta,
|
|
366
|
+
nishitaKaala,
|
|
367
|
+
gauriChoghadiya,
|
|
368
|
+
amritKaalam,
|
|
369
|
+
sandhyaPeriods
|
|
370
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nakshatra (Lunar Mansion) Calculations
|
|
3
|
+
* Nakshatra = floor(Moon longitude / 13.333) + 1
|
|
4
|
+
* 27 nakshatras, each with 4 padas (quarters)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const sunriseSunset = require('./sunrise-sunset');
|
|
8
|
+
const utils = require('./utils');
|
|
9
|
+
const { NAKSHATRA_NAMES, ONE_NAKSHATRA, ONE_PADA } = require('./constants');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get nakshatra and pada from longitude
|
|
13
|
+
* @param {number} longitude - Ecliptic longitude (0-360)
|
|
14
|
+
* @returns {object} {nakshatra, pada, remainder}
|
|
15
|
+
*/
|
|
16
|
+
function nakshatraPada(longitude) {
|
|
17
|
+
const normalizedLong = utils.norm360(longitude);
|
|
18
|
+
const nakshatraIndex = Math.floor(normalizedLong / ONE_NAKSHATRA);
|
|
19
|
+
const remainder = normalizedLong % ONE_NAKSHATRA;
|
|
20
|
+
const padaIndex = Math.floor(remainder / ONE_PADA);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
nakshatra: nakshatraIndex + 1, // 1-27
|
|
24
|
+
pada: padaIndex + 1, // 1-4
|
|
25
|
+
remainder: remainder, // Degrees within nakshatra
|
|
26
|
+
degreesInPada: remainder % ONE_PADA
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get nakshatra details for given date/time
|
|
32
|
+
* @param {number} jd - Julian Day Number (local)
|
|
33
|
+
* @param {object} place - {latitude, longitude, timezone}
|
|
34
|
+
* @returns {object} Nakshatra details
|
|
35
|
+
*/
|
|
36
|
+
function nakshatra(jd, place) {
|
|
37
|
+
const jdUTC = utils.toUTC(jd, place.timezone);
|
|
38
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
39
|
+
|
|
40
|
+
if (!rise) return null;
|
|
41
|
+
|
|
42
|
+
const riseJDUTC = utils.toUTC(rise.jd, place.timezone);
|
|
43
|
+
const moonLong = sunriseSunset.lunarLongitude(jdUTC);
|
|
44
|
+
const { nakshatra: nakNo, pada: padaNo, remainder } = nakshatraPada(moonLong);
|
|
45
|
+
|
|
46
|
+
// Calculate nakshatra end time
|
|
47
|
+
const targetDegree = nakNo * ONE_NAKSHATRA;
|
|
48
|
+
const degreesLeft = targetDegree - moonLong;
|
|
49
|
+
|
|
50
|
+
// Get moon positions at intervals
|
|
51
|
+
const offsets = [0.0, 0.25, 0.5, 0.75, 1.0];
|
|
52
|
+
const moonLongs = offsets.map(t => sunriseSunset.lunarLongitude(riseJDUTC + t));
|
|
53
|
+
const unwrapped = utils.unwrapAngles(moonLongs);
|
|
54
|
+
|
|
55
|
+
// Find when nakshatra ends
|
|
56
|
+
let endTime;
|
|
57
|
+
try {
|
|
58
|
+
const jdAtStart = utils.dateToJD(
|
|
59
|
+
utils.jdToDate(jd).year,
|
|
60
|
+
utils.jdToDate(jd).month,
|
|
61
|
+
utils.jdToDate(jd).day
|
|
62
|
+
);
|
|
63
|
+
const approxEnd = utils.inverseLagrange(offsets, unwrapped, targetDegree);
|
|
64
|
+
endTime = (rise.jd + approxEnd - jdAtStart) * 24;
|
|
65
|
+
} catch {
|
|
66
|
+
// Fallback using average moon speed
|
|
67
|
+
const avgMoonSpeed = 13.17; // degrees per day
|
|
68
|
+
endTime = rise.hours + (degreesLeft / avgMoonSpeed) * 24;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get start time from previous day
|
|
72
|
+
const prevNak = _getNakshatraAtSunrise(jd - 1, place);
|
|
73
|
+
let startTime = prevNak ? prevNak.endTime : rise.hours;
|
|
74
|
+
if (startTime > 24) startTime -= 24;
|
|
75
|
+
if (startTime < 0) startTime = -startTime;
|
|
76
|
+
|
|
77
|
+
// Check for nakshatra change during day
|
|
78
|
+
let nextNakshatra = null;
|
|
79
|
+
if (endTime < 24) {
|
|
80
|
+
const nextNakNo = (nakNo % 27) + 1;
|
|
81
|
+
nextNakshatra = {
|
|
82
|
+
number: nextNakNo,
|
|
83
|
+
name: NAKSHATRA_NAMES[nextNakNo - 1],
|
|
84
|
+
pada: 1,
|
|
85
|
+
startTime: endTime
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = {
|
|
90
|
+
number: nakNo,
|
|
91
|
+
name: NAKSHATRA_NAMES[nakNo - 1],
|
|
92
|
+
pada: padaNo,
|
|
93
|
+
startTime,
|
|
94
|
+
endTime,
|
|
95
|
+
startTimeString: utils.formatTime(startTime < 0 ? startTime + 24 : startTime),
|
|
96
|
+
endTimeString: utils.formatTime(endTime > 24 ? endTime - 24 : endTime) + (endTime > 24 ? ' (+1)' : ''),
|
|
97
|
+
remainder,
|
|
98
|
+
moonLongitude: moonLong
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (nextNakshatra) {
|
|
102
|
+
result.nextNakshatra = nextNakshatra;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Internal helper to get nakshatra at sunrise
|
|
110
|
+
*/
|
|
111
|
+
function _getNakshatraAtSunrise(jd, place) {
|
|
112
|
+
const rise = sunriseSunset.sunrise(jd, place);
|
|
113
|
+
if (!rise) return null;
|
|
114
|
+
|
|
115
|
+
const riseJDUTC = utils.toUTC(rise.jd, place.timezone);
|
|
116
|
+
const moonLong = sunriseSunset.lunarLongitude(riseJDUTC);
|
|
117
|
+
const nakNo = Math.floor(moonLong / ONE_NAKSHATRA) + 1;
|
|
118
|
+
const targetDegree = nakNo * ONE_NAKSHATRA;
|
|
119
|
+
|
|
120
|
+
const offsets = [0.0, 0.25, 0.5, 0.75, 1.0];
|
|
121
|
+
const moonLongs = offsets.map(t => sunriseSunset.lunarLongitude(riseJDUTC + t));
|
|
122
|
+
const unwrapped = utils.unwrapAngles(moonLongs);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const jdAtStart = utils.dateToJD(
|
|
126
|
+
utils.jdToDate(jd).year,
|
|
127
|
+
utils.jdToDate(jd).month,
|
|
128
|
+
utils.jdToDate(jd).day
|
|
129
|
+
);
|
|
130
|
+
const approxEnd = utils.inverseLagrange(offsets, unwrapped, targetDegree);
|
|
131
|
+
const endTime = (rise.jd + approxEnd - jdAtStart) * 24;
|
|
132
|
+
return { nakNo, endTime };
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Quick nakshatra lookup (without full timing calculation)
|
|
140
|
+
* @param {number} jdUTC - Julian Day (UTC)
|
|
141
|
+
* @returns {object} {nakshatra, pada}
|
|
142
|
+
*/
|
|
143
|
+
function quickNakshatra(jdUTC) {
|
|
144
|
+
const moonLong = sunriseSunset.lunarLongitude(jdUTC);
|
|
145
|
+
return nakshatraPada(moonLong);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get nakshatra lord (planet ruling the nakshatra)
|
|
150
|
+
* @param {number} nakshatra - Nakshatra number (1-27)
|
|
151
|
+
* @returns {number} Planet index (0=Sun, 1=Moon, ...)
|
|
152
|
+
*/
|
|
153
|
+
function nakshatraLord(nakshatra) {
|
|
154
|
+
// Vimshottari lords: Ketu(8), Venus(5), Sun(0), Moon(1), Mars(2), Rahu(7), Jupiter(4), Saturn(6), Mercury(3)
|
|
155
|
+
const lords = [8, 5, 0, 1, 2, 7, 4, 6, 3];
|
|
156
|
+
return lords[(nakshatra - 1) % 9];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get abhijit nakshatra bounds
|
|
161
|
+
* Abhijit spans the last quarter of Uttara Ashadha and first 1/15th of Shravana
|
|
162
|
+
* @returns {object} Start and end degrees
|
|
163
|
+
*/
|
|
164
|
+
function abhijitBounds() {
|
|
165
|
+
// Uttara Ashadha ends at 280°, Shravana starts at 280°
|
|
166
|
+
// Abhijit: 276°40' to 280°53'20"
|
|
167
|
+
return {
|
|
168
|
+
start: 276 + 40 / 60,
|
|
169
|
+
end: 280 + 53 / 60 + 20 / 3600
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
nakshatra,
|
|
175
|
+
nakshatraPada,
|
|
176
|
+
quickNakshatra,
|
|
177
|
+
nakshatraLord,
|
|
178
|
+
abhijitBounds,
|
|
179
|
+
NAKSHATRA_NAMES
|
|
180
|
+
};
|