signalk-racer 1.1.1 → 1.1.2

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,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test:*)",
5
+ "Bash(git -C /home/gregw/src/signalk-racer tag -l)",
6
+ "Bash(git -C /home/gregw/src/signalk-racer log --oneline -5)",
7
+ "Bash(git -C /home/gregw/src/signalk-racer status -sb)",
8
+ "Bash(git -C /home/gregw/src/signalk-racer remote -v)"
9
+ ]
10
+ }
11
+ }
package/README.md CHANGED
@@ -189,4 +189,41 @@ npm install
189
189
  npm link
190
190
  cd ~/.signalk
191
191
  npm link signalk-racer
192
- ```
192
+ ```
193
+
194
+ ---
195
+ ## 🚀 Releasing
196
+
197
+ Releases are cut manually from a clean `main` checkout and published to npm.
198
+
199
+ ### Pre-flight
200
+
201
+ 1. Confirm `main` is up to date and clean: `git checkout main && git pull && git status`.
202
+ 2. Run the test suite: `npm test`.
203
+ 3. Review commits since the last release: `git log $(git describe --tags --abbrev=0)..HEAD --oneline`.
204
+ 4. Decide the version bump (semver): `patch` for bug fixes, `minor` for features, `major` for breaking changes.
205
+
206
+ ### Cut the release
207
+
208
+ 1. Update `ReleaseNotes.md` — add a new entry at the top with the new version and a short bullet list of changes, then `git add ReleaseNotes.md`.
209
+ 2. Bump the version and create the git tag in one step (note the non-default tag prefix so it matches the existing `signalk-racer-X.Y.Z` convention):
210
+ ```
211
+ npm version <patch|minor|major> --tag-version-prefix=signalk-racer-
212
+ ```
213
+ This rewrites `package.json`, includes any staged files (e.g. `ReleaseNotes.md`) in the bump commit, and creates tag `signalk-racer-X.Y.Z`.
214
+ 3. Push the commit and the tag:
215
+ ```
216
+ git push origin main
217
+ git push origin signalk-racer-X.Y.Z
218
+ ```
219
+
220
+ ### Publish to npm
221
+
222
+ 1. Confirm npm auth: `npm whoami` (login with `npm login` if needed).
223
+ 2. Dry-run the publish to inspect the tarball contents: `npm publish --dry-run`.
224
+ 3. Publish: `npm publish` (the package is public and unscoped; no `--access` flag needed).
225
+
226
+ ### After release
227
+
228
+ 1. Verify on npm: `npm view signalk-racer version` should report the new version.
229
+ 2. (Optional) Create a GitHub release from the new tag with notes copied from `ReleaseNotes.md`.
package/ReleaseNotes.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 1.1.2 TTL fixes
2
+ + use a FIFO queue for VMGs towards the line.
3
+ + avoid undefined values in deltas
4
+
1
5
  ## 1.1.1 Unit tests
2
6
  + split into index.js and racing.js for unit testing
3
7
  + unit tested TTL
package/index.js CHANGED
@@ -196,19 +196,22 @@ module.exports = (app) => {
196
196
  });
197
197
 
198
198
  // send multiple deltas, each in the form of { path, value }
