swisseph-wasm 0.0.1

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,888 @@
1
+ # SwissEph WebAssembly Library Documentation
2
+
3
+ A JavaScript wrapper for the Swiss Ephemeris WebAssembly module, providing high-precision astronomical calculations for astrology, astronomy, and related applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [API Reference](#api-reference)
10
+ - [Examples](#examples)
11
+ - [Constants](#constants)
12
+ - [Advanced Usage](#advanced-usage)
13
+ - [Testing](#testing)
14
+ - [Browser Support](#browser-support)
15
+
16
+ ## Installation
17
+
18
+ ### Option 1: Direct Download
19
+ Download the library files and include them in your project:
20
+
21
+ ```
22
+ src/swisseph.js # Main library file
23
+ wsam/swisseph.js # WebAssembly module
24
+ wsam/swisseph.wasm # WebAssembly binary
25
+ ```
26
+
27
+ ### Option 2: ES Module Import
28
+ ```javascript
29
+ import SwissEph from './src/swisseph.js';
30
+ ```
31
+
32
+ ### Option 3: HTML Script Tag
33
+ ```html
34
+ <script type="module">
35
+ import SwissEph from './src/swisseph.js';
36
+ // Your code here
37
+ </script>
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### Basic Setup
43
+
44
+ ```javascript
45
+ import SwissEph from './src/swisseph.js';
46
+
47
+ // Create instance
48
+ const swe = new SwissEph();
49
+
50
+ // Initialize the WebAssembly module
51
+ await swe.initSwissEph();
52
+
53
+ // Calculate Julian Day
54
+ const jd = swe.julday(2023, 6, 15, 12.0); // June 15, 2023, 12:00 UTC
55
+
56
+ // Calculate Sun position
57
+ const sunPosition = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
58
+ console.log(`Sun longitude: ${sunPosition[0]}°`);
59
+
60
+ // Clean up when done
61
+ swe.close();
62
+ ```
63
+
64
+ ### Birth Chart Example
65
+
66
+ ```javascript
67
+ async function calculateBirthChart(year, month, day, hour, minute, timezone) {
68
+ const swe = new SwissEph();
69
+ await swe.initSwissEph();
70
+
71
+ // Convert local time to UTC
72
+ const utcHour = hour + minute / 60 - timezone;
73
+ const jd = swe.julday(year, month, day, utcHour);
74
+
75
+ // Define planets to calculate
76
+ const planets = [
77
+ { id: swe.SE_SUN, name: 'Sun' },
78
+ { id: swe.SE_MOON, name: 'Moon' },
79
+ { id: swe.SE_MERCURY, name: 'Mercury' },
80
+ { id: swe.SE_VENUS, name: 'Venus' },
81
+ { id: swe.SE_MARS, name: 'Mars' },
82
+ { id: swe.SE_JUPITER, name: 'Jupiter' },
83
+ { id: swe.SE_SATURN, name: 'Saturn' },
84
+ { id: swe.SE_URANUS, name: 'Uranus' },
85
+ { id: swe.SE_NEPTUNE, name: 'Neptune' },
86
+ { id: swe.SE_PLUTO, name: 'Pluto' }
87
+ ];
88
+
89
+ const chart = {};
90
+
91
+ for (const planet of planets) {
92
+ const result = swe.calc_ut(jd, planet.id, swe.SEFLG_SWIEPH);
93
+ chart[planet.name] = {
94
+ longitude: result[0],
95
+ latitude: result[1],
96
+ distance: result[2],
97
+ speed: result[3]
98
+ };
99
+ }
100
+
101
+ swe.close();
102
+ return chart;
103
+ }
104
+
105
+ // Usage
106
+ const chart = await calculateBirthChart(1990, 5, 15, 14, 30, -5); // May 15, 1990, 2:30 PM, UTC-5
107
+ console.log(chart);
108
+ ```
109
+
110
+ ## API Reference
111
+
112
+ ### Core Methods
113
+
114
+ #### `initSwissEph()`
115
+ Initializes the WebAssembly module. Must be called before using any other methods.
116
+
117
+ ```javascript
118
+ await swe.initSwissEph();
119
+ ```
120
+
121
+ #### `julday(year, month, day, hour)`
122
+ Calculates Julian Day Number for a given date and time.
123
+
124
+ ```javascript
125
+ const jd = swe.julday(2023, 6, 15, 12.5); // June 15, 2023, 12:30 UTC
126
+ ```
127
+
128
+ **Parameters:**
129
+ - `year` (number): Year (e.g., 2023)
130
+ - `month` (number): Month (1-12)
131
+ - `day` (number): Day (1-31)
132
+ - `hour` (number): Hour in decimal format (0-24)
133
+
134
+ **Returns:** Julian Day Number (number)
135
+
136
+ #### `calc_ut(jd, planet, flags)`
137
+ Calculates planetary positions for Universal Time.
138
+
139
+ ```javascript
140
+ const result = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
141
+ ```
142
+
143
+ **Parameters:**
144
+ - `jd` (number): Julian Day Number
145
+ - `planet` (number): Planet constant (e.g., `swe.SE_SUN`)
146
+ - `flags` (number): Calculation flags (e.g., `swe.SEFLG_SWIEPH`)
147
+
148
+ **Returns:** Float64Array with [longitude, latitude, distance, speed]
149
+
150
+ #### `calc(jd, planet, flags)`
151
+ Calculates planetary positions (alternative method with more detailed output).
152
+
153
+ ```javascript
154
+ const result = swe.calc(jd, swe.SE_MOON, swe.SEFLG_SWIEPH);
155
+ ```
156
+
157
+ **Returns:** Object with detailed position data:
158
+ ```javascript
159
+ {
160
+ longitude: number,
161
+ latitude: number,
162
+ distance: number,
163
+ longitudeSpeed: number,
164
+ latitudeSpeed: number,
165
+ distanceSpeed: number
166
+ }
167
+ ```
168
+
169
+ ### Time Functions
170
+
171
+ #### `deltat(jd)`
172
+ Calculates Delta T (difference between Terrestrial Time and Universal Time).
173
+
174
+ ```javascript
175
+ const deltaT = swe.deltat(jd);
176
+ ```
177
+
178
+ #### `sidtime(jd)`
179
+ Calculates sidereal time for a given Julian Day.
180
+
181
+ ```javascript
182
+ const siderealTime = swe.sidtime(jd);
183
+ ```
184
+
185
+ #### `utc_to_jd(year, month, day, hour, minute, second, gregflag)`
186
+ Converts UTC time to Julian Day.
187
+
188
+ ```javascript
189
+ const result = swe.utc_to_jd(2023, 6, 15, 12, 30, 0, swe.SE_GREG_CAL);
190
+ // Returns: { julianDayET: number, julianDayUT: number }
191
+ ```
192
+
193
+ ### Date Conversion Functions
194
+
195
+ #### `revjul(jd, gregflag)`
196
+ Converts Julian Day back to calendar date.
197
+
198
+ ```javascript
199
+ const date = swe.revjul(jd, swe.SE_GREG_CAL);
200
+ // Returns: { year: number, month: number, day: number, hour: number }
201
+ ```
202
+
203
+ #### `date_conversion(year, month, day, hour, gregflag)`
204
+ Converts calendar date to Julian Day.
205
+
206
+ ```javascript
207
+ const jd = swe.date_conversion(2023, 6, 15, 12.5, swe.SE_GREG_CAL);
208
+ ```
209
+
210
+ ### Utility Functions
211
+
212
+ #### `degnorm(degrees)`
213
+ Normalizes degrees to 0-360 range.
214
+
215
+ ```javascript
216
+ const normalized = swe.degnorm(370); // Returns 10
217
+ ```
218
+
219
+ #### `split_deg(degrees, roundflag)`
220
+ Splits decimal degrees into degrees, minutes, seconds.
221
+
222
+ ```javascript
223
+ const split = swe.split_deg(123.456789, swe.SE_SPLIT_DEG_ROUND_SEC);
224
+ // Returns: { degree: 123, min: 27, second: 24, fraction: 0, sign: 4 }
225
+ ```
226
+
227
+ #### `day_of_week(jd)`
228
+ Returns day of week for a Julian Day (0=Monday, 6=Sunday).
229
+
230
+ ```javascript
231
+ const dayOfWeek = swe.day_of_week(jd);
232
+ ```
233
+
234
+ ### Sidereal Functions
235
+
236
+ #### `set_sid_mode(sidmode, t0, ayan_t0)`
237
+ Sets sidereal calculation mode.
238
+
239
+ ```javascript
240
+ swe.set_sid_mode(swe.SE_SIDM_LAHIRI, 0, 0);
241
+ ```
242
+
243
+ #### `get_ayanamsa(jd)`
244
+ Gets ayanamsa value for sidereal calculations.
245
+
246
+ ```javascript
247
+ const ayanamsa = swe.get_ayanamsa(jd);
248
+ ```
249
+
250
+ ### Information Functions
251
+
252
+ #### `version()`
253
+ Returns Swiss Ephemeris version string.
254
+
255
+ ```javascript
256
+ const version = swe.version();
257
+ ```
258
+
259
+ #### `get_planet_name(planet)`
260
+ Returns planet name for a planet constant.
261
+
262
+ ```javascript
263
+ const name = swe.get_planet_name(swe.SE_SUN); // Returns "Sun"
264
+ ```
265
+
266
+ ### Cleanup
267
+
268
+ #### `close()`
269
+ Closes the Swiss Ephemeris and frees memory. Call when done.
270
+
271
+ ```javascript
272
+ swe.close();
273
+ ```
274
+
275
+ ## Examples
276
+
277
+ ### Example 1: Current Planetary Positions
278
+
279
+ ```javascript
280
+ async function getCurrentPlanetaryPositions() {
281
+ const swe = new SwissEph();
282
+ await swe.initSwissEph();
283
+
284
+ const now = new Date();
285
+ const jd = swe.julday(
286
+ now.getUTCFullYear(),
287
+ now.getUTCMonth() + 1,
288
+ now.getUTCDate(),
289
+ now.getUTCHours() + now.getUTCMinutes() / 60
290
+ );
291
+
292
+ const planets = [
293
+ swe.SE_SUN, swe.SE_MOON, swe.SE_MERCURY, swe.SE_VENUS,
294
+ swe.SE_MARS, swe.SE_JUPITER, swe.SE_SATURN
295
+ ];
296
+
297
+ const positions = {};
298
+
299
+ for (const planet of planets) {
300
+ const result = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);
301
+ const name = swe.get_planet_name(planet);
302
+ positions[name] = result[0]; // longitude in degrees
303
+ }
304
+
305
+ swe.close();
306
+ return positions;
307
+ }
308
+ ```
309
+
310
+ ### Example 2: Sidereal vs Tropical Positions
311
+
312
+ ```javascript
313
+ async function compareSiderealTropical(year, month, day, hour) {
314
+ const swe = new SwissEph();
315
+ await swe.initSwissEph();
316
+
317
+ const jd = swe.julday(year, month, day, hour);
318
+
319
+ // Set Lahiri ayanamsa for sidereal calculations
320
+ swe.set_sid_mode(swe.SE_SIDM_LAHIRI, 0, 0);
321
+
322
+ // Tropical position
323
+ const tropical = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
324
+
325
+ // Sidereal position
326
+ const sidereal = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH | swe.SEFLG_SIDEREAL);
327
+
328
+ // Get ayanamsa value
329
+ const ayanamsa = swe.get_ayanamsa(jd);
330
+
331
+ swe.close();
332
+
333
+ return {
334
+ tropical: tropical[0],
335
+ sidereal: sidereal[0],
336
+ ayanamsa: ayanamsa,
337
+ difference: tropical[0] - sidereal[0]
338
+ };
339
+ }
340
+ ```
341
+
342
+ ### Example 3: Time Zone Conversion
343
+
344
+ ```javascript
345
+ async function calculateWithTimeZone(year, month, day, hour, minute, second, timezone) {
346
+ const swe = new SwissEph();
347
+ await swe.initSwissEph();
348
+
349
+ // Convert to UTC using built-in function
350
+ const utcResult = swe.utc_to_jd(year, month, day, hour, minute, second, swe.SE_GREG_CAL);
351
+
352
+ // Adjust for timezone
353
+ const localJD = utcResult.julianDayUT - timezone / 24;
354
+
355
+ // Calculate planetary positions
356
+ const sunPos = swe.calc_ut(localJD, swe.SE_SUN, swe.SEFLG_SWIEPH);
357
+
358
+ swe.close();
359
+
360
+ return {
361
+ julianDay: localJD,
362
+ sunLongitude: sunPos[0],
363
+ deltaT: swe.deltat(localJD)
364
+ };
365
+ }
366
+ ```
367
+
368
+ ### Example 4: Moon Phases
369
+
370
+ ```javascript
371
+ async function getMoonPhase(year, month, day) {
372
+ const swe = new SwissEph();
373
+ await swe.initSwissEph();
374
+
375
+ const jd = swe.julday(year, month, day, 12.0);
376
+
377
+ // Get Sun and Moon positions
378
+ const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
379
+ const moonPos = swe.calc_ut(jd, swe.SE_MOON, swe.SEFLG_SWIEPH);
380
+
381
+ // Calculate phase angle
382
+ let phaseAngle = moonPos[0] - sunPos[0];
383
+ if (phaseAngle < 0) phaseAngle += 360;
384
+
385
+ // Determine phase
386
+ let phase;
387
+ if (phaseAngle < 45 || phaseAngle > 315) phase = "New Moon";
388
+ else if (phaseAngle < 135) phase = "Waxing";
389
+ else if (phaseAngle < 225) phase = "Full Moon";
390
+ else phase = "Waning";
391
+
392
+ swe.close();
393
+
394
+ return {
395
+ phaseAngle: phaseAngle,
396
+ phase: phase,
397
+ sunLongitude: sunPos[0],
398
+ moonLongitude: moonPos[0]
399
+ };
400
+ }
401
+ ```
402
+
403
+ ## Constants
404
+
405
+ ### Planet Constants
406
+
407
+ ```javascript
408
+ // Major planets
409
+ swe.SE_SUN = 0;
410
+ swe.SE_MOON = 1;
411
+ swe.SE_MERCURY = 2;
412
+ swe.SE_VENUS = 3;
413
+ swe.SE_MARS = 4;
414
+ swe.SE_JUPITER = 5;
415
+ swe.SE_SATURN = 6;
416
+ swe.SE_URANUS = 7;
417
+ swe.SE_NEPTUNE = 8;
418
+ swe.SE_PLUTO = 9;
419
+ swe.SE_EARTH = 14;
420
+
421
+ // Lunar nodes and apogee
422
+ swe.SE_MEAN_NODE = 10;
423
+ swe.SE_TRUE_NODE = 11;
424
+ swe.SE_MEAN_APOG = 12;
425
+ swe.SE_OSCU_APOG = 13;
426
+
427
+ // Major asteroids
428
+ swe.SE_CHIRON = 15;
429
+ swe.SE_PHOLUS = 16;
430
+ swe.SE_CERES = 17;
431
+ swe.SE_PALLAS = 18;
432
+ swe.SE_JUNO = 19;
433
+ swe.SE_VESTA = 20;
434
+
435
+ // Uranian planets
436
+ swe.SE_CUPIDO = 40;
437
+ swe.SE_HADES = 41;
438
+ swe.SE_ZEUS = 42;
439
+ swe.SE_KRONOS = 43;
440
+ swe.SE_APOLLON = 44;
441
+ swe.SE_ADMETOS = 45;
442
+ swe.SE_VULKANUS = 46;
443
+ swe.SE_POSEIDON = 47;
444
+ ```
445
+
446
+ ### Calculation Flags
447
+
448
+ ```javascript
449
+ // Ephemeris types
450
+ swe.SEFLG_JPLEPH = 1; // JPL ephemeris
451
+ swe.SEFLG_SWIEPH = 2; // Swiss ephemeris (default)
452
+ swe.SEFLG_MOSEPH = 4; // Moshier ephemeris
453
+
454
+ // Coordinate systems
455
+ swe.SEFLG_HELCTR = 8; // Heliocentric
456
+ swe.SEFLG_BARYCTR = 16384; // Barycentric
457
+ swe.SEFLG_TOPOCTR = 32768; // Topocentric
458
+ swe.SEFLG_EQUATORIAL = 2048; // Equatorial coordinates
459
+ swe.SEFLG_XYZ = 4096; // Cartesian coordinates
460
+ swe.SEFLG_RADIANS = 8192; // Radians instead of degrees
461
+
462
+ // Special flags
463
+ swe.SEFLG_SPEED = 256; // Calculate speed
464
+ swe.SEFLG_TRUEPOS = 16; // True positions (no light-time correction)
465
+ swe.SEFLG_J2000 = 32; // J2000 coordinates
466
+ swe.SEFLG_NONUT = 64; // No nutation
467
+ swe.SEFLG_NOGDEFL = 512; // No gravitational deflection
468
+ swe.SEFLG_NOABERR = 1024; // No aberration
469
+ swe.SEFLG_SIDEREAL = 65536; // Sidereal positions
470
+
471
+ // Composite flags
472
+ swe.SEFLG_ASTROMETRIC = 1536; // No aberration + no gravitational deflection
473
+ ```
474
+
475
+ ### Sidereal Modes
476
+
477
+ ```javascript
478
+ swe.SE_SIDM_FAGAN_BRADLEY = 0;
479
+ swe.SE_SIDM_LAHIRI = 1;
480
+ swe.SE_SIDM_DELUCE = 2;
481
+ swe.SE_SIDM_RAMAN = 3;
482
+ swe.SE_SIDM_USHASHASHI = 4;
483
+ swe.SE_SIDM_KRISHNAMURTI = 5;
484
+ swe.SE_SIDM_DJWHAL_KHUL = 6;
485
+ swe.SE_SIDM_YUKTESHWAR = 7;
486
+ swe.SE_SIDM_JN_BHASIN = 8;
487
+ swe.SE_SIDM_BABYL_KUGLER1 = 9;
488
+ swe.SE_SIDM_BABYL_KUGLER2 = 10;
489
+ swe.SE_SIDM_BABYL_KUGLER3 = 11;
490
+ swe.SE_SIDM_BABYL_HUBER = 12;
491
+ swe.SE_SIDM_BABYL_ETPSC = 13;
492
+ swe.SE_SIDM_ALDEBARAN_15TAU = 14;
493
+ swe.SE_SIDM_HIPPARCHOS = 15;
494
+ swe.SE_SIDM_SASSANIAN = 16;
495
+ swe.SE_SIDM_GALCENT_0SAG = 17;
496
+ swe.SE_SIDM_J2000 = 18;
497
+ swe.SE_SIDM_J1900 = 19;
498
+ swe.SE_SIDM_B1950 = 20;
499
+ swe.SE_SIDM_USER = 255;
500
+ ```
501
+
502
+ ### Calendar Types
503
+
504
+ ```javascript
505
+ swe.SE_JUL_CAL = 0; // Julian calendar
506
+ swe.SE_GREG_CAL = 1; // Gregorian calendar
507
+ ```
508
+
509
+ ### Degree Splitting Flags
510
+
511
+ ```javascript
512
+ swe.SE_SPLIT_DEG_ROUND_SEC = 1;
513
+ swe.SE_SPLIT_DEG_ROUND_MIN = 2;
514
+ swe.SE_SPLIT_DEG_ROUND_DEG = 4;
515
+ swe.SE_SPLIT_DEG_ZODIACAL = 8;
516
+ swe.SE_SPLIT_DEG_KEEP_SIGN = 16;
517
+ swe.SE_SPLIT_DEG_KEEP_DEG = 32;
518
+ ```
519
+
520
+ ## Advanced Usage
521
+
522
+ ### Working with Fixed Stars
523
+
524
+ ```javascript
525
+ async function getFixedStarPosition(starName, year, month, day) {
526
+ const swe = new SwissEph();
527
+ await swe.initSwissEph();
528
+
529
+ const jd = swe.julday(year, month, day, 0);
530
+
531
+ // Calculate fixed star position
532
+ const starPos = swe.fixstar(starName, jd, swe.SEFLG_SWIEPH);
533
+
534
+ if (starPos) {
535
+ const result = {
536
+ name: starName,
537
+ longitude: starPos[0],
538
+ latitude: starPos[1],
539
+ distance: starPos[2],
540
+ magnitude: swe.fixstar_mag(starName)
541
+ };
542
+
543
+ swe.close();
544
+ return result;
545
+ }
546
+
547
+ swe.close();
548
+ return null;
549
+ }
550
+
551
+ // Usage
552
+ const sirius = await getFixedStarPosition("Sirius", 2023, 6, 15);
553
+ ```
554
+
555
+ ### House Calculations
556
+
557
+ ```javascript
558
+ async function calculateHouses(year, month, day, hour, latitude, longitude, houseSystem = 'P') {
559
+ const swe = new SwissEph();
560
+ await swe.initSwissEph();
561
+
562
+ const jd = swe.julday(year, month, day, hour);
563
+
564
+ // Calculate houses
565
+ const houses = swe.houses(jd, latitude, longitude, houseSystem);
566
+
567
+ // Calculate house positions for planets
568
+ const planets = [swe.SE_SUN, swe.SE_MOON, swe.SE_MERCURY, swe.SE_VENUS, swe.SE_MARS];
569
+ const planetHouses = {};
570
+
571
+ for (const planet of planets) {
572
+ const planetPos = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);
573
+ const housePos = swe.house_pos(
574
+ swe.sidtime(jd) * 15, // ARMC
575
+ latitude,
576
+ 23.44, // obliquity
577
+ houseSystem,
578
+ planetPos[0],
579
+ planetPos[1]
580
+ );
581
+
582
+ planetHouses[swe.get_planet_name(planet)] = {
583
+ longitude: planetPos[0],
584
+ house: Math.floor(housePos)
585
+ };
586
+ }
587
+
588
+ swe.close();
589
+
590
+ return {
591
+ houses: houses,
592
+ planetHouses: planetHouses
593
+ };
594
+ }
595
+ ```
596
+
597
+ ### Eclipse Calculations
598
+
599
+ ```javascript
600
+ async function findNextSolarEclipse(startYear, startMonth, startDay) {
601
+ const swe = new SwissEph();
602
+ await swe.initSwissEph();
603
+
604
+ const startJD = swe.julday(startYear, startMonth, startDay, 12);
605
+
606
+ // Find next solar eclipse
607
+ const eclipse = swe.sol_eclipse_when_glob(
608
+ startJD,
609
+ swe.SEFLG_SWIEPH,
610
+ swe.SE_ECL_TOTAL | swe.SE_ECL_ANNULAR | swe.SE_ECL_PARTIAL,
611
+ 0 // forward search
612
+ );
613
+
614
+ if (eclipse) {
615
+ const eclipseDate = swe.revjul(eclipse[1], swe.SE_GREG_CAL);
616
+
617
+ swe.close();
618
+
619
+ return {
620
+ julianDay: eclipse[1],
621
+ date: eclipseDate,
622
+ type: eclipse[0] & swe.SE_ECL_TOTAL ? 'Total' :
623
+ eclipse[0] & swe.SE_ECL_ANNULAR ? 'Annular' : 'Partial',
624
+ magnitude: eclipse[4]
625
+ };
626
+ }
627
+
628
+ swe.close();
629
+ return null;
630
+ }
631
+ ```
632
+
633
+ ### Topocentric Calculations
634
+
635
+ ```javascript
636
+ async function calculateTopocentric(year, month, day, hour, latitude, longitude, altitude) {
637
+ const swe = new SwissEph();
638
+ await swe.initSwissEph();
639
+
640
+ // Set topocentric location
641
+ swe.set_topo(longitude, latitude, altitude);
642
+
643
+ const jd = swe.julday(year, month, day, hour);
644
+
645
+ // Calculate topocentric positions
646
+ const planets = [swe.SE_SUN, swe.SE_MOON, swe.SE_VENUS, swe.SE_MARS];
647
+ const positions = {};
648
+
649
+ for (const planet of planets) {
650
+ // Geocentric position
651
+ const geocentric = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);
652
+
653
+ // Topocentric position
654
+ const topocentric = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH | swe.SEFLG_TOPOCTR);
655
+
656
+ positions[swe.get_planet_name(planet)] = {
657
+ geocentric: geocentric[0],
658
+ topocentric: topocentric[0],
659
+ difference: geocentric[0] - topocentric[0]
660
+ };
661
+ }
662
+
663
+ swe.close();
664
+ return positions;
665
+ }
666
+ ```
667
+
668
+ ## Error Handling
669
+
670
+ ### Best Practices
671
+
672
+ ```javascript
673
+ async function safeCalculation(year, month, day, hour) {
674
+ let swe = null;
675
+
676
+ try {
677
+ swe = new SwissEph();
678
+ await swe.initSwissEph();
679
+
680
+ // Validate input
681
+ if (year < -5000 || year > 5000) {
682
+ throw new Error('Year out of valid range (-5000 to 5000)');
683
+ }
684
+
685
+ if (month < 1 || month > 12) {
686
+ throw new Error('Month must be between 1 and 12');
687
+ }
688
+
689
+ if (day < 1 || day > 31) {
690
+ throw new Error('Day must be between 1 and 31');
691
+ }
692
+
693
+ if (hour < 0 || hour >= 24) {
694
+ throw new Error('Hour must be between 0 and 23.999');
695
+ }
696
+
697
+ const jd = swe.julday(year, month, day, hour);
698
+
699
+ // Check for valid Julian Day
700
+ if (isNaN(jd) || jd === swe.TJD_INVALID) {
701
+ throw new Error('Invalid Julian Day calculated');
702
+ }
703
+
704
+ const result = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
705
+
706
+ if (!result || result.length < 4) {
707
+ throw new Error('Failed to calculate planetary position');
708
+ }
709
+
710
+ return {
711
+ success: true,
712
+ julianDay: jd,
713
+ longitude: result[0],
714
+ latitude: result[1],
715
+ distance: result[2],
716
+ speed: result[3]
717
+ };
718
+
719
+ } catch (error) {
720
+ return {
721
+ success: false,
722
+ error: error.message
723
+ };
724
+ } finally {
725
+ // Always clean up
726
+ if (swe) {
727
+ swe.close();
728
+ }
729
+ }
730
+ }
731
+ ```
732
+
733
+ ## Performance Tips
734
+
735
+ ### 1. Reuse SwissEph Instance
736
+
737
+ ```javascript
738
+ class AstrologyCalculator {
739
+ constructor() {
740
+ this.swe = null;
741
+ this.initialized = false;
742
+ }
743
+
744
+ async init() {
745
+ if (!this.initialized) {
746
+ this.swe = new SwissEph();
747
+ await this.swe.initSwissEph();
748
+ this.initialized = true;
749
+ }
750
+ }
751
+
752
+ async calculateChart(year, month, day, hour) {
753
+ await this.init();
754
+
755
+ const jd = this.swe.julday(year, month, day, hour);
756
+ const planets = [this.swe.SE_SUN, this.swe.SE_MOON, this.swe.SE_MERCURY];
757
+ const chart = {};
758
+
759
+ for (const planet of planets) {
760
+ const result = this.swe.calc_ut(jd, planet, this.swe.SEFLG_SWIEPH);
761
+ chart[this.swe.get_planet_name(planet)] = result[0];
762
+ }
763
+
764
+ return chart;
765
+ }
766
+
767
+ destroy() {
768
+ if (this.swe) {
769
+ this.swe.close();
770
+ this.swe = null;
771
+ this.initialized = false;
772
+ }
773
+ }
774
+ }
775
+
776
+ // Usage
777
+ const calculator = new AstrologyCalculator();
778
+ const chart1 = await calculator.calculateChart(1990, 5, 15, 14.5);
779
+ const chart2 = await calculator.calculateChart(1985, 12, 25, 8.0);
780
+ calculator.destroy(); // Clean up when done
781
+ ```
782
+
783
+ ### 2. Batch Calculations
784
+
785
+ ```javascript
786
+ async function batchCalculatePlanets(jd, planetList, flags) {
787
+ const swe = new SwissEph();
788
+ await swe.initSwissEph();
789
+
790
+ const results = {};
791
+
792
+ // Calculate all planets in one session
793
+ for (const planet of planetList) {
794
+ const result = swe.calc_ut(jd, planet, flags);
795
+ results[swe.get_planet_name(planet)] = result;
796
+ }
797
+
798
+ swe.close();
799
+ return results;
800
+ }
801
+ ```
802
+
803
+ ## Browser Support
804
+
805
+ ### Modern Browsers
806
+ - Chrome 61+
807
+ - Firefox 60+
808
+ - Safari 11+
809
+ - Edge 16+
810
+
811
+ ### Requirements
812
+ - WebAssembly support
813
+ - ES6 modules support
814
+ - Async/await support
815
+
816
+ ### Polyfills
817
+ For older browsers, you may need:
818
+ - WebAssembly polyfill
819
+ - ES6 module loader
820
+ - Promise polyfill
821
+
822
+ ## Testing
823
+
824
+ The library includes comprehensive tests. To run them:
825
+
826
+ ```bash
827
+ npm install
828
+ npm test
829
+ ```
830
+
831
+ ### Test Coverage
832
+ - 106 tests covering all major functionality
833
+ - 86.1% statement coverage
834
+ - Mock WebAssembly module for isolated testing
835
+ - Integration tests for real-world scenarios
836
+
837
+ ## Troubleshooting
838
+
839
+ ### Common Issues
840
+
841
+ 1. **WebAssembly not loading**
842
+ ```javascript
843
+ // Ensure proper path to WASM files
844
+ // Check browser console for loading errors
845
+ ```
846
+
847
+ 2. **Invalid Julian Day**
848
+ ```javascript
849
+ // Validate date inputs before calculation
850
+ if (isNaN(jd) || jd === swe.TJD_INVALID) {
851
+ throw new Error('Invalid date');
852
+ }
853
+ ```
854
+
855
+ 3. **Memory leaks**
856
+ ```javascript
857
+ // Always call close() when done
858
+ try {
859
+ // calculations
860
+ } finally {
861
+ swe.close();
862
+ }
863
+ ```
864
+
865
+ 4. **Incorrect time zones**
866
+ ```javascript
867
+ // Convert to UTC before calculation
868
+ const utcHour = localHour - timezoneOffset;
869
+ ```
870
+
871
+ ## License
872
+
873
+ This library is a wrapper around the Swiss Ephemeris, which is licensed under the GNU General Public License (GPL) for non-commercial use. For commercial use, a license from Astrodienst is required.
874
+
875
+ ## Support
876
+
877
+ For issues and questions:
878
+ - Check the test files for usage examples
879
+ - Review the Swiss Ephemeris documentation
880
+ - File issues on the project repository
881
+
882
+ ## Version History
883
+
884
+ - v0.0.1: Initial release with core functionality
885
+ - Comprehensive test suite
886
+ - Full WebAssembly integration
887
+ - Modern ES6+ JavaScript API
888
+ ```