signalk-livedepth-plugin 0.3.18 → 0.3.19
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/CHANGELOG.md +30 -0
- package/README.md +22 -5
- package/dist/collector.d.ts +9 -1
- package/dist/collector.d.ts.map +1 -1
- package/dist/collector.js +23 -4
- package/dist/collector.js.map +1 -1
- package/dist/config.d.ts +41 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -7
- package/dist/config.js.map +1 -1
- package/dist/history.d.ts +5 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +24 -8
- package/dist/history.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +76 -14
- package/dist/plugin.js.map +1 -1
- package/dist/streams/live.d.ts +4 -1
- package/dist/streams/live.d.ts.map +1 -1
- package/dist/streams/live.js +8 -3
- package/dist/streams/live.js.map +1 -1
- package/dist/tide.d.ts +85 -0
- package/dist/tide.d.ts.map +1 -0
- package/dist/tide.js +136 -0
- package/dist/tide.js.map +1 -0
- package/dist/tiles/core/tile-source.d.ts +7 -0
- package/dist/tiles/core/tile-source.d.ts.map +1 -1
- package/dist/tiles/core/tile-source.js.map +1 -1
- package/dist/tiles/seed.d.ts +6 -1
- package/dist/tiles/seed.d.ts.map +1 -1
- package/dist/tiles/seed.js +49 -18
- package/dist/tiles/seed.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/public/js/renderer.js +44 -3
- package/public/js/renderer.js.map +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ All notable changes to this project are documented here, newest first. The
|
|
|
4
4
|
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
5
|
versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.19] - 2026-11-07
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Tide-corrected soundings.** With tide correction on, every depth is reduced
|
|
12
|
+
to a fixed chart datum (LAT or MLLW) using the predicted water level at that
|
|
13
|
+
sample's time and position, so soundings from different days and different
|
|
14
|
+
vessels share one grid. The Slackwater station database ships with the
|
|
15
|
+
plugin, so nothing is downloaded. The chart is told the datum in the plugin
|
|
16
|
+
metadata and stops folding its own live depth onto the corrected grid.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Depth is always read from a Signal K depth path.** The external
|
|
21
|
+
tide-compensated paths (`belowSurfaceTideCompensated`, `…LAT`, `…MLLW`) are
|
|
22
|
+
removed; tide compensation now happens inside the plugin. Point the Depth
|
|
23
|
+
setting at `environment.depth.belowSurface` and turn tide correction on.
|
|
24
|
+
- **The Depth selector is disabled while tide correction is on**, with a note
|
|
25
|
+
that changing the datum clears the grid because the stored cells are depths
|
|
26
|
+
in the old datum.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **A history rebuild no longer races the history provider.** The plugin waits
|
|
31
|
+
up to 30 s for the History API provider to register before falling back to
|
|
32
|
+
the HTTP endpoint, so a rebuild at server start no longer fails instantly.
|
|
33
|
+
- **Older pinned Slackwater versions are supported.** The tide predictor is
|
|
34
|
+
awaited, which works whether `nearestStation` returns a promise or the
|
|
35
|
+
predictor directly.
|
|
36
|
+
|
|
7
37
|
## [0.3.18] - 2026-11-02
|
|
8
38
|
|
|
9
39
|
### Changed
|
package/README.md
CHANGED
|
@@ -19,6 +19,9 @@ already covered is there to look at the next time you're in the area.
|
|
|
19
19
|
server has built up over time — extended live as you move.
|
|
20
20
|
- **Rebuild from history.** Already have a track recorded on the server? A
|
|
21
21
|
one-time rebuild fills the map in from it.
|
|
22
|
+
- **Comparable across days.** Optionally reduce every sounding to a fixed
|
|
23
|
+
vertical datum (LAT or MLLW) using the tide predicted at the moment and place
|
|
24
|
+
it was taken, so a depth recorded months ago still means the same thing.
|
|
22
25
|
|
|
23
26
|
## What you need
|
|
24
27
|
|
|
@@ -37,7 +40,6 @@ chart's. Open **Layer settings** on its row (or the LiveDepth toolbar button) to
|
|
|
37
40
|
choose what it shows:
|
|
38
41
|
|
|
39
42
|
- **Show the layer** — on or off.
|
|
40
|
-
- **Depth source** — your own recent track, or the accumulated depth map.
|
|
41
43
|
- **Shade limit** — the depth the shading runs to; deeper water is left to the
|
|
42
44
|
chart.
|
|
43
45
|
- **Isobath interval** — how far apart the depth-contour lines are.
|
|
@@ -51,7 +53,16 @@ Depth values are shown in your preferred units.
|
|
|
51
53
|
## Server settings
|
|
52
54
|
|
|
53
55
|
- **Path** — which depth measurement to record (below surface, transducer or
|
|
54
|
-
keel).
|
|
56
|
+
keel). Ignored when tide correction is on, which always uses the raw depth
|
|
57
|
+
below the surface.
|
|
58
|
+
- **Tide correction** — store soundings relative to a fixed vertical datum
|
|
59
|
+
instead of the moving surface. Choose chart datum, **LAT** or **MLLW**. LAT
|
|
60
|
+
and MLLW are fixed references at each location, so a corrected depth keeps its
|
|
61
|
+
meaning when you return; chart datum changes with the chart region. Each
|
|
62
|
+
sounding — live, rebuilt from history, or recorded by another vessel — is
|
|
63
|
+
reduced using the tide predicted at its own time and position. Changing the
|
|
64
|
+
datum clears the map, because the stored cells are depths in the old datum;
|
|
65
|
+
run **Rebuild from history** to repopulate it.
|
|
55
66
|
- **Depth transducer** — where the sounder sits on the hull, so each reading is
|
|
56
67
|
placed at it rather than at the position the server reports.
|
|
57
68
|
- **Transducer position correction** — shift readings from the vessel's
|
|
@@ -62,9 +73,15 @@ Depth values are shown in your preferred units.
|
|
|
62
73
|
## How it works
|
|
63
74
|
|
|
64
75
|
The plugin runs on your Signal K server and turns each sounding into a
|
|
65
|
-
Web-Mercator depth map, keeping the shallowest reading for every spot.
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
Web-Mercator depth map, keeping the shallowest reading for every spot. With tide
|
|
77
|
+
correction on it predicts the water level from the station database bundled with
|
|
78
|
+
the plugin (Slackwater) — offline, for any past or future instant — and
|
|
79
|
+
subtracts it before the reading reaches the map; a reading whose tide cannot be
|
|
80
|
+
predicted is skipped rather than stored in the wrong datum. Because the browser
|
|
81
|
+
cannot correct its own live readings, the layer then draws the corrected server
|
|
82
|
+
tiles (refreshed as new soundings arrive) instead of adding the raw live depth on
|
|
83
|
+
top. A chartplotter that supports plotter extensions draws it as a map layer,
|
|
84
|
+
rendering in the browser. The plugin offers the layer to the chartplotter automatically —
|
|
68
85
|
there's nothing to configure on the chart side.
|
|
69
86
|
|
|
70
87
|
## Development
|
package/dist/collector.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Writable } from "stream";
|
|
|
2
2
|
import { ServerAPI } from "@signalk/server-api";
|
|
3
3
|
import { TransducerOffset } from "./tiles/core/index.js";
|
|
4
4
|
import { Config } from "./config.js";
|
|
5
|
+
import { TideCorrection } from "./tide.js";
|
|
5
6
|
export interface CollectorOptions {
|
|
6
7
|
app: ServerAPI;
|
|
7
8
|
config: Config;
|
|
@@ -9,10 +10,17 @@ export interface CollectorOptions {
|
|
|
9
10
|
writer: Writable;
|
|
10
11
|
/** Shift each sounding from the reported position to the transducer. */
|
|
11
12
|
offset?: TransducerOffset;
|
|
13
|
+
/** Reduce each sounding to a fixed datum, using that sounding's own time and
|
|
14
|
+
* position. */
|
|
15
|
+
tide?: TideCorrection;
|
|
12
16
|
signal: AbortSignal;
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
19
|
* Pair depth with position once and feed the soundings to the tile writer.
|
|
20
|
+
*
|
|
21
|
+
* With tide correction on, the raw depth below the surface is recorded (not the
|
|
22
|
+
* configured depth path, which may already be compensated) and each sounding is
|
|
23
|
+
* reduced to the chosen datum before it reaches the store.
|
|
16
24
|
*/
|
|
17
|
-
export declare function createCollector({ app, config, writer, offset, signal, }: CollectorOptions): import("stream-transform").Transformer;
|
|
25
|
+
export declare function createCollector({ app, config, writer, offset, tide, signal, }: CollectorOptions): import("stream-transform").Transformer;
|
|
18
26
|
//# sourceMappingURL=collector.d.ts.map
|
package/dist/collector.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,cAAc,EAAsB,MAAM,WAAW,CAAC;AAG/D,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,CAAC;IACjB,wEAAwE;IACxE,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;mBACe;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,MAAM,GACP,EAAE,gBAAgB,0CAyBlB"}
|
package/dist/collector.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { transform } from "stream-transform";
|
|
2
2
|
import { createLiveStream, toPrecision } from "./streams/index.js";
|
|
3
|
+
import { recordingDepthPath } from "./tide.js";
|
|
3
4
|
/**
|
|
4
5
|
* Pair depth with position once and feed the soundings to the tile writer.
|
|
6
|
+
*
|
|
7
|
+
* With tide correction on, the raw depth below the surface is recorded (not the
|
|
8
|
+
* configured depth path, which may already be compensated) and each sounding is
|
|
9
|
+
* reduced to the chosen datum before it reaches the store.
|
|
5
10
|
*/
|
|
6
|
-
export function createCollector({ app, config, writer, offset, signal, }) {
|
|
7
|
-
const live = createLiveStream(app, config, offset);
|
|
11
|
+
export function createCollector({ app, config, writer, offset, tide, signal, }) {
|
|
12
|
+
const live = createLiveStream(app, config, offset, recordingDepthPath(config, tide !== undefined));
|
|
8
13
|
const precise = live.pipe(transform(toPrecision()));
|
|
9
|
-
precise.pipe(
|
|
14
|
+
const output = tide ? precise.pipe(transform(tideTransform(tide))) : precise;
|
|
15
|
+
output.pipe(writer);
|
|
10
16
|
const destroy = () => {
|
|
11
17
|
live.destroy();
|
|
12
18
|
precise.destroy();
|
|
19
|
+
output.destroy();
|
|
13
20
|
writer.destroy();
|
|
14
21
|
};
|
|
15
22
|
if (signal.aborted) {
|
|
@@ -18,6 +25,18 @@ export function createCollector({ app, config, writer, offset, signal, }) {
|
|
|
18
25
|
else {
|
|
19
26
|
signal.addEventListener("abort", destroy, { once: true });
|
|
20
27
|
}
|
|
21
|
-
return
|
|
28
|
+
return output;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Async stream handler that corrects each sounding. A sounding is dropped when
|
|
32
|
+
* the tide is unavailable, so a datum grid never mixes in a raw depth.
|
|
33
|
+
*/
|
|
34
|
+
function tideTransform(tide) {
|
|
35
|
+
return (data, callback) => {
|
|
36
|
+
tide
|
|
37
|
+
.correct(data.longitude, data.latitude, data.timestamp, data.depth)
|
|
38
|
+
.then((depth) => callback(null, depth === undefined ? undefined : { ...data, depth }))
|
|
39
|
+
.catch((error) => callback(error));
|
|
40
|
+
};
|
|
22
41
|
}
|
|
23
42
|
//# sourceMappingURL=collector.js.map
|
package/dist/collector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAK7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAK7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAkB,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAgB/D;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,MAAM,GACW;IACjB,MAAM,IAAI,GAAG,gBAAgB,CAC3B,GAAG,EACH,MAAM,EACN,MAAM,EACN,kBAAkB,CAAC,MAAM,EAAE,IAAI,KAAK,SAAS,CAAC,CAC/C,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7E,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEpB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC;IAEF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,IAAoB;IACzC,OAAO,CACL,IAAoB,EACpB,QAAiE,EACjE,EAAE;QACF,IAAI;aACD,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC;aAClE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACd,QAAQ,CAAC,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CACrE;aACA,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAc,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { ServerAPI } from "@signalk/server-api";
|
|
2
|
+
import { TideDatum } from "./tide.js";
|
|
2
3
|
export type Config = {
|
|
3
|
-
/** Which depth
|
|
4
|
-
* its own reference, so
|
|
4
|
+
/** Which depth measurement to record when tide correction is off. Signal K
|
|
5
|
+
* already publishes each relative to its own reference, so it is stored
|
|
6
|
+
* as-is. With tide correction on, the raw depth below the surface is used
|
|
7
|
+
* and reduced to {@link Config.tide.datum} instead. */
|
|
5
8
|
path: (typeof DepthPaths)[number];
|
|
9
|
+
/** Reduce each sounding to a fixed vertical datum, using the predicted tide
|
|
10
|
+
* at that sounding's own time and position. */
|
|
11
|
+
tide?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
datum?: TideDatum;
|
|
14
|
+
};
|
|
6
15
|
/** The depth transducer's position on the hull: `y` from the bow, `x` from
|
|
7
16
|
* the centreline (+starboard). Used only to place a sounding at the
|
|
8
17
|
* transducer rather than at the position the server reports. */
|
|
@@ -40,7 +49,13 @@ export type Config = {
|
|
|
40
49
|
vesselFootprint?: boolean;
|
|
41
50
|
};
|
|
42
51
|
};
|
|
43
|
-
export declare const DepthPaths: readonly ["belowSurface", "belowTransducer", "belowKeel"
|
|
52
|
+
export declare const DepthPaths: readonly ["belowSurface", "belowTransducer", "belowKeel"];
|
|
53
|
+
/**
|
|
54
|
+
* The form overrides for the saved config. The Depth selector has no effect
|
|
55
|
+
* while tide correction is on (the raw below-surface depth is used), so the
|
|
56
|
+
* admin UI should show it disabled rather than as a live control.
|
|
57
|
+
*/
|
|
58
|
+
export declare function formUiSchema(config?: Pick<Config, "tide">): Record<string, object>;
|
|
44
59
|
export declare function schema(app: ServerAPI): {
|
|
45
60
|
type: string;
|
|
46
61
|
description: string;
|
|
@@ -49,9 +64,30 @@ export declare function schema(app: ServerAPI): {
|
|
|
49
64
|
type: string;
|
|
50
65
|
title: string;
|
|
51
66
|
description: string;
|
|
52
|
-
enum: readonly ["belowSurface", "belowTransducer", "belowKeel"
|
|
67
|
+
enum: readonly ["belowSurface", "belowTransducer", "belowKeel"];
|
|
53
68
|
enumNames: string[];
|
|
54
|
-
default?: "belowKeel" | "belowSurface" | "
|
|
69
|
+
default?: "belowKeel" | "belowSurface" | "belowTransducer" | undefined;
|
|
70
|
+
};
|
|
71
|
+
tide: {
|
|
72
|
+
type: string;
|
|
73
|
+
title: string;
|
|
74
|
+
description: string;
|
|
75
|
+
properties: {
|
|
76
|
+
enabled: {
|
|
77
|
+
type: string;
|
|
78
|
+
default: boolean;
|
|
79
|
+
title: string;
|
|
80
|
+
description: string;
|
|
81
|
+
};
|
|
82
|
+
datum: {
|
|
83
|
+
type: string;
|
|
84
|
+
title: string;
|
|
85
|
+
enum: readonly TideDatum[];
|
|
86
|
+
enumNames: string[];
|
|
87
|
+
default: string;
|
|
88
|
+
description: string;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
55
91
|
};
|
|
56
92
|
sounder: {
|
|
57
93
|
type: string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { TIDE_DATUMS } from "./tide.js";
|
|
1
2
|
export const DepthPaths = [
|
|
2
3
|
"belowSurface",
|
|
3
4
|
"belowTransducer",
|
|
4
5
|
"belowKeel",
|
|
5
|
-
"belowSurfaceTideCompensated",
|
|
6
|
-
"belowSurfaceTideCompensatedLAT",
|
|
7
|
-
"belowSurfaceTideCompensatedMLLW",
|
|
8
6
|
];
|
|
7
|
+
/**
|
|
8
|
+
* The form overrides for the saved config. The Depth selector has no effect
|
|
9
|
+
* while tide correction is on (the raw below-surface depth is used), so the
|
|
10
|
+
* admin UI should show it disabled rather than as a live control.
|
|
11
|
+
*/
|
|
12
|
+
export function formUiSchema(config) {
|
|
13
|
+
return config?.tide?.enabled ? { path: { "ui:disabled": true } } : {};
|
|
14
|
+
}
|
|
9
15
|
export function schema(app) {
|
|
10
16
|
const defaultPath = DepthPaths.find((path) => app.getSelfPath(`environment.depth.${path}.value`));
|
|
11
17
|
// A `default` is attached only when present: `default: undefined` survives in
|
|
@@ -20,18 +26,40 @@ export function schema(app) {
|
|
|
20
26
|
path: {
|
|
21
27
|
type: "string",
|
|
22
28
|
title: "Depth",
|
|
23
|
-
description: "Which depth
|
|
29
|
+
description: "Which depth measurement to record. Signal K publishes each relative to its own reference, so no draft or transducer offset is needed here. Disabled while tide correction is on: the raw depth below the surface is used then and reduced to the chosen datum.",
|
|
24
30
|
enum: DepthPaths,
|
|
25
31
|
enumNames: [
|
|
26
32
|
"Depth below surface",
|
|
27
33
|
"Depth below transducer",
|
|
28
34
|
"Depth below keel",
|
|
29
|
-
"Depth below surface (tide compensated)",
|
|
30
|
-
"Depth below surface (tide compensated, LAT)",
|
|
31
|
-
"Depth below surface (tide compensated, MLLW)",
|
|
32
35
|
],
|
|
33
36
|
...(defaultPath === undefined ? {} : { default: defaultPath }),
|
|
34
37
|
},
|
|
38
|
+
tide: {
|
|
39
|
+
type: "object",
|
|
40
|
+
title: "Tide correction",
|
|
41
|
+
description: "Store soundings relative to a fixed vertical datum by subtracting the predicted tide at each sounding's own time and position. A depth below the surface rises and falls with the tide; a depth below a datum does not, so it still means the same thing when you return months later. When this is on, depth is always calculated using 'Depth below surface'. Changing the datum clears the depth map, because the stored cells are depths in the old datum.",
|
|
42
|
+
properties: {
|
|
43
|
+
enabled: {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
default: false,
|
|
46
|
+
title: "Correct samples for tides?",
|
|
47
|
+
description: "Off records the selected Depth as measured. On records the depth below the surface reduced to the datum below.",
|
|
48
|
+
},
|
|
49
|
+
datum: {
|
|
50
|
+
type: "string",
|
|
51
|
+
title: "Datum",
|
|
52
|
+
enum: TIDE_DATUMS,
|
|
53
|
+
enumNames: [
|
|
54
|
+
"Chart datum / local datum",
|
|
55
|
+
"Lowest Astronomical Tide (LAT)",
|
|
56
|
+
"Mean Lower Low Water (MLLW)",
|
|
57
|
+
],
|
|
58
|
+
default: "LAT",
|
|
59
|
+
description: "LAT and MLLW are fixed references at each location, so a corrected sounding keeps its meaning wherever and whenever you return. Chart datum is set per chart region, so a map corrected to it cannot be compared across regions; LAT and MLLW are the predictable choices.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
35
63
|
sounder: {
|
|
36
64
|
type: "object",
|
|
37
65
|
title: "Depth Transducer",
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/history.d.ts
CHANGED
|
@@ -3,12 +3,16 @@ import { HistoryApi, ValuesResponse } from "@signalk/server-api/history";
|
|
|
3
3
|
export type HistoryAPIOptions = {
|
|
4
4
|
app: ServerAPI;
|
|
5
5
|
host?: string;
|
|
6
|
+
/** How long to wait for a provider to register, in milliseconds. */
|
|
7
|
+
waitMs?: number;
|
|
8
|
+
/** Delay between provider checks, in milliseconds. */
|
|
9
|
+
pollMs?: number;
|
|
6
10
|
};
|
|
7
11
|
/**
|
|
8
12
|
* The History Provider API is new and may not be available. This uses a provider if available,
|
|
9
13
|
* and otherwise falls back to direct HTTP access if available.
|
|
10
14
|
*/
|
|
11
|
-
export declare function getHistoryAPI({ app, port, host, }: HistoryAPIOptions): Promise<HistoryApi | undefined>;
|
|
15
|
+
export declare function getHistoryAPI({ app, port, host, waitMs, pollMs, }: HistoryAPIOptions): Promise<HistoryApi | undefined>;
|
|
12
16
|
/**
|
|
13
17
|
* Build a lookup from SignalK path to its column index in a history `DataRow`.
|
|
14
18
|
*
|
package/dist/history.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAGL,UAAU,EAIV,cAAc,EACf,MAAM,6BAA6B,CAAC;AAKrC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAGL,UAAU,EAIV,cAAc,EACf,MAAM,6BAA6B,CAAC;AAKrC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AASF;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAClC,GAAG,EAEH,IAAqE,EACrE,IAA8D,EAC9D,MAAiC,EACjC,MAAiC,GAClC,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAwErD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,SAAS,EAC5C,SAAS,EAAE,SAAS;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,EAAE,GACnC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrB"}
|
package/dist/history.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { BATHY_EPOCH } from "./constants.js";
|
|
3
3
|
import fetch from "./fetch.js";
|
|
4
|
+
const DEFAULT_PROVIDER_WAIT_MS = 30_000;
|
|
5
|
+
const DEFAULT_PROVIDER_POLL_MS = 500;
|
|
4
6
|
/**
|
|
5
7
|
* The History Provider API is new and may not be available. This uses a provider if available,
|
|
6
8
|
* and otherwise falls back to direct HTTP access if available.
|
|
7
9
|
*/
|
|
8
10
|
export async function getHistoryAPI({ app,
|
|
9
11
|
// @ts-expect-error: app.config is not a public API
|
|
10
|
-
port = Number(process.env.PORT) || app.config?.settings?.port || 3000, host = process.env.SIGNALK_HOST ?? `http://localhost:${port}/`, }) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
port = Number(process.env.PORT) || app.config?.settings?.port || 3000, host = process.env.SIGNALK_HOST ?? `http://localhost:${port}/`, waitMs = DEFAULT_PROVIDER_WAIT_MS, pollMs = DEFAULT_PROVIDER_POLL_MS, }) {
|
|
13
|
+
// Plugins start in an unspecified order, so this plugin can reach the History
|
|
14
|
+
// API before the provider plugin has registered. Wait briefly for it instead
|
|
15
|
+
// of falling through to the HTTP path, which may not be reachable from the
|
|
16
|
+
// server's own network namespace.
|
|
17
|
+
const api = await waitForProvider();
|
|
18
|
+
if (api) {
|
|
15
19
|
app.debug("Using built-in history provider");
|
|
16
20
|
return api;
|
|
17
21
|
}
|
|
18
|
-
catch {
|
|
19
|
-
// History provider API not available or no provider configured.
|
|
20
|
-
}
|
|
21
22
|
try {
|
|
22
23
|
await getContexts({
|
|
23
24
|
from: BATHY_EPOCH,
|
|
@@ -30,6 +31,21 @@ port = Number(process.env.PORT) || app.config?.settings?.port || 3000, host = pr
|
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
return { getValues, getContexts, getPaths };
|
|
34
|
+
async function waitForProvider() {
|
|
35
|
+
if (typeof app.getHistoryApi !== "function")
|
|
36
|
+
return undefined;
|
|
37
|
+
const deadline = Date.now() + waitMs;
|
|
38
|
+
for (;;) {
|
|
39
|
+
try {
|
|
40
|
+
return await app.getHistoryApi();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
if (Date.now() >= deadline)
|
|
44
|
+
return undefined;
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
33
49
|
async function get(path, params = {}) {
|
|
34
50
|
const url = new URL(path, new URL("/signalk/v2/api/history/", host));
|
|
35
51
|
url.search = new URLSearchParams(params).toString();
|
package/dist/history.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,MAAM,YAAY,CAAC;AAW/B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,GAAG;AACH,mDAAmD;AACnD,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,EACrE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,oBAAoB,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAEtE,OAAO,EAAE,gBAAgB,EAAa,MAAM,uBAAuB,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAEtE,OAAO,EAAE,gBAAgB,EAAa,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAwB,MAAM,EAAE,MAAM,aAAa,CAAC;AAuB3D;;;;;;;mEAOmE;AACnE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAWpE;AAED;sEACsE;AACtE,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAOjE;AAQD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,SAAS,CA2B9B;AAOD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAqM3D"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { join } from "path";
|
|
2
|
-
import { schema } from "./config.js";
|
|
2
|
+
import { schema, formUiSchema } from "./config.js";
|
|
3
3
|
import { createTileDB, createTileStore } from "./tiles/store.js";
|
|
4
4
|
import { createTileWriter } from "./tiles/writer.js";
|
|
5
|
-
import { seedFromHistory } from "./tiles/seed.js";
|
|
5
|
+
import { SEED_CURSOR_KEY, seedFromHistory } from "./tiles/seed.js";
|
|
6
|
+
import { TIDE_MODE_KEY, createSlackwaterTideProvider, createTideCorrection, datumLabel, shouldResetGrid, tideMode, } from "./tide.js";
|
|
6
7
|
import { registerBathymetryRoutes, registerSeedRoutes, } from "./tiles/api.js";
|
|
7
8
|
import { registerPlotterExtension } from "./plotterext/provider.js";
|
|
8
9
|
import { createCollector } from "./collector.js";
|
|
@@ -71,6 +72,10 @@ export function resolveTransducerOffset(app, config) {
|
|
|
71
72
|
starboard: config.sounder?.x ?? 0,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
75
|
+
/** A parenthetical for rows dropped because the tide was unavailable. */
|
|
76
|
+
function skipNote(skipped) {
|
|
77
|
+
return skipped > 0 ? ` (${skipped} skipped: no tide prediction)` : "";
|
|
78
|
+
}
|
|
74
79
|
export default function createPlugin(app) {
|
|
75
80
|
let abortController = undefined;
|
|
76
81
|
// The gridded store served to chart clients. Built in start(), read by the
|
|
@@ -78,6 +83,9 @@ export default function createPlugin(app) {
|
|
|
78
83
|
let tileStore;
|
|
79
84
|
let config;
|
|
80
85
|
let transducer;
|
|
86
|
+
let tide;
|
|
87
|
+
/** The datum the store is in; also reported to chart clients. */
|
|
88
|
+
let tideState = "off";
|
|
81
89
|
let seedStatus = { running: false };
|
|
82
90
|
let running = false;
|
|
83
91
|
let routesRegistered = false;
|
|
@@ -88,6 +96,14 @@ export default function createPlugin(app) {
|
|
|
88
96
|
const cfg = config;
|
|
89
97
|
if (!store || !cfg)
|
|
90
98
|
return;
|
|
99
|
+
// The store's datum is fixed once it holds cells: fold history with the
|
|
100
|
+
// same correction it was built with, and refuse rather than write raw rows
|
|
101
|
+
// into a datum grid.
|
|
102
|
+
if (tideState !== "off" && !tide) {
|
|
103
|
+
seedStatus = { running: false, error: "tide model unavailable" };
|
|
104
|
+
app.setPluginError("Cannot rebuild from history: the tide model is unavailable, so raw depths would be stored in the wrong datum.");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
91
107
|
seedStatus = { running: true };
|
|
92
108
|
app.setPluginStatus("Rebuilding bathymetry tiles from history…");
|
|
93
109
|
try {
|
|
@@ -97,14 +113,15 @@ export default function createPlugin(app) {
|
|
|
97
113
|
config: cfg,
|
|
98
114
|
resolution,
|
|
99
115
|
offset: transducer,
|
|
116
|
+
tide,
|
|
100
117
|
signal: abortController?.signal,
|
|
101
118
|
onProgress: (p) => {
|
|
102
119
|
seedStatus = { running: !p.done, progress: p };
|
|
103
|
-
app.setPluginStatus(`Rebuilding bathymetry tiles: ${p.points} points over ${p.chunks} windows`);
|
|
120
|
+
app.setPluginStatus(`Rebuilding bathymetry tiles: ${p.points} points over ${p.chunks} windows${skipNote(p.skipped)}`);
|
|
104
121
|
},
|
|
105
122
|
});
|
|
106
123
|
seedStatus = { running: false, progress };
|
|
107
|
-
app.setPluginStatus(`Bathymetry rebuild complete: ${progress.points} points`);
|
|
124
|
+
app.setPluginStatus(`Bathymetry rebuild complete: ${progress.points} points${skipNote(progress.skipped)}`);
|
|
108
125
|
}
|
|
109
126
|
catch (err) {
|
|
110
127
|
seedStatus = { running: false, error: String(err) };
|
|
@@ -122,21 +139,63 @@ export default function createPlugin(app) {
|
|
|
122
139
|
abortController = new AbortController();
|
|
123
140
|
tileStore = createTileStore(createTileDB(join(app.getDataDirPath(), `bathymetry-tiles.sqlite`)));
|
|
124
141
|
transducer = resolveTransducerOffset(app, config);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
// Build the tide source before the collector starts, so the first
|
|
143
|
+
// sounding already goes through it. The station database loads here, once.
|
|
144
|
+
const configured = tideMode(config);
|
|
145
|
+
tideState = configured;
|
|
146
|
+
tide = undefined;
|
|
147
|
+
let collecting = true;
|
|
148
|
+
if (configured !== "off") {
|
|
149
|
+
try {
|
|
150
|
+
tide = createTideCorrection(await createSlackwaterTideProvider(), configured);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
// A transient model failure must not clear or relabel an existing
|
|
154
|
+
// grid, nor write raw depths into a datum grid. Keep the stored
|
|
155
|
+
// datum, record nothing, and surface the error.
|
|
156
|
+
collecting = false;
|
|
157
|
+
const stored = tileStore.getMeta(TIDE_MODE_KEY);
|
|
158
|
+
tideState = stored === undefined ? "off" : stored;
|
|
159
|
+
app.setPluginError(`Tide correction is on but the tide model could not be loaded ` +
|
|
160
|
+
`(${err}); recording is paused and the existing grid is kept.`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (collecting) {
|
|
164
|
+
// The cells hold depths, not raw soundings, so a grid built in another
|
|
165
|
+
// datum cannot be corrected in place. Clearing is the only way to avoid
|
|
166
|
+
// silently mixing datums; the old seed cursor goes with it.
|
|
167
|
+
const reset = shouldResetGrid(tileStore.getMeta(TIDE_MODE_KEY), tideState);
|
|
168
|
+
if (reset) {
|
|
169
|
+
tileStore.clear();
|
|
170
|
+
tileStore.setMeta(SEED_CURSOR_KEY, "");
|
|
171
|
+
}
|
|
172
|
+
tileStore.setMeta(TIDE_MODE_KEY, tideState);
|
|
173
|
+
createCollector({
|
|
174
|
+
app,
|
|
175
|
+
config,
|
|
176
|
+
writer: createTileWriter(tileStore),
|
|
177
|
+
offset: transducer,
|
|
178
|
+
tide,
|
|
179
|
+
signal: abortController.signal,
|
|
180
|
+
});
|
|
181
|
+
app.setPluginStatus(tideState === "off"
|
|
182
|
+
? "Collecting depth into the bathymetry tile store"
|
|
183
|
+
: `Collecting depth into the bathymetry tile store, relative to ${datumLabel(tideState)}`);
|
|
184
|
+
if (reset) {
|
|
185
|
+
app.setPluginStatus("The tide datum changed, so the bathymetry grid was cleared. " +
|
|
186
|
+
"Rebuild from history to repopulate it.");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
133
189
|
running = true;
|
|
134
190
|
// Offer the chart layer to a host chartplotter, and serve the store the
|
|
135
191
|
// layer reads from a top-level, non-admin route.
|
|
136
192
|
registerPlotterExtension(app, () => running);
|
|
137
193
|
if (!routesRegistered) {
|
|
138
194
|
registerBathymetryRoutes(app, () => tileStore, () => ({
|
|
139
|
-
depthPath: config
|
|
195
|
+
depthPath: config
|
|
196
|
+
? `environment.depth.${tideState === "off" ? config.path : "belowSurface"}`
|
|
197
|
+
: undefined,
|
|
198
|
+
tide: { mode: tideState },
|
|
140
199
|
transducer,
|
|
141
200
|
vessel: {
|
|
142
201
|
loa: readLengthOverall(app),
|
|
@@ -148,7 +207,7 @@ export default function createPlugin(app) {
|
|
|
148
207
|
}));
|
|
149
208
|
routesRegistered = true;
|
|
150
209
|
}
|
|
151
|
-
if (startConfig.seed?.run) {
|
|
210
|
+
if (collecting && startConfig.seed?.run) {
|
|
152
211
|
// One-time rebuild. Clear the trigger when it finishes (or fails) so it
|
|
153
212
|
// doesn't fire again on the next start; an interrupted run resumes from
|
|
154
213
|
// the stored cursor instead of repeating.
|
|
@@ -167,6 +226,9 @@ export default function createPlugin(app) {
|
|
|
167
226
|
schema() {
|
|
168
227
|
return schema(app);
|
|
169
228
|
},
|
|
229
|
+
uiSchema() {
|
|
230
|
+
return formUiSchema(config);
|
|
231
|
+
},
|
|
170
232
|
registerWithRouter(router) {
|
|
171
233
|
// The one-time seed trigger stays admin-only, on the plugin's own router;
|
|
172
234
|
// the store the extension reads is served from the top-level route above.
|