199
+ // null values are preserved (meaningful in SignalK for clearing a value);
200
+ // undefined values are dropped to avoid "Delta is missing value" warnings.
199
201
  function sendDeltas(updatesArray) {
200
- const timestamp = new Date().toISOString();
202
+ const values = updatesArray
203
+ .filter(({value}) => value !== undefined)
204
+ .map(({path, value}) => ({path, value}));
205
+
206
+ if (values.length === 0) return;
201
207
 
202
208
  const delta = {
203
209
  context: 'vessels.self',
204
210
  updates: [
205
211
  {
206
212
  source: {label: 'signalk-racer'},
207
- timestamp: timestamp,
208
- values: updatesArray.map(({path, value}) => ({
209
- path,
210
- value
211
- }))
213
+ timestamp: new Date().toISOString(),
214
+ values
212
215
  }
213
216
  ]
214
217
  };
@@ -812,8 +815,8 @@ module.exports = (app) => {
812
815
  if (cog != null && sog != null) {
813
816
  collectVmgSamples(cog, sog, startLine.bearing, toZoneVz, perpToLineVx);
814
817
  }
815
- const ttl = computeTimeToLine(cog, sog, startLine.bearing, toZoneVz, perpToLineVx, ocs, closestEnd);
816
- const ttb = !ocs && state.timerRunning && state.timeToStart > 0 ? (state.timeToStart - ttl) : null;
818
+ const ttl = computeTimeToLine(cog, sog, startLine.bearing, toZoneVz, perpToLineVx, ocs, closestEnd, state.timeToStart ?? 0);
819
+ const ttb = !ocs && state.timerRunning && state.timeToStart > 0 && ttl != null ? (state.timeToStart - ttl) : null;
817
820
 
818
821
  sendDeltas([
819
822
  {path: 'navigation.racing.distanceStartline', value: distanceToLine},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-racer",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Signalk plugin to calculate values of interest to sail racers, such as: Time to Start; Time of Start, Time to Burn; Distance to Line; Next leg TWA.",
5
5
  "main": "index.js",
6
6
  "repository": {
package/racer.js CHANGED
@@ -8,27 +8,31 @@ let cfg = {
8
8
  percentile: 0.9
9
9
  };
10
10
 
11
- let log = { debug: () => {} };
11
+ let log = {
12
+ debug: () => {
13
+ }
14
+ };
12
15
 
13
16
  function initRacer(config = {}, logger = null) {
14
- cfg = { ...cfg, ...config };
17
+ cfg = {...cfg, ...config};
15
18
  if (logger?.debug) log = logger;
16
19
  }
17
20
 
18
21
  // VMG sample state is kept private to this module.
19
- // These arrays are kept sorted ascending.
22
+ // Each entry has a `sorted` array (ascending, for O(1) percentile) and a
23
+ // `queue` array (insertion-order FIFO, for correct oldest-first eviction).
20
24
  const vmgState = {
21
- vmgToLinePos: [], // VMG towards the line (not OCS to line)
22
- vmgToLineNeg: [], // VMG away from the line (OCS to line)
23
- vmgToZonePort: [], // Motion along the line towards the PORT end (pin)
24
- vmgToZoneStb: [], // Motion along the line towards the STARBOARD end (boat)
25
+ vmgToLinePos: {sorted: [], queue: []}, // VMG towards the line (not OCS to line)
26
+ vmgToLineNeg: {sorted: [], queue: []}, // VMG away from the line (OCS to line)
27
+ vmgToZonePort: {sorted: [], queue: []}, // Motion along the line towards the PORT end (pin)
28
+ vmgToZoneStb: {sorted: [], queue: []}, // Motion along the line towards the STARBOARD end (boat)
25
29
  };
26
30
 
27
31
  function resetVmgSamples() {
28
- vmgState.vmgToLinePos.length = 0;
29
- vmgState.vmgToLineNeg.length = 0;
30
- vmgState.vmgToZonePort.length = 0;
31
- vmgState.vmgToZoneStb.length = 0;
32
+ for (const vmg of Object.values(vmgState)) {
33
+ vmg.sorted.length = 0;
34
+ vmg.queue.length = 0;
35
+ }
32
36
  }
33
37
 
34
38
  function toDegrees(rad) {
@@ -41,10 +45,10 @@ function toRadians(deg) {
41
45
  return deg * (Math.PI / 180);
42
46
  }
43
47
 
44
- function percentile(sortedArray, p) {
45
- if (!sortedArray || !sortedArray.length) return 0;
46
- const idx = Math.floor(p * (sortedArray.length - 1));
47
- return sortedArray[idx];
48
+ function percentile(vmg, p) {
49
+ if (!vmg.sorted.length) return 0;
50
+ const idx = Math.floor(p * (vmg.sorted.length - 1));
51
+ return vmg.sorted[idx];
48
52
  }
49
53
 
50
54
  function collectVmgSamples(cog, sog, lineBearing, toZoneVz, perpToLineVx) {
@@ -65,20 +69,36 @@ function collectVmgSamples(cog, sog, lineBearing, toZoneVz, perpToLineVx) {
65
69
  insertSample(vmgState.vmgToZoneStb, -vmgTangent);
66
70
  }
67
71
 
68
- function insertSample(arr, value) {
69
- if (value <= 1.0) return; // reject low VMGs
70
- if (arr.length >= cfg.maxSamples) arr.shift(); // Keep up to 10 minutes at 1Hz
71
-
72
- let lo = 0, hi = arr.length;
72
+ function insertSample(vmg, value) {
73
+ if (value <= 1.0)
74
+ return; // reject low VMGs
75
+ if (vmg.queue.length >= cfg.maxSamples) {
76
+ const oldest = vmg.queue.shift(); // evict oldest from FIFO
77
+ // remove oldest from sorted (binary search)
78
+ let lo = 0, hi = vmg.sorted.length;
79
+ while (lo < hi) {
80
+ const mid = (lo + hi) >> 1;
81
+ if (vmg.sorted[mid] < oldest)
82
+ lo = mid + 1;
83
+ else
84
+ hi = mid;
85
+ }
86
+ vmg.sorted.splice(lo, 1);
87
+ }
88
+ // insert value into sorted (binary search)
89
+ let lo = 0, hi = vmg.sorted.length;
73
90
  while (lo < hi) {
74
91
  const mid = (lo + hi) >> 1;
75
- if (arr[mid] < value) lo = mid + 1;
76
- else hi = mid;
92
+ if (vmg.sorted[mid] < value)
93
+ lo = mid + 1;
94
+ else
95
+ hi = mid;
77
96
  }
78
- arr.splice(lo, 0, value);
97
+ vmg.sorted.splice(lo, 0, value);
98
+ vmg.queue.push(value);
79
99
  }
80
100
 
81
- function computeTimeToLine(cog, sog, lineBearing, toZoneVz, perpToLineVx, ocs, closestEnd) {
101
+ function computeTimeToLine(cog, sog, lineBearing, toZoneVz, perpToLineVx, ocs, closestEnd, timeToStart = 0) {
82
102
  let vmgNormalSigned = 0;
83
103
  let vmgTangentSigned = 0;
84
104
 
@@ -146,7 +166,7 @@ function computeTimeToLine(cog, sog, lineBearing, toZoneVz, perpToLineVx, ocs, c
146
166
  }
147
167
 
148
168
  // If we still somehow have zero (no speed or distances), just return time to start so TTB logic doesn't explode.
149
- return ttl <= 0 ? vmgState.timeToStart : ttl;
169
+ return ttl <= 0 ? timeToStart : ttl;
150
170
  }
151
171
 
152
172
  module.exports = {
@@ -24,12 +24,13 @@ describe('racer math', () => {
24
24
  expect(toRadians(undefined)).toBeNull();
25
25
  });
26
26
  test('percentile: test maths', () => {
27
- expect(_percentile([], 0.5)).toBe(0);
28
- expect(_percentile([10], 0.5)).toBe(10);
29
- expect(_percentile([10, 20, 30], 0)).toBe(10);
30
- expect(_percentile([10, 20, 30], 0.5)).toBe(20);
31
- expect(_percentile([10, 20, 30], 0.9)).toBe(20);
32
- expect(_percentile([10, 20, 30], 1)).toBe(30);
33
- expect(_percentile([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0.75)).toBe(7);
27
+ const vmg = (arr) => ({ sorted: arr, queue: [] });
28
+ expect(_percentile(vmg([]), 0.5)).toBe(0);
29
+ expect(_percentile(vmg([10]), 0.5)).toBe(10);
30
+ expect(_percentile(vmg([10, 20, 30]), 0)).toBe(10);
31
+ expect(_percentile(vmg([10, 20, 30]), 0.5)).toBe(20);
32
+ expect(_percentile(vmg([10, 20, 30]), 0.9)).toBe(20);
33
+ expect(_percentile(vmg([10, 20, 30]), 1)).toBe(30);
34
+ expect(_percentile(vmg([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 0.75)).toBe(7);
34
35
  })
35
36
  });
@@ -14,15 +14,18 @@ describe('racer ttl', () => {
14
14
  test('timeToLineNoVMG: no VMG', () => {
15
15
  resetVmgSamples();
16
16
  expect(computeTimeToLine(toRadians(0), 10, 270, 0, 100, false, 'stb')).toBeCloseTo(10);
17
- expect(computeTimeToLine(toRadians(0), 10, 270, 100, 0, false, 'stb')).toBeUndefined();
17
+ // Fallback: ttl computed as 0, so returns the supplied timeToStart.
18
+ expect(computeTimeToLine(toRadians(0), 10, 270, 100, 0, false, 'stb', 300)).toBe(300);
19
+ // Default timeToStart parameter is 0.
20
+ expect(computeTimeToLine(toRadians(0), 10, 270, 100, 0, false, 'stb')).toBe(0);
18
21
  });
19
22
 
20
23
  test('timeToLineSmallVmg: small VMG', () => {
21
24
  resetVmgSamples();
22
- _vmgState.vmgToLinePos.push(1, 1, 1);
23
- _vmgState.vmgToLineNeg.push(2, 2, 2);
24
- _vmgState.vmgToZonePort.push(1, 1, 1);
25
- _vmgState.vmgToZoneStb.push(2, 2, 2);
25
+ _vmgState.vmgToLinePos.sorted.push(1, 1, 1);
26
+ _vmgState.vmgToLineNeg.sorted.push(2, 2, 2);
27
+ _vmgState.vmgToZonePort.sorted.push(1, 1, 1);
28
+ _vmgState.vmgToZoneStb.sorted.push(2, 2, 2);
26
29
 
27
30
  expect(computeTimeToLine(toRadians(-1), 10, 270, 0, 100, false, 'stb')).toBeCloseTo(10);
28
31
  expect(computeTimeToLine(toRadians(0), 10, 270, 100, 0, false, 'stb')).toBeCloseTo(100);
@@ -59,10 +62,10 @@ describe('racer ttl', () => {
59
62
 
60
63
  test('timeToLineSmallInstant: small instant', () => {
61
64
  resetVmgSamples();
62
- _vmgState.vmgToLinePos.push(10, 10, 10, 10, 10, 10, 10, 10, 10, 15);
63
- _vmgState.vmgToLineNeg.push(20, 20, 20, 20, 20, 20, 20, 20, 20, 25);
64
- _vmgState.vmgToZonePort.push(10, 10, 10, 10, 10, 10, 10, 10, 10, 15);
65
- _vmgState.vmgToZoneStb.push(20, 20, 20, 20, 20, 20, 20, 20, 20, 25);
65
+ _vmgState.vmgToLinePos.sorted.push(10, 10, 10, 10, 10, 10, 10, 10, 10, 15);
66
+ _vmgState.vmgToLineNeg.sorted.push(20, 20, 20, 20, 20, 20, 20, 20, 20, 25);
67
+ _vmgState.vmgToZonePort.sorted.push(10, 10, 10, 10, 10, 10, 10, 10, 10, 15);
68
+ _vmgState.vmgToZoneStb.sorted.push(20, 20, 20, 20, 20, 20, 20, 20, 20, 25);
66
69
 
67
70
  expect(computeTimeToLine(toRadians(-1), 1, 270, 0, 100, false, 'stb')).toBeCloseTo(10);
68
71
  expect(computeTimeToLine(toRadians(0), 1, 270, 100, 0, false, 'stb')).toBeCloseTo(10);
@@ -11,154 +11,158 @@ const {
11
11
 
12
12
  describe('racer vmg', () => {
13
13
  test('resetVmgSamples: test arrays are cleared', () => {
14
- _vmgState.vmgToLinePos.push(1, 2, 3);
15
- _vmgState.vmgToLineNeg.push(4, 5, 6);
16
- _vmgState.vmgToZonePort.push(7, 8, 9);
17
- _vmgState.vmgToZoneStb.push(10, 11, 12);
18
- expect(_vmgState.vmgToLinePos.length).toBe(3);
19
- expect(_vmgState.vmgToLineNeg.length).toBe(3);
20
- expect(_vmgState.vmgToZonePort.length).toBe(3);
21
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
14
+ _vmgState.vmgToLinePos.sorted.push(1, 2, 3);
15
+ _vmgState.vmgToLinePos.queue.push(1, 2, 3);
16
+ _vmgState.vmgToLineNeg.sorted.push(4, 5, 6);
17
+ _vmgState.vmgToLineNeg.queue.push(4, 5, 6);
18
+ _vmgState.vmgToZonePort.sorted.push(7, 8, 9);
19
+ _vmgState.vmgToZonePort.queue.push(7, 8, 9);
20
+ _vmgState.vmgToZoneStb.sorted.push(10, 11, 12);
21
+ _vmgState.vmgToZoneStb.queue.push(10, 11, 12);
22
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(3);
23
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(3);
24
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(3);
25
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
22
26
  resetVmgSamples();
23
- expect(_vmgState.vmgToLinePos.length).toBe(0);
24
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
25
- expect(_vmgState.vmgToZonePort.length).toBe(0);
26
- expect(_vmgState.vmgToZoneStb.length).toBe(0);
27
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(0);
28
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
29
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
30
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(0);
27
31
  });
28
32
 
29
33
 
30
34
  test('collectVmgSamplesEastWestNarrow: test east west line, with perpendicular and narrow approaches', () => {
31
35
  resetVmgSamples();
32
36
  collectVmgSamples(toRadians(0), 19.7, 270, 0, 0);
33
- expect(_vmgState.vmgToLinePos.length).toBe(1);
34
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(19.7);
35
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
36
- expect(_vmgState.vmgToZonePort.length).toBe(0);
37
- expect(_vmgState.vmgToZoneStb.length).toBe(0);
37
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(1);
38
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(19.7);
39
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
40
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
41
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(0);
38
42
 
39
43
  collectVmgSamples(toRadians(8.21), 19.7, 270, 0, 0);
40
- expect(_vmgState.vmgToLinePos.length).toBe(2);
41
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(19.5);
42
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(19.7);
43
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
44
- expect(_vmgState.vmgToZonePort.length).toBe(0);
45
- expect(_vmgState.vmgToZoneStb.length).toBe(1);
46
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(2.81);
44
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
45
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(19.5);
46
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(19.7);
47
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
48
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
49
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(1);
50
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(2.81);
47
51
  });
48
52
 
49
53
 
50
54
  test('collectVmgSamplesEastWest: test east west line with circle', () => {
51
55
  resetVmgSamples();
52
56
  collectVmgSamples(toRadians(0), 5, 270, 0, 0);
53
- expect(_vmgState.vmgToLinePos.length).toBe(1);
54
- expect(_vmgState.vmgToLinePos[0]).toBe(5);
55
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
56
- expect(_vmgState.vmgToZonePort.length).toBe(0);
57
- expect(_vmgState.vmgToZoneStb.length).toBe(0);
57
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(1);
58
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBe(5);
59
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
60
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
61
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(0);
58
62
 
59
63
  collectVmgSamples(toRadians(36.87), 5, 270, 0, 0);
60
- expect(_vmgState.vmgToLinePos.length).toBe(2);
61
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
62
- expect(_vmgState.vmgToLinePos[1]).toBe(5);
63
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
64
- expect(_vmgState.vmgToZonePort.length).toBe(0);
65
- expect(_vmgState.vmgToZoneStb.length).toBe(1);
66
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
64
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
65
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
66
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBe(5);
67
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
68
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
69
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(1);
70
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
67
71
 
68
72
  collectVmgSamples(toRadians(90), 5, 270, 0, 0);
69
- expect(_vmgState.vmgToLinePos.length).toBe(2);
70
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
71
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(5);
72
- expect(_vmgState.vmgToLineNeg.length).toBe(0);
73
- expect(_vmgState.vmgToZonePort.length).toBe(0);
74
- expect(_vmgState.vmgToZoneStb.length).toBe(2);
75
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
76
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(5);
73
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
74
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
75
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(5);
76
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(0);
77
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
78
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(2);
79
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
80
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(5);
77
81
 
78
82
  collectVmgSamples(toRadians(90 + 36.87), 5, 270, 0, 0);
79
- expect(_vmgState.vmgToLinePos.length).toBe(2);
80
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
81
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(5);
82
- expect(_vmgState.vmgToLineNeg.length).toBe(1);
83
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(3);
84
- expect(_vmgState.vmgToZonePort.length).toBe(0);
85
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
86
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
87
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(4);
88
- expect(_vmgState.vmgToZoneStb[2]).toBeCloseTo(5);
83
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
84
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
85
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(5);
86
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(1);
87
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(3);
88
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
89
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
90
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
91
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(4);
92
+ expect(_vmgState.vmgToZoneStb.sorted[2]).toBeCloseTo(5);
89
93
 
90
94
  collectVmgSamples(toRadians(180), 5, 270, 0, 0);
91
- expect(_vmgState.vmgToLinePos.length).toBe(2);
92
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
93
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(5);
94
- expect(_vmgState.vmgToLineNeg.length).toBe(2);
95
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(3);
96
- expect(_vmgState.vmgToLineNeg[1]).toBeCloseTo(5);
97
- expect(_vmgState.vmgToZonePort.length).toBe(0);
98
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
99
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
100
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(4);
101
- expect(_vmgState.vmgToZoneStb[2]).toBeCloseTo(5);
95
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
96
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
97
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(5);
98
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(2);
99
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(3);
100
+ expect(_vmgState.vmgToLineNeg.sorted[1]).toBeCloseTo(5);
101
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(0);
102
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
103
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
104
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(4);
105
+ expect(_vmgState.vmgToZoneStb.sorted[2]).toBeCloseTo(5);
102
106
 
103
107
  collectVmgSamples(toRadians(180 + 53.13), 5, 270, 0, 0);
104
- expect(_vmgState.vmgToLinePos.length).toBe(2);
105
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
106
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(5);
107
- expect(_vmgState.vmgToLineNeg.length).toBe(3);
108
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(3);
109
- expect(_vmgState.vmgToLineNeg[1]).toBeCloseTo(3);
110
- expect(_vmgState.vmgToLineNeg[2]).toBeCloseTo(5);
111
- expect(_vmgState.vmgToZonePort.length).toBe(1);
112
- expect(_vmgState.vmgToZonePort[0]).toBeCloseTo(4);
113
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
114
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
115
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(4);
116
- expect(_vmgState.vmgToZoneStb[2]).toBeCloseTo(5);
108
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
109
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
110
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(5);
111
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(3);
112
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(3);
113
+ expect(_vmgState.vmgToLineNeg.sorted[1]).toBeCloseTo(3);
114
+ expect(_vmgState.vmgToLineNeg.sorted[2]).toBeCloseTo(5);
115
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(1);
116
+ expect(_vmgState.vmgToZonePort.sorted[0]).toBeCloseTo(4);
117
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
118
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
119
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(4);
120
+ expect(_vmgState.vmgToZoneStb.sorted[2]).toBeCloseTo(5);
117
121
 
118
122
  collectVmgSamples(toRadians(270), 5, 270, 0, 0);
119
- expect(_vmgState.vmgToLinePos.length).toBe(2);
120
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(4);
121
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(5);
122
- expect(_vmgState.vmgToLineNeg.length).toBe(3);
123
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(3);
124
- expect(_vmgState.vmgToLineNeg[1]).toBeCloseTo(3);
125
- expect(_vmgState.vmgToLineNeg[2]).toBeCloseTo(5);
126
- expect(_vmgState.vmgToZonePort.length).toBe(2);
127
- expect(_vmgState.vmgToZonePort[0]).toBeCloseTo(4);
128
- expect(_vmgState.vmgToZonePort[1]).toBeCloseTo(5);
129
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
130
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
131
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(4);
132
- expect(_vmgState.vmgToZoneStb[2]).toBeCloseTo(5);
123
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(2);
124
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(4);
125
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(5);
126
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(3);
127
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(3);
128
+ expect(_vmgState.vmgToLineNeg.sorted[1]).toBeCloseTo(3);
129
+ expect(_vmgState.vmgToLineNeg.sorted[2]).toBeCloseTo(5);
130
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(2);
131
+ expect(_vmgState.vmgToZonePort.sorted[0]).toBeCloseTo(4);
132
+ expect(_vmgState.vmgToZonePort.sorted[1]).toBeCloseTo(5);
133
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
134
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
135
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(4);
136
+ expect(_vmgState.vmgToZoneStb.sorted[2]).toBeCloseTo(5);
133
137
 
134
138
  collectVmgSamples(-toRadians(53.13), 5, 270, 0, 0);
135
- expect(_vmgState.vmgToLinePos.length).toBe(3);
136
- expect(_vmgState.vmgToLinePos[0]).toBeCloseTo(3);
137
- expect(_vmgState.vmgToLinePos[1]).toBeCloseTo(4);
138
- expect(_vmgState.vmgToLinePos[2]).toBeCloseTo(5);
139
- expect(_vmgState.vmgToLineNeg.length).toBe(3);
140
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(3);
141
- expect(_vmgState.vmgToLineNeg[1]).toBeCloseTo(3);
142
- expect(_vmgState.vmgToLineNeg[2]).toBeCloseTo(5);
143
- expect(_vmgState.vmgToZonePort.length).toBe(3);
144
- expect(_vmgState.vmgToZonePort[0]).toBeCloseTo(4);
145
- expect(_vmgState.vmgToZonePort[1]).toBeCloseTo(4);
146
- expect(_vmgState.vmgToZonePort[2]).toBeCloseTo(5);
147
- expect(_vmgState.vmgToZoneStb.length).toBe(3);
148
- expect(_vmgState.vmgToZoneStb[0]).toBeCloseTo(3);
149
- expect(_vmgState.vmgToZoneStb[1]).toBeCloseTo(4);
150
- expect(_vmgState.vmgToZoneStb[2]).toBeCloseTo(5);
139
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(3);
140
+ expect(_vmgState.vmgToLinePos.sorted[0]).toBeCloseTo(3);
141
+ expect(_vmgState.vmgToLinePos.sorted[1]).toBeCloseTo(4);
142
+ expect(_vmgState.vmgToLinePos.sorted[2]).toBeCloseTo(5);
143
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(3);
144
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(3);
145
+ expect(_vmgState.vmgToLineNeg.sorted[1]).toBeCloseTo(3);
146
+ expect(_vmgState.vmgToLineNeg.sorted[2]).toBeCloseTo(5);
147
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(3);
148
+ expect(_vmgState.vmgToZonePort.sorted[0]).toBeCloseTo(4);
149
+ expect(_vmgState.vmgToZonePort.sorted[1]).toBeCloseTo(4);
150
+ expect(_vmgState.vmgToZonePort.sorted[2]).toBeCloseTo(5);
151
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(3);
152
+ expect(_vmgState.vmgToZoneStb.sorted[0]).toBeCloseTo(3);
153
+ expect(_vmgState.vmgToZoneStb.sorted[1]).toBeCloseTo(4);
154
+ expect(_vmgState.vmgToZoneStb.sorted[2]).toBeCloseTo(5);
151
155
 
152
156
  });
153
157
 
154
158
  test('collectVmgSamplesNW2SE: test NW to SE line approached obliquely', () => {
155
159
  resetVmgSamples();
156
160
  collectVmgSamples(toRadians(0), 19.7, 8.21, 0, 0);
157
- expect(_vmgState.vmgToLinePos.length).toBe(0);
158
- expect(_vmgState.vmgToLineNeg.length).toBe(1);
159
- expect(_vmgState.vmgToLineNeg[0]).toBeCloseTo(2.81);
160
- expect(_vmgState.vmgToZonePort.length).toBe(1);
161
- expect(_vmgState.vmgToZonePort[0]).toBeCloseTo(19.5);
162
- expect(_vmgState.vmgToZoneStb.length).toBe(0);
161
+ expect(_vmgState.vmgToLinePos.sorted.length).toBe(0);
162
+ expect(_vmgState.vmgToLineNeg.sorted.length).toBe(1);
163
+ expect(_vmgState.vmgToLineNeg.sorted[0]).toBeCloseTo(2.81);
164
+ expect(_vmgState.vmgToZonePort.sorted.length).toBe(1);
165
+ expect(_vmgState.vmgToZonePort.sorted[0]).toBeCloseTo(19.5);
166
+ expect(_vmgState.vmgToZoneStb.sorted.length).toBe(0);
163
167
  });
164
168
  });