magvar 2.0.2 → 2.2.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 CHANGED
@@ -1,44 +1,210 @@
1
- <img src="https://www.ncei.noaa.gov/sites/g/files/anmtlf171/files/inline-images/D.jpg" width="100%" alt="React Native Gesture Handler by Software Mansion">
1
+ <img src="https://www.ncei.noaa.gov/sites/g/files/anmtlf171/files/inline-images/D.jpg" width="100%" alt="World Magnetic Model declination map">
2
2
 
3
3
  # magvar
4
- This package computes the estimated Geomagnetic Declination (which is sometimes called Magnetic variation) at a specified location based upon the **World Magnetic Model 2025-2030**. More details about the World Magnetic Model can be found here: [https://www.ncei.noaa.gov/products/world-magnetic-model](https://www.ncei.noaa.gov/products/world-magnetic-model)
5
4
 
6
- ## BREAKING CHANGE IN VERSION 2.0
7
- If you are upgrading from version 1, note that the way you import the module has changed, as well as the name of the method you call. The 'get' method in version 1 has now been renamed 'magvar'.
5
+ Compute **magnetic variation** (geomagnetic declination) and related World Magnetic Model field components in JavaScript.
6
+
7
+ `magvar` implements the [World Magnetic Model 2025–2030](https://www.ncei.noaa.gov/products/world-magnetic-model) (WMM2025), the standard model used for navigation, surveying, and compass correction. Given a geographic position (and optionally altitude and time), it returns how far magnetic north differs from true north at that point.
8
+
9
+ | | |
10
+ | --- | --- |
11
+ | Model | WMM2025 |
12
+ | Valid period | decimal years **2025.0 – 2030.0** |
13
+ | Coordinates | WGS84 geodetic latitude / longitude |
14
+ | Altitude | kilometres above mean sea level |
15
+ | Package | CommonJS (works with `require` and named ESM imports in modern Node) |
8
16
 
9
17
  ## Installation
10
18
 
11
- `$ npm install magvar --save`
19
+ ```bash
20
+ npm install magvar
21
+ ```
22
+
23
+ ```bash
24
+ yarn add magvar
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ```javascript
30
+ const { magvar, magneticField } = require('magvar');
31
+ // or: import { magvar, magneticField } from 'magvar';
32
+
33
+ // Declination in degrees for London at sea level, current UTC time
34
+ const variation = magvar(51.5, -0.1);
35
+
36
+ // Positive => magnetic north is east of true north
37
+ // Negative => magnetic north is west of true north
38
+ console.log(variation);
39
+ ```
40
+
41
+ ## Inputs
42
+
43
+ All public helpers use the same position conventions:
44
+
45
+ | Argument | Meaning | Notes |
46
+ | --- | --- | --- |
47
+ | `latitude` | Geodetic latitude (degrees) | North positive, south negative. Range typically −90…90. |
48
+ | `longitude` | Geodetic longitude (degrees) | East positive, west negative. Values outside −180…180 (e.g. `240`) are accepted. |
49
+ | `altitude` | Height above mean sea level | **Kilometres**, not metres. Optional; defaults to `0`. Example: 100 m → `0.1`. |
50
+ | `when` | Evaluation time | Optional. A decimal year (`2027.5`) or a `Date`. If omitted, the **current UTC time** is used on each call. |
12
51
 
13
- or
52
+ ### Decimal years
14
53
 
15
- `$ yarn add magvar`
54
+ WMM times are expressed as decimal years:
16
55
 
17
- ## Usage
56
+ - `2025.0` → 2025-01-01
57
+ - `2027.5` → mid-2027
58
+ - A `Date` is converted using UTC
18
59
 
19
- Input required is a spot location in geodetic (WGS84) latitude and longitude (positive for northern latitudes and eastern longitudes), and altitude.
60
+ ```javascript
61
+ magvar(51.5, -0.1, 0, 2027.0);
62
+ magvar(51.5, -0.1, 0, new Date('2027-06-01T00:00:00Z'));
63
+ ```
64
+
65
+ ### Model validity
66
+
67
+ WMM2025 is intended for **2025.0 ≤ year < 2030.0**.
68
+ Requests outside that window still return a numeric result, but a one-time `console.warn` is emitted because accuracy is not guaranteed.
20
69
 
21
- Altitude is optional and should be specified in **kilometers above mean sea level** - defaults to 0 if not given.
70
+ ## API
71
+
72
+ ### `magvar(latitude, longitude, altitude?, when?)`
73
+
74
+ Returns magnetic variation (declination) in degrees, rounded to 2 decimal places.
22
75
 
23
76
  ```javascript
24
- import {magvar} from 'magvar';
77
+ const { magvar } = require('magvar');
25
78
 
26
- const variation = magvar([latitude], [longitude]);
79
+ magvar(40.7, -74.0); // now, sea level
80
+ magvar(40.7, -74.0, 0.3); // 300 m altitude
81
+ magvar(40.7, -74.0, 0, 2026.5); // mid-2026
82
+ magvar(-33.9, 151.2, 0, new Date()); // Sydney, current UTC time
83
+ ```
27
84
 
28
- // or
85
+ ### `magneticField(latitude, longitude, altitude?, when?)`
29
86
 
30
- const variation = magvar([latitude], [longitude], [altitude]);
87
+ Returns the main field components at the same point in time:
31
88
 
89
+ ```javascript
90
+ const { magneticField } = require('magvar');
91
+
92
+ const field = magneticField(80, 0, 0, 2025.0);
93
+ /*
94
+ {
95
+ declination: 1.28, // D, degrees (east positive)
96
+ inclination: 83.21, // I, degrees (down positive)
97
+ x: 6521.6, // north component, nT
98
+ y: 145.9, // east component, nT
99
+ z: 54791.5, // down component, nT
100
+ h: 6523.2, // horizontal intensity, nT
101
+ f: 55178.5, // total intensity, nT
102
+ decimalYear: 2025
103
+ }
104
+ */
32
105
  ```
33
106
 
34
- Take a look at the test file for some examples.
35
-
36
- ## Test
37
- After installing:
107
+ | Field | Symbol | Unit | Description |
108
+ | --- | --- | --- | --- |
109
+ | `declination` | D | degrees | Angle from true north to magnetic north (east positive) |
110
+ | `inclination` | I | degrees | Angle of the field below the horizontal (down positive) |
111
+ | `x` | X | nT | Northward component |
112
+ | `y` | Y | nT | Eastward component |
113
+ | `z` | Z | nT | Downward component |
114
+ | `h` | H | nT | Horizontal intensity (`√(X² + Y²)`) |
115
+ | `f` | F | nT | Total intensity (`√(H² + Z²)`) |
116
+ | `decimalYear` | — | year | Decimal year used for the calculation |
117
+
118
+ Angles are rounded to 2 decimal places; intensities to 0.1 nT, matching the published WMM test-value precision.
119
+
120
+ ### Explicit-time helpers
121
+
122
+ Use these when you already have a decimal year or Julian day and do not want “now” resolution:
123
+
38
124
  ```javascript
39
- npm run test
125
+ const {
126
+ calculateMagVarForDecimalYear,
127
+ calculateMagneticField,
128
+ calculateMagVar, // Julian Day Number → declination (legacy)
129
+ MODEL_EPOCH, // 2025.0
130
+ MODEL_VALID_UNTIL // 2030.0
131
+ } = require('magvar');
132
+
133
+ calculateMagVarForDecimalYear(2025.0, 0, 120, 0); // -0.16
134
+ calculateMagneticField(2027.5, -80, 240, 100);
135
+ calculateMagVar(2460677, 80, 0, 0); // Julian day at model epoch (noon)
40
136
  ```
41
137
 
138
+ For new code, prefer `magvar`, `magneticField`, or `calculateMagVarForDecimalYear`.
139
+
140
+ ### Date utilities
141
+
142
+ ```javascript
143
+ const {
144
+ dateToDecimalYear,
145
+ gregorianToJulian,
146
+ resolveDecimalYear
147
+ } = require('magvar/utils');
148
+
149
+ dateToDecimalYear(new Date('2025-07-02T12:00:00Z')); // ~2025.5
150
+ gregorianToJulian(2025, 0, 1); // 2460676.5 (00:00 UTC; month is 0-indexed)
151
+ resolveDecimalYear(2026.25);
152
+ resolveDecimalYear(new Date());
153
+ ```
154
+
155
+ `gregorianToJulian` always uses UTC, so results do not depend on the process timezone.
156
+
157
+ ## Examples
158
+
159
+ **Correct a magnetic heading to true heading**
160
+
161
+ ```javascript
162
+ const { magvar } = require('magvar');
163
+
164
+ const magneticHeading = 90; // degrees
165
+ const variation = magvar(37.77, -122.42); // San Francisco
166
+ const trueHeading = magneticHeading + variation;
167
+ ```
168
+
169
+ **Compare sea level vs aircraft altitude**
170
+
171
+ ```javascript
172
+ const { magneticField } = require('magvar');
173
+
174
+ const surface = magneticField(0, 120, 0, 2025.0);
175
+ const flightLevel = magneticField(0, 120, 10, 2025.0); // 10 km
176
+ console.log(surface.declination, flightLevel.declination);
177
+ ```
178
+
179
+ ## Migrating
180
+
181
+ ### From 2.0 to 2.1
182
+
183
+ - `julianDaysNow` is no longer exported. Pass a `Date` / decimal year into `magvar`, or use `dateToDecimalYear`.
184
+ - `magvar()` now uses the **current** UTC time on every call (it is not frozen at module load).
185
+ - The unused WMM 2020 coefficient file was removed from the published package.
186
+
187
+ ### From 1.x to 2.x
188
+
189
+ - Import style and method name changed: use `{ magvar }` instead of the old `get` API.
190
+ - Coefficients were updated from WMM2020 to **WMM2025**.
191
+
192
+ ## Development and tests
193
+
194
+ Clone the repo, install dependencies, and run the suite:
195
+
196
+ ```bash
197
+ npm install
198
+ npm test
199
+ ```
200
+
201
+ Tests use the official NOAA/NCEI [`WMM2025_TEST_VALUES.txt`](https://www.ncei.noaa.gov/sites/default/files/2025-02/WMM2025_TEST_VALUES.txt) document (vendored at `test/fixtures/WMM2025_TEST_VALUES.txt`) as the primary fixture, plus an extended regression set spanning 2025.0–2029.5.
202
+
203
+ ## References
204
+
205
+ - [World Magnetic Model (NCEI)](https://www.ncei.noaa.gov/products/world-magnetic-model)
206
+ - [WMM2025 test values](https://www.ncei.noaa.gov/sites/default/files/2025-02/WMM2025_TEST_VALUES.txt)
207
+
42
208
  ## License
43
209
 
44
- The magvar library is licensed under [The MIT License](LICENSE).
210
+ [MIT](LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magvar",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "Calculates magnetic variation at a specified location based upon the World Magnetic Model 2025-2030.",
5
5
  "author": {
6
6
  "name": "Darren Yeates",
@@ -9,6 +9,17 @@
9
9
  "license": "MIT",
10
10
  "licenseFilename": "LICENSE",
11
11
  "main": "src/magvar.js",
12
+ "exports": {
13
+ ".": "./src/magvar.js",
14
+ "./utils": "./src/utils.js"
15
+ },
16
+ "files": [
17
+ "src/magvar.js",
18
+ "src/utils.js",
19
+ "src/WMMCOF2025.js",
20
+ "LICENSE",
21
+ "README.md"
22
+ ],
12
23
  "scripts": {
13
24
  "test": "jest"
14
25
  },
@@ -30,7 +41,7 @@
30
41
  },
31
42
  "homepage": "https://github.com/dpyeates/magvar#readme",
32
43
  "devDependencies": {
33
- "jest": "^26.1.0"
44
+ "jest": "^29.7.0"
34
45
  },
35
46
  "jest": {
36
47
  "verbose": true,
package/src/WMMCOF2025.js CHANGED
@@ -65,134 +65,12 @@ const htnmWmm = [
65
65
 
66
66
  const julianDaysCOF = 2460677;
67
67
 
68
- const testData = [
69
- /*
70
- # Field 1: Decimal year
71
- # Field 2: Altitude (km)
72
- # Field 3: geodetic latitude (deg)
73
- # Field 4: geodetic longitude (deg)
74
- # Field 5: declination (deg)
75
- # Field 6: inclination (deg)
76
- # Field 7: H (nT)
77
- # Field 8: X (nT)
78
- # Field 9: Y (nT)
79
- # Field 10: Z (nT)
80
- # Field 11: F (nT)
81
- # Field 12: dD/dt (deg/year)
82
- # Field 13: dI/dt (deg/year)
83
- # Field 14: dH/dt (nT/year)
84
- # Field 15: dX/dt (nT/year)
85
- # Field 16: dY/dt (nT/year)
86
- # Field 17: dZ/dt (nT/year)
87
- # Field 18: dF/dt (nT/year)
88
- */
89
- [2025.0, 28, 89, -121, -99.77, 88.47, 1504.298146, -255.388723, -1482.460628, 56194.288771, 56214.419888, 2.491706, -0.009987, 10.28564, 62.723738, -21.242793, 18.075146, 18.343917],
90
- [2025.0, 48, 80, -96, -29.91, 87.77, 2164.285547, 1875.98228, -1079.269389, 55623.044051, 55665.134163, 1.248417, -0.057537, 55.469239, 71.596398, 13.214774, -12.148507, -9.982652],
91
- [2025.0, 54, 82, 87, 54.89, 87.68, 2302.427342, 1324.336929, 1883.42862, 56740.772059, 56787.4668, 0.884574, 0.03613, -34.169939, -48.732002, -7.505574, 41.134755, 39.715524],
92
- [2025.0, 65, 43, 93, 0.5, 64.1, 24300.764692, 24299.852822, 210.517066, 50037.923998, 55626.621348, -0.019813, 0.030822, -3.938347, -3.865401, -8.437166, 60.388819, 52.601187],
93
- [2025.0, 51, -33, 109, -5.49, -67.5, 21838.046477, 21737.778822, -2090.274098, -52710.00392, 57054.752538, 0.069861, 0.026375, 29.809763, 32.221578, 23.651711, -3.33404, 14.490015],
94
- [2025.0, 39, -59, -8, -15.75, -58.55, 14918.115796, 14358.095523, -4049.10754, -24389.086374, 28589.818346, 0.010988, 0.055916, -0.905656, -0.095123, 2.999401, 54.951779, -47.350226],
95
- [2025.0, 3, -50, -103, 27.96, -54.89, 22106.041373, 19526.532799, 10362.99098, -31437.562789, 38431.724126, -0.032104, 0.024888, -42.140973, -31.417096, -30.696074, 88.952383, -97.003616],
96
- [2025.0, 94, -29, -110, 15.74, -38.25, 24181.990098, 23275.47108, 6559.046509, -19063.605287, 30792.688931, -0.011062, 0.027107, -42.453758, -39.595926, -16.008811, 52.018933, -65.544285],
97
- [2025.0, 66, 14, 143, -0.19, 12.82, 35003.635649, 35003.441364, -116.624818, 7966.315182, 35898.700342, -0.055086, -0.003859, 10.848172, 10.735986, -33.689325, -0.010939, 10.575266],
98
- [2025.0, 18, 0, 21, 1.29, -26.06, 29282.246275, 29274.811882, 659.800118, -14316.72254, 32594.761714, 0.030544, 0.079429, -8.761425, -9.110937, 15.408838, 54.58125, -31.844958],
99
- [2025.5, 6, -36, -137, 20.28, -52.11, 25353.230658, 23781.930678, 8786.698927, -32577.518648, 41280.516301, 0.025348, 0.014224, -34.463126, -36.214518, -1.422651, 60.968936, -69.281309],
100
- [2025.5, 63, 26, 81, 0.51, 41.07, 34803.980117, 34802.613433, 308.431881, 30332.056989, 46166.554053, 0.018525, 0.021888, 20.342054, 20.241534, 11.432574, 41.122831, 42.353703],
101
- [2025.5, 69, 38, -144, 12.93, 56.97, 23096.337032, 22510.804524, 5167.636206, 35525.990264, 42373.774537, -0.1053, -0.007297, -33.951051, -23.593092, -48.967345, -62.123761, -70.589724],
102
- [2025.5, 50, -70, -133, 57.21, -71.94, 16656.709403, 9021.847823, 14001.865232, -51084.838301, 53731.803175, -0.036998, 0.045634, 8.590794, 13.694592, 1.395803, 111.703074, -103.537175],
103
- [2025.5, 8, -52, -75, 14.91, -49.63, 20005.506364, 19331.857134, 5147.774727, -23532.664273, 30886.996822, -0.098427, -0.028885, -58.502212, -47.688987, -48.263512, 44.775999, -72.006513],
104
- [2025.5, 8, -66, 17, -33.14, -59.55, 18154.870136, 15201.438652, -9925.501123, -30881.123197, 35822.382382, -0.112991, 0.041932, 10.080437, -11.133189, -35.489343, 34.582697, -24.703647],
105
- [2025.5, 22, -37, 140, 9.28, -68.62, 21688.84759, 21404.761773, 3498.89743, -55397.58776, 59492.006517, 0.028818, 0.004094, 0.393984, -1.37102, 10.829532, 10.653875, -9.77701],
106
- [2025.5, 40, -12, -129, 10.76, -15.46, 29105.866326, 28594.451238, 5432.201489, -8052.525092, 30199.248583, -0.009568, 0.030404, -39.188467, -37.592705, -12.089297, 27.469404, -45.094246],
107
- [2025.5, 44, 33, -118, 11.1, 57.89, 23678.743422, 23235.83585, 4558.379362, 37727.715752, 44542.826874, -0.075717, -0.021971, -41.700619, -34.896682, -38.734089, -98.57311, -105.659134],
108
- [2025.5, 50, -81, -67, 28.13, -67.61, 18292.84272, 16132.682326, 8623.494405, -44412.283868, 48032.062762, -0.099291, 0.019879, -12.853806, 3.608124, -34.016532, 74.964128, -74.210029],
109
- [2026.0, 74, -57, 3, -22.51, -58.65, 14362.206593, 13268.119649, -5498.179626, -23576.062921, 27606.226129, -0.037845, 0.082339, 12.94561, 8.327751, -13.719801, 55.005355, -40.240277],
110
- [2026.0, 46, -24, -122, 14.01, -34.17, 26638.50009, 25846.118652, 6448.863284, -18080.399376, 32194.883578, 0.000271, 0.019932, -40.514451, -39.339842, -9.685735, 41.034418, -56.566841],
111
- [2026.0, 69, 23, 63, 1.17, 35.92, 34565.858527, 34558.605297, 708.078839, 25043.405885, 42684.54936, 0.013234, 0.010624, 23.857027, 23.688475, 8.470762, 27.058242, 35.194683],
112
- [2026.0, 33, -3, -147, 9.71, -2.12, 30957.666082, 30514.086409, 5221.840663, -1146.978999, 30978.906535, -0.006185, 0.036855, -36.361404, -35.276692, -9.427361, 21.287924, -37.124648],
113
- [2026.0, 47, -72, -22, -6.32, -61.16, 18392.516222, 18280.800055, -2024.10532, -33397.48616, 38127.112857, -0.048225, 0.01733, -15.939764, -17.546607, -13.632509, 52.849287, -53.982732],
114
- [2026.0, 62, -14, 99, -1.43, -44.7, 33448.275663, 33437.82863, -835.919473, -33100.922253, 47058.03012, 0.0614, 0.07248, 38.755947, 39.639639, 34.864408, 45.397052, -4.385324],
115
- [2026.0, 83, 86, -46, -30.61, 86.84, 3000.255301, 2582.099403, -1527.839829, 54279.308437, 54362.16383, 1.221388, -0.002547, 3.280093, 35.392266, 53.372894, 15.551936, 15.709262],
116
- [2026.0, 82, -64, 87, -81.74, -75.4, 13974.248411, 2007.07487, -13829.362571, -53663.514288, 55453.154865, -0.18726, -0.029553, -23.144309, -48.522801, 16.344602, -24.623137, 17.996086],
117
- [2026.0, 34, -19, 43, -14.98, -52.33, 20212.448819, 19525.69405, -5224.017527, -26182.86233, 33076.961273, -0.1357, 0.023383, 51.024088, 36.91785, -59.432251, -44.004821, 66.012531],
118
- [2026.0, 56, -81, 40, -59.77, -68.45, 17877.818542, 9000.536024, -15446.90089, -45267.890393, 48670.301997, -0.146798, 0.017031, -1.33047, -40.246526, -21.910829, 42.754262, -40.25414],
119
- [2026.5, 14, 0, 80, -3.1, -17.15, 39489.089663, 39431.415992, -2133.456168, -12188.838466, 41327.424134, 0.056431, 0.021103, 33.618195, 35.670346, 37.019904, 5.553327, 30.484922],
120
- [2026.5, 12, -82, -68, 29.79, -68.07, 18497.480257, 16052.819558, 9190.416754, -45940.146795, 49524.275496, -0.104864, 0.021236, -11.118018, 7.171828, -34.904116, 76.756556, -75.354213],
121
- [2026.5, 44, -46, -42, -11.36, -54.39, 14140.316272, 13863.176808, -2785.834358, -19744.304022, 24285.511845, 0.039043, -0.119143, -63.277988, -60.139435, 21.913412, 1.623579, -38.163791],
122
- [2026.5, 43, 17, 52, 1.19, 23.95, 36002.676553, 35994.907968, 747.876551, 15990.895993, 39394.180708, -0.010785, 0.016571, 20.35104, 20.487426, -6.352799, 21.505886, 27.328663],
123
- [2026.5, 64, 10, 78, -1.53, 7.53, 39441.702532, 39427.72448, -1049.971869, 5214.811025, 39784.948821, 0.031231, 0.021184, 27.794943, 28.357414, 20.751458, 18.512565, 29.981674],
124
- [2026.5, 12, 33, -145, 11.96, 52.51, 24672.289649, 24136.839639, 5112.225422, 32162.934508, 40536.110231, -0.08662, 0.00034, -40.973255, -32.355349, -44.980044, -53.017362, -67.004405],
125
- [2026.5, 12, -79, 115, -137.58, -77.37, 13023.480845, -9613.650418, -8785.714482, -58104.306533, 59545.961164, -0.241722, 0.014808, 7.471685, -42.581077, 35.51814, 37.027107, -34.496496],
126
- [2026.5, 14, -33, -114, 18.12, -44.1, 24613.183584, 23393.133584, 7653.110952, -23854.293436, 34275.882505, -0.000322, 0.022971, -42.803882, -40.639067, -13.440897, 60.620545, -72.925914],
127
- [2026.5, 19, 29, 66, 2.24, 46.04, 32749.959268, 32725.000691, 1278.34338, 33958.454002, 47177.71116, 0.018224, 0.011101, 23.712541, 23.287876, 11.334193, 37.754815, 43.636705],
128
- [2026.5, 86, -11, 167, 10.24, -31.6, 33105.255278, 32578.377044, 5882.794932, -20368.224196, 38869.300019, 0.013426, -0.025948, -16.653141, -17.766581, 4.674631, -10.422224, -8.722161],
129
- [2027.0, 37, -66, -5, -17.22, -59.04, 17159.8365, 16390.771268, -5079.626554, -28608.243575, 33360.029814, -0.04363, 0.039964, -2.074991, -5.850093, -11.867216, 48.69553, -42.826703],
130
- [2027.0, 67, 72, -115, 13.73, 84.84, 5026.868699, 4883.287833, 1192.857435, 55689.615237, 55916.032175, -0.315901, -0.072636, 66.581032, 71.25614, -11.124661, -50.889259, -44.697541],
131
- [2027.0, 44, 22, 174, 6.46, 31.89, 28867.487799, 28683.97251, 3249.857362, 17961.198575, 33999.066253, -0.024375, -0.000168, 2.815221, 4.179894, -11.885941, 1.634357, 3.253718],
132
- [2027.0, 54, 54, 178, 0.63, 65.46, 20617.099795, 20615.871559, 225.041793, 45149.631876, 49634.202547, -0.170729, 0.012944, -3.909066, -3.238256, -61.473597, 18.434524, 15.145168],
133
- [2027.0, 57, -43, 50, -48.27, -63.13, 16833.417225, 11203.748204, -12563.437494, -33221.366617, 37242.759503, -0.165742, -0.033376, 12.323002, -28.141121, -41.606787, -72.317297, 70.078525],
134
- [2027.0, 44, -43, -111, 24.31, -52.57, 22462.470699, 20471.522067, 9245.50562, -29347.556906, 36957.295441, -0.006299, 0.024728, -40.058633, -35.491603, -18.738696, 78.579889, -86.747247],
135
- [2027.0, 12, -63, 178, 57.87, -79.14, 11720.691727, 6233.774935, 9925.455386, -61075.730989, 62190.188377, 0.151793, 0.036783, 27.353301, -11.747306, 39.678751, 69.308326, -62.911163],
136
- [2027.0, 38, 27, -169, 8.48, 42.66, 26106.758229, 25821.061601, 3851.701313, 24058.365347, 35501.658671, -0.058765, 0.013094, -19.135886, -14.976028, -29.306273, -6.601488, -18.545527],
137
- [2027.0, 61, 59, -77, -16.48, 78.68, 10884.812802, 10437.775689, -3087.391846, 54397.713552, 55476.03437, 0.230418, -0.074136, 59.669126, 69.634646, 25.051419, -67.643997, -54.621632],
138
- [2027.0, 67, -47, -32, -13.52, -57.98, 12805.786103, 12450.832543, -2994.148743, -20475.298079, 24150.072239, 0.11555, -0.077078, -52.470761, -44.977997, 37.378183, 22.62727, -47.00729],
139
- [2027.5, 8, 62, 53, 19.39, 76.67, 12997.751893, 12260.425578, 4315.497527, 54849.810218, 56368.814385, 0.099601, 0.030088, -12.884268, -19.655269, 17.035218, 74.003425, 69.038304],
140
- [2027.5, 77, -68, -7, -16.19, -59.82, 17262.817301, 16578.099495, -4813.676173, -29680.383821, 34335.550744, -0.048399, 0.032649, -5.108702, -8.972297, -12.579376, 47.699056, -43.800575],
141
- [2027.5, 98, -5, 159, 7.79, -23.22, 33857.496691, 33544.960996, 4589.735713, -14525.780052, 36841.937629, -0.005671, -0.036102, -11.751062, -11.188278, -4.913401, -20.218524, -2.827531],
142
- [2027.5, 34, -29, -107, 15.64, -37.45, 24446.053109, 23540.396213, 6592.363667, -18723.097084, 30792.269761, -0.017007, 0.030517, -44.335923, -40.736596, -18.943541, 54.61513, -68.406867],
143
- [2027.5, 60, 27, 65, 1.85, 42.83, 33079.422542, 33062.159421, 1068.555172, 30667.425573, 45108.083389, 0.016699, 0.011093, 23.388908, 23.065275, 10.391374, 33.592654, 39.990433],
144
- [2027.5, 73, -72, 95, -102.64, -76.49, 13306.747292, -2912.418045, -12984.118939, -55399.217725, 56974.931751, -0.235287, -0.006801, -8.44255, -51.471801, 20.197772, 6.191978, -7.992526],
145
- [2027.5, 96, -46, -85, 17.93, -47.37, 19914.457641, 18947.658009, 6129.590452, -21631.434316, 29402.458634, -0.091418, -0.005439, -53.229644, -40.865392, -46.615842, 53.698344, -75.558704],
146
- [2027.5, 0, -13, -59, -17.49, -15.26, 22401.49301, 21365.849306, -6732.56062, -6112.31352, 23220.406233, -0.17369, -0.38568, -68.873971, -86.099325, -44.070217, -143.226783, -28.743372],
147
- [2027.5, 16, 66, -178, 0.37, 75.67, 13821.614337, 13821.326215, 89.244328, 54092.646409, 55830.559897, -0.327448, 0.003026, -1.280867, -0.770805, -78.997758, 6.899408, 6.367545],
148
- [2027.5, 72, -87, 38, -65.44, -70.97, 16661.696834, 6926.051596, -15153.941754, -48295.635435, 51088.94737, -0.14831, 0.02134, 0.540463, -39.00136, -18.41966, 56.777921, -53.4973],
149
- [2028.0, 49, 20, 167, 5.1, 26.82, 30251.262453, 30131.436134, 2689.876669, 15295.611788, 33898.298187, -0.020383, -0.011359, 9.497816, 10.417136, -9.874921, -2.728173, 7.244961],
150
- [2028.0, 71, 5, -13, -6.47, -17.66, 28323.200567, 28142.647915, -3192.970202, -9017.928693, 29724.177504, 0.166481, -0.073692, -18.125869, -8.732692, 83.815865, -34.350117, -6.850172],
151
- [2028.0, 95, 14, 65, -0.51, 17.44, 36933.940251, 36932.466767, -329.910612, 11601.180703, 38713.089985, 0.011924, 0.011881, 29.013291, 29.080794, 7.427118, 17.527263, 32.932326],
152
- [2028.0, 86, -85, -79, 41.09, -70.25, 16867.459172, 12713.115116, 11085.480728, -46988.258092, 49924.018042, -0.121705, 0.024621, -3.420695, 20.969078, -29.252744, 73.02543, -69.886927],
153
- [2028.0, 30, -36, -64, -4.65, -40.08, 17398.651081, 17341.333537, -1411.102609, -14639.967305, 22738.551012, -0.162162, -0.152944, -69.721104, -73.485198, -43.42573, -20.660569, -40.045784],
154
- [2028.0, 75, 79, 125, -18.59, 87.42, 2582.332595, 2447.5959, -823.235047, 57308.751756, 57366.902212, -0.910648, 0.022931, -21.221891, -33.198946, -32.13617, 39.08266, 38.087754],
155
- [2028.0, 21, 6, -32, -14.34, -8.7, 28453.070088, 27567.09426, -7045.034525, -4352.046687, 28783.980055, 0.17796, -0.31147, -29.041854, -6.255704, 92.814073, -153.852374, -5.445988],
156
- [2028.0, 1, -76, -75, 29.87, -65.23, 19597.63521, 16993.742946, 9761.147808, -42479.84725, 46782.525885, -0.086171, 0.016127, -22.854241, -5.137167, -36.941289, 80.971555, -83.0983],
157
- [2028.0, 45, -46, -41, -11.68, -54.96, 13897.562372, 13609.973828, -2812.623735, -19816.196211, 24203.798713, 0.048411, -0.117246, -62.721602, -59.047199, 24.193263, 3.174357, -38.612991],
158
- [2028.0, 11, -22, -21, -23.24, -57.67, 13528.270036, 12430.606447, -5337.987778, -21373.837958, 25295.35608, 0.176027, -0.19656, -92.571749, -68.660974, 74.716931, -16.002042, -35.98726],
159
- [2028.5, 28, 54, -120, 15.43, 73.74, 15286.094495, 14735.145798, 4066.959949, 52393.678137, 54578.03765, -0.147623, -0.053732, 20.801434, 30.530225, -32.430768, -111.448817, -101.162318],
160
- [2028.5, 68, -58, 156, 41.57, -81.52, 9282.943032, 6944.959164, 6159.591995, -62264.415929, 62952.605366, 0.218259, 0.014957, 11.274695, -15.02895, 33.936954, 35.824068, -33.769886],
161
- [2028.5, 39, -65, -88, 29.45, -60.2, 20609.270068, 17946.905687, 10131.662696, -35982.872979, 41466.964689, -0.076076, 0.014457, -36.693634, -18.500934, -41.868201, 85.117374, -92.097328],
162
- [2028.5, 27, -23, 81, -13.27, -58.58, 25625.329284, 24940.672257, -5883.907569, -41948.733657, 49156.421313, 0.129297, -0.007968, 18.629651, 31.409843, 52.004836, -43.610533, 46.927695],
163
- [2028.5, 11, 34, 0, 1.57, 46.77, 29089.194286, 29078.234597, 798.434048, 30945.577813, 42471.284539, 0.111314, 0.010464, 15.380882, 13.823891, 56.915296, 27.686933, 30.707939],
164
- [2028.5, 72, -62, 65, -67.87, -68.5, 17434.847799, 6567.891205, -16150.440331, -44267.327811, 47576.992646, -0.176819, -0.022421, -7.894025, -52.815238, -12.956527, -30.762466, 25.729685],
165
- [2028.5, 55, 86, 70, 67.64, 87.57, 2370.361097, 901.741754, 2192.139033, 55926.154052, 55976.36393, 1.341225, 0.014915, -13.210618, -56.340956, 8.891342, 32.41574, 31.82725],
166
- [2028.5, 59, 32, 163, 0.15, 43.1, 28217.204381, 28217.104175, 75.200106, 26405.862611, 38645.571587, -0.043407, 0.005164, 13.00012, 13.057045, -21.342658, 16.9364, 21.064439],
167
- [2028.5, 65, 48, 148, -9.55, 61.79, 23693.546804, 23365.16701, -3931.047029, 44177.553125, 50130.233993, -0.040524, 0.019409, 4.495479, 1.652822, -17.271572, 44.310648, 41.173752],
168
- [2028.5, 95, 30, 28, 4.56, 44.27, 29786.406936, 29692.133298, 2367.965026, 29039.808439, 41599.765773, 0.034245, 0.035803, 8.349023, 6.90731, 18.410171, 44.444119, 37.00348],
169
- [2029.0, 95, -60, -59, 8.58, -55.17, 18095.598879, 17893.018948, 2700.105868, -26011.845842, 31687.013474, -0.053323, -0.035373, -49.689333, -46.620155, -24.066802, 37.170306, -58.889314],
170
- [2029.0, 95, -70, 42, -55.06, -64.54, 18202.66048, 10426.249753, -14920.796381, -38237.047006, 42348.655378, -0.157751, 0.012766, 3.604264, -39.016467, -31.660684, 14.381378, -11.435882],
171
- [2029.0, 50, 87, -154, -73.48, 89.07, 906.920459, 257.87742, -869.484879, 55992.308183, 55999.652503, 1.562405, -0.064156, 62.930173, 41.603892, -53.300475, 13.406039, 14.423442],
172
- [2029.0, 58, 32, 19, 4.11, 46.03, 29434.989012, 29359.395781, 2108.18821, 30508.465542, 42393.219362, 0.056569, 0.028397, 10.5931, 8.484435, 29.745872, 41.240139, 37.03378],
173
- [2029.0, 57, 34, -13, -1.89, 45.74, 28257.277809, 28241.863242, -933.225495, 28997.458189, 40488.595068, 0.154151, -0.026819, 21.195169, 23.694393, 75.28301, -5.404499, 10.92162],
174
- [2029.0, 38, -76, 49, -64.28, -67.36, 18412.640681, 7991.121323, -16588.167977, -44151.303941, 47836.837024, -0.160186, 0.012467, -0.041101, -46.394534, -22.304306, 27.140173, -25.06501],
175
- [2029.0, 49, -50, -179, 32.11, -71.33, 18080.59379, 15315.042731, 9610.272521, -53522.651338, 56494.088877, 0.110906, 0.012602, -5.111774, -22.932228, 26.927867, 53.95627, -52.754307],
176
- [2029.0, 90, -55, -171, 38.65, -72.79, 16416.538468, 12821.507915, 10252.398258, -52995.143972, 55479.618058, 0.117199, 0.024083, 2.622098, -18.923406, 27.863957, 70.344114, -66.418097],
177
- [2029.0, 41, 42, -19, -4.13, 56.44, 24503.475397, 24439.977735, -1762.893879, 36929.897501, 44319.720622, 0.180959, -0.034375, 24.712482, 30.216258, 75.411786, -10.849192, 4.622821],
178
- [2029.0, 19, 46, -22, -5.65, 60.89, 22632.016516, 22522.142232, -2227.39329, 40651.688191, 46527.066578, 0.196585, -0.034725, 25.397648, 32.916674, 74.775309, -12.351198, 1.562595],
179
- [2029.5, 31, 13, -132, 9.04, 31.41, 28145.022897, 27795.620707, 4421.061344, 17187.98359, 32978.312476, -0.039071, 0.025976, -57.60425, -53.874337, -28.002848, -17.659477, -58.365744],
180
- [2029.5, 93, -2, 158, 7.09, -17.84, 34067.30102, 33806.520595, 4207.156292, -10964.647547, 35788.329028, -0.010065, -0.038403, -8.149803, -7.348347, -6.945244, -22.576489, -0.841015],
181
- [2029.5, 51, -76, 40, -56.34, -66.22, 18517.441118, 10263.650094, -15412.758102, -42018.541264, 45917.898858, -0.148779, 0.015688, 0.140771, -39.943896, -26.768531, 30.856345, -28.179249],
182
- [2029.5, 64, 22, -132, 10.23, 43.76, 26014.780783, 25601.266218, 4619.955329, 24914.378368, 36020.758857, -0.057237, 0.010172, -55.619345, -50.120044, -35.452393, -44.412257, -70.887703],
183
- [2029.5, 26, -65, 55, -63.48, -65.71, 18695.741849, 8347.198524, -16728.868464, -41431.528035, 45454.397791, -0.179396, -0.005003, 1.416835, -51.746426, -27.403316, -12.788863, 12.23976],
184
- [2029.5, 66, -21, 32, -14.63, -56.68, 16100.322907, 15578.335534, -4066.430831, -24494.877642, 29312.444941, -0.198629, 0.076846, 37.723336, 22.403071, -63.533684, 14.184486, 8.866905],
185
- [2029.5, 18, 9, -172, 9.24, 15.85, 30922.51441, 30520.999126, 4966.941695, 8779.472279, 32144.689001, -5.3e-05, 0.016657, -17.13715, -16.910013, -2.781046, 4.848633, -15.161302],
186
- [2029.5, 63, 88, 26, 36.52, 87.37, 2539.900024, 2041.140972, 1511.567287, 55286.620082, 55344.931586, 1.375737, 0.002257, -1.029565, -37.121853, 48.397411, 25.084712, 25.011033],
187
- [2029.5, 33, 17, 5, 0.89, 13.77, 34026.126581, 34021.976056, 531.446418, 8341.137769, 35033.582023, 0.078642, 0.026998, 3.099752, 2.369935, 46.745426, 17.756374, 7.238224],
188
- [2029.5, 77, -18, 138, 4.45, -47.55, 31847.600506, 31751.49758, 2472.257962, -34817.395113, 47186.021876, -0.03658, 0.012491, 0.895474, 2.471158, -20.201885, 14.262673, -9.919684]
189
- ];
190
-
191
68
  module.exports = {
192
69
  gnmWmm,
193
70
  hnmWmm,
194
71
  gtnmWmm,
195
72
  htnmWmm,
196
73
  julianDaysCOF,
197
- testData
74
+ MODEL_EPOCH: 2025.0,
75
+ MODEL_VALID_UNTIL: 2030.0
198
76
  };
package/src/magvar.js CHANGED
@@ -1,70 +1,115 @@
1
- const { gnmWmm, gtnmWmm, hnmWmm, htnmWmm, julianDaysCOF } = require('./WMMCOF2025');
2
- const { deg2rad, rad2deg, zeroArray2D, roundToDecimalPlace, gregorianToJulian } = require('./utils');
1
+ const {
2
+ gnmWmm,
3
+ gtnmWmm,
4
+ hnmWmm,
5
+ htnmWmm,
6
+ julianDaysCOF,
7
+ MODEL_EPOCH,
8
+ MODEL_VALID_UNTIL
9
+ } = require('./WMMCOF2025');
10
+ const {
11
+ deg2rad,
12
+ rad2deg,
13
+ zeroArray2D,
14
+ roundToDecimalPlace,
15
+ resolveDecimalYear
16
+ } = require('./utils');
17
+
18
+ const MAX_N = 12;
19
+ const SIZE = MAX_N + 1;
3
20
 
4
21
  const globe = {
5
- a: 6378.137, // semi-major axis [equatorial radius] of WGS84 ellipsoid
6
- b: 6356.7523142, // semi-minor axis referenced to the WGS84 ellipsoid
7
- r0: 6371.2 // "mean radius" for spherical harmonic expansion
22
+ a: 6378.137, // semi-major axis [equatorial radius] of WGS84 ellipsoid (km)
23
+ b: 6356.7523142, // semi-minor axis referenced to the WGS84 ellipsoid (km)
24
+ r0: 6371.2 // "mean radius" for spherical harmonic expansion (km)
8
25
  };
9
26
 
10
- const P = zeroArray2D(13, 13);
11
- const DP = zeroArray2D(13, 13);
12
- const gnm = zeroArray2D(13, 13);
13
- const hnm = zeroArray2D(13, 13);
14
- const sm = new Float32Array(13);
15
- const cm = new Float32Array(13);
16
- const root = new Float32Array(13);
17
- const roots = zeroArray2D(13, 13).map(row => row.map(_ => new Float32Array(2)));
27
+ // Scratch arrays reused across calls (single-threaded JS; not worker-safe).
28
+ const P = zeroArray2D(SIZE, SIZE);
29
+ const DP = zeroArray2D(SIZE, SIZE);
30
+ const gnm = zeroArray2D(SIZE, SIZE);
31
+ const hnm = zeroArray2D(SIZE, SIZE);
32
+ const sm = new Array(SIZE).fill(0);
33
+ const cm = new Array(SIZE).fill(0);
34
+ const root = new Array(SIZE).fill(0);
35
+ const roots = zeroArray2D(SIZE, SIZE).map((row) => row.map(() => [0, 0]));
18
36
 
19
- for (let n = 2; n <= 12; n++) {
37
+ for (let n = 2; n <= MAX_N; n++) {
20
38
  root[n] = Math.sqrt((2.0 * n - 1) / (2.0 * n));
21
39
  }
22
40
 
23
- for (let m = 0; m <= 12; m++) {
41
+ for (let m = 0; m <= MAX_N; m++) {
24
42
  const mm = m * m;
25
- for (let n = Math.max(m + 1, 2); n <= 12; n++) {
43
+ for (let n = Math.max(m + 1, 2); n <= MAX_N; n++) {
26
44
  roots[m][n][0] = Math.sqrt((n - 1) * (n - 1) - mm);
27
45
  roots[m][n][1] = 1.0 / Math.sqrt(n * n - mm);
28
46
  }
29
47
  }
30
48
 
31
- const d = new Date();
32
- const julianDaysNow = gregorianToJulian(d.getFullYear(),d.getMonth(),d.getDate());
49
+ let cachedDecimalYear = Number.NaN;
50
+ let warnedOutsideValidity = false;
33
51
 
34
- /**
35
- * magvar
36
- * Given a latitude and longitude position and optional height in metres above mean sea level,
37
- * return magnetic variation in degrees for the current date.
38
- * N and E latitude and longitude are positive values, S and W negative.
39
- * @param {number} latitude the latitude in degrees of the point we want to obtain the magnetic variation.
40
- * @param {number} longitude the longitude in degrees of the point we want to obtain the magnetic variation.
41
- * @param {number} altitude the height in km above mean sea level of the point we want to obtain the magnetic variation.
42
- * @returns {number} magnetic variation at the given coordinates and height for the current date.
43
- */
44
- const magvar = (latitude, longitude, altitude = 0) => calculateMagVar(julianDaysNow, latitude, longitude, altitude);
52
+ const warnIfOutsideValidity = (decimalYear) => {
53
+ if (
54
+ !warnedOutsideValidity &&
55
+ (decimalYear < MODEL_EPOCH || decimalYear >= MODEL_VALID_UNTIL)
56
+ ) {
57
+ warnedOutsideValidity = true;
58
+ console.warn(
59
+ `magvar: decimal year ${decimalYear} is outside the WMM ${MODEL_EPOCH}-${MODEL_VALID_UNTIL} validity period; results may be inaccurate.`
60
+ );
61
+ }
62
+ };
63
+
64
+ const updateSecularVariation = (decimalYear) => {
65
+ if (decimalYear === cachedDecimalYear) {
66
+ return;
67
+ }
68
+ cachedDecimalYear = decimalYear;
69
+ const yearFrac = decimalYear - MODEL_EPOCH;
70
+ for (let n = 1; n <= MAX_N; n++) {
71
+ for (let m = 0; m <= n; m++) {
72
+ gnm[n][m] = gnmWmm[n][m] + yearFrac * gtnmWmm[n][m];
73
+ hnm[n][m] = hnmWmm[n][m] + yearFrac * htnmWmm[n][m];
74
+ }
75
+ }
76
+ };
45
77
 
46
78
  /**
47
- * calculateMagVar
48
- * Given a date in julian days, latitude, longitude and height, return variation (in degrees)
49
- * N and E latitude and longitude are positive values, South and West negative.
50
- * @param {number} julianDays the current number of days since epoch.
51
- * @param {number} latitude the latitude in degrees of the point we want to obtain the magnetic variation.
52
- * @param {number} longitude the longitude in degrees of the point we want to obtain the magnetic variation.
53
- * @param {number} altitude the height in km above mean sea level of the point we want to obtain the magnetic variation.
54
- * @returns {number} magnetic variation at the given coordinates and height.
79
+ * Compute geomagnetic field components for a decimal year.
80
+ * @param {number} decimalYear WMM decimal year (e.g. 2025.0, 2027.5)
81
+ * @param {number} latitude geodetic latitude in degrees (N positive)
82
+ * @param {number} longitude geodetic longitude in degrees (E positive)
83
+ * @param {number} [altitude=0] height in kilometers above mean sea level
84
+ * @returns {{
85
+ * declination: number,
86
+ * inclination: number,
87
+ * x: number,
88
+ * y: number,
89
+ * z: number,
90
+ * h: number,
91
+ * f: number,
92
+ * decimalYear: number
93
+ * }} field components (angles in degrees, intensities in nT)
55
94
  */
56
- const calculateMagVar = (julianDays, latitude, longitude, altitude = 0) => {
95
+ const calculateMagneticField = (decimalYear, latitude, longitude, altitude = 0) => {
96
+ warnIfOutsideValidity(decimalYear);
97
+ updateSecularVariation(decimalYear);
98
+
57
99
  const latRad = deg2rad(latitude);
58
100
  const lonRad = deg2rad(longitude);
59
101
  const sinLat = Math.sin(latRad);
60
102
  const cosLat = Math.cos(latRad);
61
103
  const sr = Math.sqrt(globe.a ** 2 * cosLat ** 2 + globe.b ** 2 * sinLat ** 2);
62
- const theta = Math.atan2(cosLat * (altitude * sr + globe.a ** 2), sinLat * (altitude * sr + globe.b ** 2));
104
+ const theta = Math.atan2(
105
+ cosLat * (altitude * sr + globe.a ** 2),
106
+ sinLat * (altitude * sr + globe.b ** 2)
107
+ );
63
108
  const r = Math.sqrt(
64
109
  altitude ** 2 +
65
- 2 * altitude * sr +
66
- (globe.a ** 4 - (globe.a ** 4 - globe.b ** 4) * sinLat ** 2) /
67
- (globe.a ** 2 - (globe.a ** 2 - globe.b ** 2) * sinLat ** 2)
110
+ 2 * altitude * sr +
111
+ (globe.a ** 4 - (globe.a ** 4 - globe.b ** 4) * sinLat ** 2) /
112
+ (globe.a ** 2 - (globe.a ** 2 - globe.b ** 2) * sinLat ** 2)
68
113
  );
69
114
  const c = Math.cos(theta);
70
115
  const s = Math.sin(theta);
@@ -77,28 +122,24 @@ const calculateMagVar = (julianDays, latitude, longitude, altitude = 0) => {
77
122
  P[1][0] = c;
78
123
  DP[1][0] = -s;
79
124
 
80
- for (let n = 2; n <= 12; n++) {
125
+ for (let n = 2; n <= MAX_N; n++) {
81
126
  P[n][n] = P[n - 1][n - 1] * s * root[n];
82
127
  DP[n][n] = (DP[n - 1][n - 1] * s + P[n - 1][n - 1] * c) * root[n];
83
128
  }
84
129
 
85
- for (let m = 0; m <= 12; m++) {
86
- for (let n = Math.max(m + 1, 2); n <= 12; n++) {
87
- P[n][m] = (P[n - 1][m] * c * (2 * n - 1) - P[n - 2][m] * roots[m][n][0]) * roots[m][n][1];
88
- DP[n][m] = ((DP[n - 1][m] * c - P[n - 1][m] * s) * (2 * n - 1) - DP[n - 2][m] * roots[m][n][0]) *
130
+ for (let m = 0; m <= MAX_N; m++) {
131
+ for (let n = Math.max(m + 1, 2); n <= MAX_N; n++) {
132
+ P[n][m] =
133
+ (P[n - 1][m] * c * (2 * n - 1) - P[n - 2][m] * roots[m][n][0]) *
134
+ roots[m][n][1];
135
+ DP[n][m] =
136
+ ((DP[n - 1][m] * c - P[n - 1][m] * s) * (2 * n - 1) -
137
+ DP[n - 2][m] * roots[m][n][0]) *
89
138
  roots[m][n][1];
90
139
  }
91
140
  }
92
141
 
93
- const yearFrac = (julianDays - julianDaysCOF) / 365.25;
94
- for (let n = 1; n <= 12; n++) {
95
- for (let m = 0; m <= 12; m++) {
96
- gnm[n][m] = gnmWmm[n][m] + yearFrac * gtnmWmm[n][m];
97
- hnm[n][m] = hnmWmm[n][m] + yearFrac * htnmWmm[n][m];
98
- }
99
- }
100
-
101
- for (let m = 0; m <= 12; m++) {
142
+ for (let m = 0; m <= MAX_N; m++) {
102
143
  sm[m] = Math.sin(m * lonRad);
103
144
  cm[m] = Math.cos(m * lonRad);
104
145
  }
@@ -109,7 +150,7 @@ const calculateMagVar = (julianDays, latitude, longitude, altitude = 0) => {
109
150
  const fn0 = globe.r0 / r;
110
151
  let fn = fn0 ** 2;
111
152
 
112
- for (let n = 1; n <= 12; n++) {
153
+ for (let n = 1; n <= MAX_N; n++) {
113
154
  let c1n = 0;
114
155
  let c2n = 0;
115
156
  let c3n = 0;
@@ -128,16 +169,101 @@ const calculateMagVar = (julianDays, latitude, longitude, altitude = 0) => {
128
169
  const psi = theta - (Math.PI / 2 - latRad);
129
170
  const sinPsi = Math.sin(psi);
130
171
  const cosPsi = Math.cos(psi);
131
- const X = -BTheta * cosPsi - BR * sinPsi;
132
- const Y = BPhi;
172
+ const x = -BTheta * cosPsi - BR * sinPsi;
173
+ const y = BPhi;
174
+ const z = BTheta * sinPsi - BR * cosPsi;
175
+ const h = Math.hypot(x, y);
176
+ const f = Math.hypot(h, z);
177
+ const declination =
178
+ x !== 0.0 || y !== 0.0 ? rad2deg(Math.atan2(y, x)) : 0.0;
179
+ const inclination = h !== 0.0 || z !== 0.0 ? rad2deg(Math.atan2(z, h)) : 0.0;
133
180
 
134
- return X !== 0.0 || Y !== 0.0 ?
135
- roundToDecimalPlace(rad2deg(Math.atan2(Y, X)), 2) :
136
- 0.0;
181
+ return {
182
+ declination: roundToDecimalPlace(declination, 2),
183
+ inclination: roundToDecimalPlace(inclination, 2),
184
+ x: roundToDecimalPlace(x, 1),
185
+ y: roundToDecimalPlace(y, 1),
186
+ z: roundToDecimalPlace(z, 1),
187
+ h: roundToDecimalPlace(h, 1),
188
+ f: roundToDecimalPlace(f, 1),
189
+ decimalYear
190
+ };
137
191
  };
138
192
 
193
+ /**
194
+ * Given a decimal year, latitude, longitude and optional height in km above MSL,
195
+ * return magnetic variation (declination) in degrees.
196
+ * @param {number} decimalYear
197
+ * @param {number} latitude
198
+ * @param {number} longitude
199
+ * @param {number} [altitude=0] height in kilometers above mean sea level
200
+ * @returns {number} magnetic variation in degrees
201
+ */
202
+ const calculateMagVarForDecimalYear = (
203
+ decimalYear,
204
+ latitude,
205
+ longitude,
206
+ altitude = 0
207
+ ) => calculateMagneticField(decimalYear, latitude, longitude, altitude).declination;
208
+
209
+ /**
210
+ * Given a Julian day, latitude, longitude and optional height in km above MSL,
211
+ * return magnetic variation (declination) in degrees.
212
+ * Prefer `magvar` or `calculateMagVarForDecimalYear` for new code.
213
+ * @param {number} julianDays Julian Day Number
214
+ * @param {number} latitude
215
+ * @param {number} longitude
216
+ * @param {number} [altitude=0] height in kilometers above mean sea level
217
+ * @returns {number} magnetic variation in degrees
218
+ */
219
+ const calculateMagVar = (julianDays, latitude, longitude, altitude = 0) => {
220
+ const decimalYear = MODEL_EPOCH + (julianDays - julianDaysCOF) / 365.25;
221
+ return calculateMagVarForDecimalYear(
222
+ decimalYear,
223
+ latitude,
224
+ longitude,
225
+ altitude
226
+ );
227
+ };
228
+
229
+ /**
230
+ * Magnetic variation (declination) for a location.
231
+ * North and east latitudes/longitudes are positive; south and west are negative.
232
+ * @param {number} latitude degrees
233
+ * @param {number} longitude degrees
234
+ * @param {number} [altitude=0] height in kilometers above mean sea level
235
+ * @param {number|Date} [when] decimal year (e.g. 2026.5) or Date; defaults to current UTC time
236
+ * @returns {number} magnetic variation in degrees
237
+ */
238
+ const magvar = (latitude, longitude, altitude = 0, when) =>
239
+ calculateMagVarForDecimalYear(
240
+ resolveDecimalYear(when),
241
+ latitude,
242
+ longitude,
243
+ altitude
244
+ );
245
+
246
+ /**
247
+ * Full geomagnetic field for a location.
248
+ * @param {number} latitude degrees
249
+ * @param {number} longitude degrees
250
+ * @param {number} [altitude=0] height in kilometers above mean sea level
251
+ * @param {number|Date} [when] decimal year or Date; defaults to current UTC time
252
+ */
253
+ const magneticField = (latitude, longitude, altitude = 0, when) =>
254
+ calculateMagneticField(
255
+ resolveDecimalYear(when),
256
+ latitude,
257
+ longitude,
258
+ altitude
259
+ );
260
+
139
261
  module.exports = {
140
262
  magvar,
263
+ magneticField,
141
264
  calculateMagVar,
142
- julianDaysNow
265
+ calculateMagVarForDecimalYear,
266
+ calculateMagneticField,
267
+ MODEL_EPOCH,
268
+ MODEL_VALID_UNTIL
143
269
  };
package/src/utils.js CHANGED
@@ -6,16 +6,64 @@ const roundToDecimalPlace = (value, decimals) => {
6
6
  return Math.round(value * factor) / factor;
7
7
  };
8
8
 
9
- const gregorianToJulian = (year, month, day) => {
10
- return (new Date(year, month, day).valueOf() / 86400000) + 2440587.5;
11
- }
9
+ /**
10
+ * Convert a Gregorian calendar date to Julian Day Number at 00:00 UTC.
11
+ * @param {number} year full year (e.g. 2025)
12
+ * @param {number} month 0-indexed month (0 = January), matching `Date.getUTCMonth()`
13
+ * @param {number} day day of month (1-31)
14
+ * @returns {number} Julian Day Number at 00:00 UTC
15
+ */
16
+ const gregorianToJulian = (year, month, day) =>
17
+ Date.UTC(year, month, day) / 86400000 + 2440587.5;
12
18
 
13
- const zeroArray2D = (rows, columns) => Array.from({ length: rows }, () => Array(columns).fill(0));
19
+ /**
20
+ * Convert a Date to a decimal year in UTC (e.g. mid-2025 → ~2025.5).
21
+ * @param {Date} [date]
22
+ * @returns {number}
23
+ */
24
+ const dateToDecimalYear = (date = new Date()) => {
25
+ const year = date.getUTCFullYear();
26
+ const start = Date.UTC(year, 0, 1);
27
+ const next = Date.UTC(year + 1, 0, 1);
28
+ const current = Date.UTC(
29
+ year,
30
+ date.getUTCMonth(),
31
+ date.getUTCDate(),
32
+ date.getUTCHours(),
33
+ date.getUTCMinutes(),
34
+ date.getUTCSeconds(),
35
+ date.getUTCMilliseconds()
36
+ );
37
+ return year + (current - start) / (next - start);
38
+ };
39
+
40
+ /**
41
+ * Resolve a date input to a WMM decimal year.
42
+ * @param {number|Date} [when] decimal year, Date, or omit for now
43
+ * @returns {number}
44
+ */
45
+ const resolveDecimalYear = (when) => {
46
+ if (when === undefined || when === null) {
47
+ return dateToDecimalYear(new Date());
48
+ }
49
+ if (when instanceof Date) {
50
+ return dateToDecimalYear(when);
51
+ }
52
+ if (typeof when === 'number' && Number.isFinite(when)) {
53
+ return when;
54
+ }
55
+ throw new TypeError('when must be a decimal year number or Date');
56
+ };
57
+
58
+ const zeroArray2D = (rows, columns) =>
59
+ Array.from({ length: rows }, () => Array(columns).fill(0));
14
60
 
15
61
  module.exports = {
16
62
  deg2rad: (degrees) => degrees * DEG_TO_RAD,
17
63
  rad2deg: (radians) => radians * RAD_TO_DEG,
18
64
  roundToDecimalPlace,
19
65
  gregorianToJulian,
66
+ dateToDecimalYear,
67
+ resolveDecimalYear,
20
68
  zeroArray2D
21
69
  };
package/src/WMMCOF2020.js DELETED
@@ -1,266 +0,0 @@
1
- // WMM _2020 coefficients
2
- const gnmWmm = [
3
- [
4
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0
5
- ],
6
- [
7
- -29404.5, -1450.7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 1
8
- ],
9
- [
10
- -2500.0, 2982.0, 1676.8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 2
11
- ],
12
- [
13
- 1363.9, -2381.0, 1236.2, 525.7, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 3
14
- ],
15
- [
16
- 903.1, 809.4, 86.2, -309.4, 47.9, 0, 0, 0, 0, 0, 0, 0, 0 // 4
17
- ],
18
- [
19
- -234.4, 363.1, 187.8, -140.7, -151.2, 13.7, 0, 0, 0, 0, 0, 0, 0 // 5
20
- ],
21
- [
22
- 65.9, 65.6, 73.0, -121.5, -36.2, 13.5, -64.7, 0, 0, 0, 0, 0, 0 // 6
23
- ],
24
- [
25
- 80.6, -76.8, -8.3, 56.5, 15.8, 6.4, -7.2, 9.8, 0, 0, 0, 0, 0 // 7
26
- ],
27
- [
28
- 23.6, 9.8, -17.5, -0.4, -21.1, 15.3, 13.7, -16.5, -0.3, 0, 0, 0, 0 // 8
29
- ],
30
- [
31
- 5.0, 8.2, 2.9, -1.4, -1.1, -13.3, 1.1, 8.9, -9.3, -11.9, 0, 0, 0 // 9
32
- ],
33
- [
34
- -1.9, -6.2, -0.1, 1.7, -0.9, 0.6, -0.9, 1.9, 1.4, -2.4, -3.9, 0, 0 // 10
35
- ],
36
- [
37
- 3.0, -1.4, -2.5, 2.4, -0.9, 0.3, -0.7, -0.1, 1.4, -0.6, 0.2, 3.1, 0 // 11
38
- ],
39
- [
40
- -2.0, -0.1, 0.5, 1.3, -1.2, 0.7, 0.3, 0.5, -0.2, -0.5, 0.1, -1.1, -0.3 // 12
41
- ]
42
- ];
43
-
44
- const hnmWmm = [
45
- [
46
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0
47
- ], [
48
- 0, 4652.9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 1
49
- ], [
50
- 0, -2991.6, -734.8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 2
51
- ], [
52
- 0, -82.2, 241.8, -542.9, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 3
53
- ], [
54
- 0, 282.0, -158.4, 199.8, -350.1, 0, 0, 0, 0, 0, 0, 0, 0 // 4
55
- ], [
56
- 0, 47.7, 208.4, -121.3, 32.2, 99.1, 0, 0, 0, 0, 0, 0, 0 // 5
57
- ], [
58
- 0, -19.1, 25.0, 52.7, -64.4, 9.0, 68.1, 0, 0, 0, 0, 0, 0 // 6
59
- ], [
60
- 0, -51.4, -16.8, 2.3, 23.5, -2.2, -27.2, -1.9, 0, 0, 0, 0, 0 // 7
61
- ], [
62
- 0, 8.4, -15.3, 12.8, -11.8, 14.9, 3.6, -6.9, 2.8, 0, 0, 0, 0 // 8
63
- ], [
64
- 0, -23.3, 11.1, 9.8, -5.1, -6.2, 7.8, 0.4, -1.5, 9.7, 0, 0, 0 // 9
65
- ], [
66
- 0, 3.4, -0.2, 3.5, 4.8, -8.6, -0.1, -4.2, -3.4, -0.1, -8.8, 0, 0 // 10
67
- ], [
68
- 0, -0, 2.6, -0.5, -0.4, 0.6, -0.2, -1.7, -1.6, -3.0, -2.0, -2.6, 0 // 11
69
- ], [
70
- 0, -1.2, 0.5, 1.3, -1.8, 0.1, 0.7, -0.1, 0.6, 0.2, -0.9, -0.0, 0.5 // 12
71
- ]
72
- ];
73
-
74
- const gtnmWmm = [
75
- [
76
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0
77
- ], [
78
- 6.7, 7.7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 1
79
- ], [
80
- -11.5, -7.1, -2.2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 2
81
- ], [
82
- 2.8, -6.2, 3.4, -12.2, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 3
83
- ], [
84
- -1.1, -1.6, -6.0, 5.4, -5.5, 0, 0, 0, 0, 0, 0, 0, 0 // 4
85
- ], [
86
- -0.3, 0.6, -0.7, 0.1, 1.2, 1.0, 0, 0, 0, 0, 0, 0, 0 // 5
87
- ], [
88
- -0.6, -0.4, 0.5, 1.4, -1.4, 0, 0.8, 0, 0, 0, 0, 0, 0 // 6
89
- ], [
90
- -0.1, -0.3, -0.1, 0.7, 0.2, -0.5, -0.8, 1, 0, 0, 0, 0, 0 // 7
91
- ], [
92
- -0.1, 0.1, -0.1, 0.5, -0.1, 0.4, 0.5, 0, 0.4, 0, 0, 0, 0 // 8
93
- ], [
94
- -0.1, -0.2, -0, 0.4, -0.3, 0, 0.3, -0, 0, -0.4, 0, 0, 0 // 9
95
- ], [
96
- 0, -0, -0, 0.2, -0.1, -0.2, -0, -0.1, -0.2, -0.1, -0, 0, 0 // 10
97
- ], [
98
- 0, -0.1, 0, 0, 0, -0.1, 0, 0, -0.1, -0.1, -0.1, -0.1, 0 // 11
99
- ], [
100
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.1 //12
101
- ]
102
- ];
103
-
104
- const htnmWmm = [
105
- [
106
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0
107
- ], [
108
- 0, -25.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 1
109
- ], [
110
- 0, -30.2, -23.9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 2
111
- ], [
112
- 0, 5.7, -1.0, 1.1, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 3
113
- ], [
114
- 0, 0.2, 6.9, 3.7, -5.6, 0, 0, 0, 0, 0, 0, 0, 0 // 4
115
- ], [
116
- 0, 0.1, 2.5, -0.9, 3.0, 0.5, 0, 0, 0, 0, 0, 0, 0 // 5
117
- ], [
118
- 0, 0.1, -1.8, -1.4, 0.9, 0.1, 1.0, 0, 0, 0, 0, 0, 0 // 6
119
- ], [
120
- 0, 0.5, 0.6, -0.7, -0.2, -1.2, 0.2, 0.3, 0, 0, 0, 0, 0 // 7
121
- ], [
122
- 0, -0.3, 0.7, -0.2, 0.5, -0.3, -0.5, 0.4, 0.1, 0, 0, 0, 0 // 8
123
- ], [
124
- 0, -0.3, 0.2, -0.4, 0.4, 0.1, -0, -0.2, 0.5, 0.2, 0, 0, 0 // 9
125
- ], [
126
- 0, -0, 0.1, -0.3, 0.1, -0.2, 0.1, -0, -0.1, 0.2, -0, 0, 0 // 10
127
- ], [
128
- 0, -0, 0.1, 0, 0.2, -0, 0, 0.1, -0, -0.1, 0, -0, 0 // 11
129
- ], [
130
- 0, -0, 0, -0.1, 0.1, -0, 0, -0, 0.1, -0, -0, 0, -0.1 // 12
131
- ]
132
- ];
133
-
134
- const julianDaysCOF = 2458850;
135
-
136
- const testData = [
137
- /*
138
- # Field 1: Date
139
- # Field 2: Height above ellipsoid (km)
140
- # Field 3: geodetic latitude (deg)
141
- # Field 4: geodetic longitude (deg)
142
- # Field 5: declination (deg)
143
- # Field 6: inclination (deg)
144
- # Field 7: H (nT)
145
- # Field 8: X (nT)
146
- # Field 9: Y (nT)
147
- # Field 10: Z (nT)
148
- # Field 11: F (nT)
149
- # Field 12: dD/dt (deg/year)
150
- # Field 13: dI/dt (deg/year)
151
- # Field 14: dH/dt (nT/year)
152
- # Field 15: dX/dt (nT/year)
153
- # Field 16: dY/dt (nT/year)
154
- # Field 17: dZ/dt (nT/year)
155
- # Field 18: dF/dt (nT/year)
156
- */
157
- [2020.0,28,89,-121,-112.41,88.46,1510.0,-575.7,-1396.0,56082.3,56102.7,2.6,0.0,-18.0,69.5,-9.2,20.1,19.6],
158
- [2020.0,48,80,-96,-37.40,88.03,1910.8,1518.0,-1160.5,55671.9,55704.7,1.9,-0.0,41.6,72.5,26.3,-11.3,-9.8],
159
- [2020.0,54,82,87,51.30,87.48,2487.8,1555.6,1941.4,56520.5,56575.2,0.6,0.0,-46.4,-48.7,-20.4,39.7,37.7],
160
- [2020.0,65,43,93,0.71,63.87,24377.2,24375.3,303.2,49691.4,55348.7,-0.1,0.1,-34.1,-33.7,-32.4,101.1,75.7],
161
- [2020.0,51,-33,109,-5.78,-67.64,21666.6,21556.3,-2183.2,-52676.0,56957.9,0.0,0.0,33.3,34.4,9.2,-16.8,28.2],
162
- [2020.0,39,-59,-8,-15.79,-58.82,14933.4,14369.9,-4063.3,-24679.0,28845.4,0.0,0.0,-13.8,-12.3,7.3,60.0,-58.5],
163
- [2020.0,3,-50,-103,28.10,-55.01,22315.5,19684.4,10512.2,-31883.6,38917.2,-0.0,0.0,-45.1,-34.2,-31.8,85.1,-95.6],
164
- [2020.0,94,-29,-110,15.82,-38.38,24392.0,23467.8,6650.9,-19320.7,31116.9,-0.0,0.0,-44.7,-37.9,-30.4,42.5,-61.4],
165
- [2020.0,66,14,143,0.12,13.08,34916.9,34916.8,70.2,8114.9,35847.5,-0.1,-0.1,29.6,29.6,-41.4,-46.4,18.3],
166
- [2020.0,18,0,21,1.05,-26.46,29316.1,29311.2,536.0,-14589.0,32745.6,0.1,0.1,0.0,-0.9,51.2,54.5,-24.3],
167
- [2020.5,6,-36,-137,20.16,-52.21,25511.4,23948.6,8791.9,-32897.6,41630.3,0.0,0.0,-21.6,-21.6,-3.8,65.4,-64.9],
168
- [2020.5,63,26,81,0.43,40.84,34738.7,34737.7,259.2,30023.4,45914.9,0.0,0.1,5.9,5.9,2.4,128.2,88.3],
169
- [2020.5,69,38,-144,13.39,56.99,23279.9,22647.3,5390.0,35831.9,42730.3,-0.1,0.0,-46.5,-38.9,-37.1,-53.5,-70.1],
170
- [2020.5,50,-70,-133,57.40,-72.18,16597.2,8943.1,13981.7,-51628.5,54230.7,-0.0,0.0,13.3,13.3,7.2,103.1,-94.0],
171
- [2020.5,8,-52,-75,15.39,-49.50,20299.7,19571.7,5387.5,-23769.0,31257.7,-0.1,-0.0,-66.6,-55.8,-48.3,46.4,-78.6],
172
- [2020.5,8,-66,17,-32.56,-59.78,18089.7,15247.0,-9734.8,-31062.0,35945.6,-0.1,0.0,9.6,-12.5,-37.5,42.6,-32.0],
173
- [2020.5,22,-37,140,9.15,-68.61,21705.2,21429.0,3451.3,-55415.6,59514.7,0.0,-0.0,-9.6,-11.1,8.5,-12.6,8.2],
174
- [2020.5,40,-12,-129,10.83,-15.68,29295.6,28773.9,5503.9,-8221.8,30427.4,-0.0,0.1,-24.5,-20.0,-26.0,53.9,-38.2],
175
- [2020.5,44,33,-118,11.46,57.97,23890.9,23414.3,4748.3,38184.5,45042.6,-0.1,0.0,-51.1,-42.7,-46.2,-77.1,-92.4],
176
- [2020.5,50,-81,-67,28.65,-67.74,18332.1,16087.9,8788.9,-44780.8,48387.8,-0.1,0.0,-4.9,8.6,-25.9,81.0,-76.8],
177
- [2021.0,74,-57,3,-22.29,-59.07,14296.6,13228.0,-5423.3,-23859.2,27814.7,-0.0,0.1,1.6,-1.7,-8.2,59.2,-50.0],
178
- [2021.0,46,-24,-122,14.02,-34.29,26836.5,26037.0,6501.8,-18297.4,32480.6,-0.0,0.0,-32.2,-26.5,-26.8,46.8,-53.0],
179
- [2021.0,69,23,63,1.08,35.82,34456.5,34450.4,646.9,24869.2,42493.9,0.0,0.1,15.7,15.2,27.3,95.8,68.7],
180
- [2021.0,33,-3,-147,9.74,-2.35,31138.6,30690.1,5265.7,-1277.4,31164.8,0.0,0.1,-25.6,-25.6,-2.3,62.6,-28.1],
181
- [2021.0,47,-72,-22,-6.05,-61.27,18455.7,18352.8,-1946.6,-33665.0,38392.0,-0.0,0.0,-18.1,-19.5,-12.8,65.5,-66.1],
182
- [2021.0,62,-14,99,-1.71,-45.11,33227.7,33213.0,-990.3,-33354.0,47080.5,0.0,0.1,59.3,59.6,11.8,38.5,14.5],
183
- [2021.0,83,86,-46,-36.71,86.83,3004.8,2408.8,-1796.2,54184.7,54268.0,1.3,0.0,-15.6,26.9,62.1,21.4,20.5],
184
- [2021.0,82,-64,87,-80.81,-75.25,14087.8,2249.5,-13907.0,-53526.9,55349.8,-0.2,-0.0,-16.4,-48.1,8.9,-31.9,26.7],
185
- [2021.0,34,-19,43,-14.32,-52.47,19947.3,19327.6,-4933.5,-25969.5,32746.2,-0.1,0.1,70.3,59.8,-49.8,-15.5,55.1],
186
- [2021.0,56,-81,40,-59.03,-68.54,17872.0,9198.0,-15323.4,-45453.8,48841.2,-0.2,0.0,7.8,-37.3,-31.4,45.4,-39.4],
187
- [2021.5,14,0,80,-3.41,-17.32,39316.2,39246.6,-2338.9,-12258.0,41182.8,0.0,0.1,77.8,79.0,18.2,61.1,56.1],
188
- [2021.5,12,-82,-68,30.36,-68.18,18536.8,15995.1,9368.5,-46308.7,49880.9,-0.1,0.0,-2.2,12.7,-26.1,82.4,-77.3],
189
- [2021.5,44,-46,-42,-11.54,-53.82,14450.9,14159.0,-2889.8,-19762.2,24482.1,0.0,-0.1,-75.2,-73.4,16.4,-5.7,-39.8],
190
- [2021.5,43,17,52,1.23,23.87,35904.4,35896.1,773.7,15885.6,39261.7,0.0,0.1,23.4,23.0,17.8,74.5,51.5],
191
- [2021.5,64,10,78,-1.71,7.37,39311.5,39294.0,-1172.2,5088.1,39639.4,0.0,0.1,52.8,53.1,11.5,102.0,65.4],
192
- [2021.5,12,33,-145,12.36,52.51,24878.3,24301.2,5327.4,32429.3,40872.8,-0.0,0.0,-54.8,-48.9,-32.7,-38.8,-64.2],
193
- [2021.5,12,-79,115,-136.34,-77.43,12997.3,-9403.6,-8972.3,-58271.0,59702.9,-0.2,0.0,16.0,-49.4,28.7,29.1,-24.9],
194
- [2021.5,14,-33,-114,18.10,-44.23,24820.9,23592.3,7712.3,-24163.1,34640.0,-0.0,0.0,-43.5,-37.1,-26.7,54.1,-68.9],
195
- [2021.5,19,29,66,2.13,45.97,32640.6,32618.0,1215.2,33763.7,46961.7,0.0,0.1,3.9,2.9,27.5,105.1,78.3],
196
- [2021.5,86,-11,167,10.11,-31.45,33191.7,32676.0,5828.1,-20299.1,38906.8,0.1,-0.1,-18.5,-23.4,26.1,-43.0,6.7],
197
- [2022.0,37,-66,-5,-16.99,-59.27,17152.6,16404.3,-5011.2,-28849.5,33563.5,-0.0,0.0,-8.3,-11.9,-10.3,59.1,-55.1],
198
- [2022.0,67,72,-115,15.47,85.19,4703.2,4532.7,1254.7,55923.4,56120.8,-0.2,-0.1,68.0,70.4,0.4,-56.5,-50.7],
199
- [2022.0,44,22,174,6.56,31.91,28859.7,28671.0,3294.9,17967.2,33995.6,0.0,-0.0,12.7,11.8,9.1,-15.7,2.5],
200
- [2022.0,54,54,178,1.43,65.41,20631.2,20624.8,514.8,45076.8,49573.8,-0.2,0.0,-0.6,0.9,-59.8,9.2,8.1],
201
- [2022.0,57,-43,50,-47.43,-62.96,16769.3,11344.6,-12349.5,-32850.3,36883.0,-0.2,-0.0,21.7,-18.5,-46.4,-74.7,76.4],
202
- [2022.0,44,-43,-111,24.32,-52.71,22656.4,20646.1,9330.1,-29747.5,37392.8,-0.0,0.0,-42.5,-35.5,-24.5,72.8,-83.6],
203
- [2022.0,12,-63,178,57.08,-79.33,11577.2,6292.0,9718.1,-61429.1,62510.5,0.2,0.0,31.2,-12.8,45.4,58.2,-51.4],
204
- [2022.0,38,27,-169,8.76,42.60,26202.3,25896.5,3991.3,24097.4,35598.4,-0.0,0.0,-24.3,-23.5,-7.3,-4.6,-21.0],
205
- [2022.0,61,59,-77,-17.63,79.04,10595.8,10098.3,-3208.9,54735.3,55751.4,0.3,-0.1,56.5,67.9,27.2,-56.4,-44.6],
206
- [2022.0,67,-47,-32,-14.09,-57.63,13056.5,12663.7,-3178.2,-20600.9,24389.9,0.1,-0.1,-66.7,-60.3,34.0,13.5,-47.1],
207
- [2022.5,8,62,53,18.95,76.54,13043.3,12336.1,4236.6,54498.1,56037.2,0.1,0.0,-29.9,-37.9,18.3,80.0,70.8],
208
- [2022.5,77,-68,-7,-15.94,-60.00,17268.3,16604.7,-4741.2,-29908.6,34535.8,-0.0,0.0,-9.7,-13.4,-11.7,58.5,-55.5],
209
- [2022.5,98,-5,159,7.79,-23.04,33927.0,33613.6,4601.1,-14426.9,36867.1,0.0,-0.1,-12.4,-13.7,9.3,-63.6,13.5],
210
- [2022.5,34,-29,-107,15.68,-37.65,24657.0,23739.9,6662.3,-19023.5,31142.6,-0.0,0.0,-48.7,-41.4,-32.5,42.0,-64.2],
211
- [2022.5,60,27,65,1.78,42.84,32954.8,32938.8,1025.8,30561.3,44944.6,0.0,0.1,7.7,6.9,27.2,101.1,74.4],
212
- [2022.5,73,-72,95,-101.49,-76.44,13362.8,-2661.0,-13095.2,-55423.4,57011.5,-0.2,-0.0,-1.6,-54.1,12.6,-1.5,1.1],
213
- [2022.5,96,-46,-85,18.38,-47.38,20165.1,19136.4,6358.2,-21915.4,29781.1,-0.1,-0.0,-60.2,-47.6,-47.5,49.9,-77.5],
214
- [2022.5,0,-13,-59,-16.65,-13.41,22751.7,21797.3,-6520.6,-5425.9,23389.8,-0.2,-0.4,-66.3,-84.4,-50.8,-156.1,-28.3],
215
- [2022.5,16,66,-178,1.92,75.67,13812.2,13804.5,463.2,54055.4,55792.1,-0.3,-0.0,0.3,3.0,-80.1,-3.5,-3.3],
216
- [2022.5,72,-87,38,-64.66,-71.05,16666.4,7132.3,-15063.2,-48550.5,51331.5,-0.1,0.0,10.9,-33.8,-28.0,58.0,-51.3],
217
- [2023.0,49,20,167,5.20,26.85,30223.6,30099.4,2737.4,15301.2,33876.1,0.0,-0.1,23.9,23.3,7.2,-28.7,8.3],
218
- [2023.0,71,5,-13,-7.26,-17.38,28445.5,28217.7,-3592.6,-8905.8,29807.1,0.2,-0.1,-2.1,9.3,89.7,-65.6,17.6],
219
- [2023.0,95,14,65,-0.56,17.52,36805.8,36804.1,-356.8,11616.0,38595.3,0.0,0.1,37.7,37.9,22.9,90.4,63.1],
220
- [2023.0,86,-85,-79,41.76,-70.36,16888.7,12598.0,11248.0,-47331.2,50254.0,-0.1,0.0,6.0,25.6,-19.7,75.2,-68.9],
221
- [2023.0,30,-36,-64,-3.87,-39.40,17735.3,17694.8,-1198.1,-14566.5,22950.5,-0.2,-0.2,-76.8,-80.2,-47.9,-28.3,-41.4],
222
- [2023.0,75,79,125,-14.54,87.30,2692.1,2605.8,-676.0,57085.9,57149.3,-1.0,0.0,-15.7,-27.0,-41.5,31.6,30.8],
223
- [2023.0,21,6,-32,-15.22,-7.26,28634.3,27630.5,-7515.0,-3646.9,28865.6,0.2,-0.3,-10.8,11.3,82.5,-174.9,11.4],
224
- [2023.0,1,-76,-75,30.36,-65.32,19700.9,16998.9,9958.1,-42873.8,47183.6,-0.1,0.0,-17.2,-3.2,-28.5,88.0,-87.1],
225
- [2023.0,45,-46,-41,-11.94,-54.45,14187.4,13880.3,-2936.1,-19853.0,24401.3,0.0,-0.1,-74.8,-72.5,18.8,-4.5,-39.9],
226
- [2023.0,11,-22,-21,-24.12,-56.82,13972.9,12752.8,-5710.4,-21372.4,25534.7,0.1,-0.3,-99.1,-77.0,70.4,-57.8,-5.8],
227
- [2023.5,28,54,-120,16.20,74.02,15158.8,14556.6,4230.3,52945.6,55072.9,-0.1,-0.0,11.9,19.3,-24.0,-105.1,-97.8],
228
- [2023.5,68,-58,156,40.48,-81.60,9223.1,7015.1,5987.8,-62459.5,63136.8,0.3,0.0,9.5,-20.2,38.3,21.0,-19.4],
229
- [2023.5,39,-65,-88,29.86,-60.29,20783.0,18024.0,10347.2,-36415.6,41928.8,-0.1,0.0,-35.9,-20.5,-36.4,88.4,-94.6],
230
- [2023.5,27,-23,81,-13.98,-58.52,25568.2,24811.0,-6176.1,-41759.2,48965.0,0.1,0.0,44.0,55.4,40.2,-51.8,67.2],
231
- [2023.5,11,34,0,1.08,46.69,29038.8,29033.7,545.3,30807.0,42335.9,0.1,-0.0,26.2,24.9,70.4,26.6,37.4],
232
- [2023.5,72,-62,65,-66.98,-68.38,17485.0,6836.8,-16092.9,-44111.8,47450.8,-0.2,-0.0,-2.0,-50.3,-19.2,-35.3,32.1],
233
- [2023.5,55,86,70,61.19,87.51,2424.9,1168.7,2124.7,55750.6,55803.4,1.2,0.0,-32.0,-59.7,-3.6,34.7,33.3],
234
- [2023.5,59,32,163,0.36,43.05,28170.6,28170.0,176.0,26318.3,38551.7,-0.0,-0.0,25.9,26.0,-13.8,-0.7,18.4],
235
- [2023.5,65,48,148,-9.39,61.70,23673.8,23356.8,-3861.2,43968.0,49936.3,-0.1,0.0,14.1,10.3,-23.9,31.6,34.6],
236
- [2023.5,95,30,28,4.49,44.12,29754.7,29663.3,2331.2,28857.6,41450.1,0.1,0.1,12.0,8.7,42.8,66.2,54.8],
237
- [2024.0,95,-60,-59,8.86,-55.03,18317.9,18099.3,2821.2,-26193.2,31962.9,-0.1,-0.0,-57.4,-54.2,-25.1,43.5,-68.5],
238
- [2024.0,95,-70,42,-54.29,-64.59,18188.3,10615.3,-14769.3,-38293.4,42393.4,-0.2,0.0,9.1,-37.8,-38.4,15.3,-9.9],
239
- [2024.0,50,87,-154,-82.23,89.39,597.9,80.9,-592.4,55904.6,55907.8,4.5,-0.1,54.0,53.7,-47.2,12.8,13.3],
240
- [2024.0,58,32,19,3.94,45.89,29401.0,29331.6,2019.5,30329.8,42241.2,0.1,0.0,17.0,13.4,52.8,54.5,50.9],
241
- [2024.0,57,34,-13,-2.62,45.83,28188.3,28158.7,-1290.8,29015.5,40453.4,0.2,-0.0,33.7,37.4,79.9,-8.7,17.2],
242
- [2024.0,38,-76,49,-63.51,-67.40,18425.8,8218.0,-16491.7,-44260.7,47942.9,-0.2,0.0,7.9,-45.6,-31.5,27.6,-22.5],
243
- [2024.0,49,-50,-179,31.57,-71.40,18112.2,15431.4,9482.7,-53818.3,56784.4,0.1,0.0,-0.6,-22.2,34.9,44.3,-42.2],
244
- [2024.0,90,-55,-171,38.07,-72.91,16409.7,12918.7,10118.6,-53373.5,55839.2,0.1,0.0,7.8,-17.0,34.3,61.5,-56.5],
245
- [2024.0,41,42,-19,-5.00,56.57,24410.2,24317.3,-2127.0,36981.3,44311.1,0.2,-0.0,34.5,41.4,76.7,-9.2,11.4],
246
- [2024.0,19,46,-22,-6.60,61.04,22534.0,22384.7,-2590.4,40713.3,46533.4,0.2,-0.0,34.0,42.9,75.0,-8.5,9.0],
247
- [2024.5,31,13,-132,9.21,31.51,28413.4,28046.9,4548.8,17417.2,33326.9,-0.0,0.1,-62.9,-58.7,-30.8,27.1,-39.5],
248
- [2024.5,93,-2,158,7.16,-17.78,34124.3,33858.1,4253.5,-10940.3,35835.1,0.0,-0.1,-6.5,-7.4,6.3,-67.8,14.5],
249
- [2024.5,51,-76,40,-55.63,-66.27,18529.2,10459.6,-15294.7,-42141.5,46035.2,-0.2,0.0,7.6,-38.3,-35.4,33.4,-27.5],
250
- [2024.5,64,22,-132,10.52,43.88,26250.1,25808.9,4792.5,25239.1,36415.3,-0.0,0.1,-67.8,-62.6,-34.5,-9.5,-55.5],
251
- [2024.5,26,-65,55,-62.60,-65.67,18702.1,8607.6,-16603.5,-41366.0,45397.3,-0.2,0.0,7.3,-49.7,-34.0,-15.7,17.3],
252
- [2024.5,66,-21,32,-13.34,-56.95,15940.8,15510.7,-3677.9,-24502.7,29231.7,-0.1,0.1,45.8,37.2,-41.5,32.1,-1.9],
253
- [2024.5,18,9,-172,9.39,15.78,31031.6,30615.4,5065.5,8768.5,32246.7,0.1,0.0,-12.5,-17.9,31.4,12.7,-8.6],
254
- [2024.5,63,88,26,29.81,87.38,2523.6,2189.6,1254.6,55156.1,55213.8,1.4,0.0,-21.0,-48.3,42.1,29.2,28.2],
255
- [2024.5,33,17,5,0.61,13.58,34062.9,34060.9,362.9,8230.6,35043.1,0.1,0.0,19.6,18.9,65.9,9.6,21.3],
256
- [2024.5,77,-18,138,4.63,-47.71,31825.9,31722.0,2569.6,-34986.2,47296.1,-0.0,-0.0,-11.3,-9.0,-27.7,-26.8,12.2]
257
- ];
258
-
259
- module.exports = {
260
- gnmWmm,
261
- hnmWmm,
262
- gtnmWmm,
263
- htnmWmm,
264
- julianDaysCOF,
265
- testData
266
- };
@@ -1,30 +0,0 @@
1
- const {gregorianToJulian} = require('../src/utils');
2
- const {magvar, calculateMagVar} = require('../src/magvar');
3
- const {testData} = require('../src/WMMCOF2025');
4
-
5
- describe('MagVar tests', () => {
6
- // you can use the online calculator at
7
- // http://www.geomag.bgs.ac.uk/data_service/models_compass/wmm_calc.html
8
-
9
- test('can calculate Magvar using the World Magnetic Model 2025-2030 test data', () => {
10
- testData.forEach((test) => {
11
- const year = Math.trunc(test[0]);
12
- const month = 12 * (test[0] - year);
13
- const julian_days = gregorianToJulian(year, month, 1);
14
- const height = test[1];
15
- const lat = test[2];
16
- const lon = test[3];
17
- const result = calculateMagVar(julian_days, lat, lon, height);
18
- expect(result).toBeCloseTo(test[4], 1);
19
- });
20
- });
21
-
22
- test('can calculate Magvar for the current date', () => {
23
- const lat = 51.2;
24
- const lon = -0.3;
25
- const d = new Date();
26
- const julianDaysNow = gregorianToJulian(d.getFullYear(), d.getMonth(), d.getDate());
27
- const magVarNow = calculateMagVar(julianDaysNow, lat, lon, 0);
28
- expect(magvar(lat, lon)).toBeCloseTo(magVarNow, 1);
29
- });
30
- });