mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-09 04:44:01 +00:00
fix(cockpit): a river bridge keeps its water height through empty and coarse reads
A full-detail water read that sampled nothing (tiles not loaded) fell back to the geoid, and while a full read ran, a loaded-tile read with one coarse low probe did the same: on the Seine either dropped the eye ~27 m under the water until the next read. An empty read is now no answer, and once a full-detail read has answered only full-detail reads change the height. The read sequence numbers go: only one full-detail read runs at a time, so a late read can never be older than one already applied.
This commit is contained in:
1 parent
36a2216528
commit
18c15d0352
3 files changed
+70
-15
No files matched your search
@@ -1568,8 +1568,10 @@ eight probes 30 m out, the lowest probe taken as the water when it is 2 m to
|
||||
4,000 m above sea level. The first read starts from the tiles already loaded, then
|
||||
the full-detail read replaces it; it repeats every 5 s. Only one full-detail
|
||||
read runs at a time: while one is still running, a refresh reads the loaded
|
||||
tiles instead, and the slow read applies whenever it lands unless a newer
|
||||
one has. A failed read keeps the sea surface.
|
||||
tiles instead, and the slow read applies whenever it lands. Once a
|
||||
full-detail read has answered, loaded-tile reads no longer change the height,
|
||||
and a read that sampled nothing keeps the height it had. A failed read keeps
|
||||
the sea surface.
|
||||
|
||||
The HUD keeps its frame, vision styles, briefing, weather and Display/Radio
|
||||
utilities. It names the ship, reads `{type} · LIVE AIS · EYE {height}`, shows
|
||||
|
||||
@@ -1261,6 +1261,59 @@ test('a slow water read is never stacked, and lands however late', async (t) =>
|
||||
);
|
||||
});
|
||||
|
||||
test('a river height stands through reads that sample nothing or one coarse probe', async (t) => {
|
||||
const clock = manualClock(t, START_MS);
|
||||
const sea = await bridgeAt(t, clock);
|
||||
frames(sea.cockpit, clock, sea.shots, 60);
|
||||
const seaEyeM = eyeHeightM(sea.shots.at(-1));
|
||||
sea.cockpit.exit();
|
||||
|
||||
clock.set(START_MS);
|
||||
const record = { ...RECORD, lastPositionEpoch: START_MS / 1000 };
|
||||
const ship = fixture(t, { record });
|
||||
const reads = [];
|
||||
ship.viewer.scene.sampleHeightMostDetailed = (positions) =>
|
||||
new Promise((resolve) => reads.push({ positions, resolve }));
|
||||
ship.viewer.scene.sampleHeight = () => undefined;
|
||||
const answer = (read, height) =>
|
||||
read.resolve(
|
||||
read.positions.map((position) => Object.assign(position, { height })),
|
||||
);
|
||||
ship.cockpit.enter();
|
||||
await settle();
|
||||
ship.cockpit.update();
|
||||
await settle();
|
||||
answer(reads[0], 27);
|
||||
await settle();
|
||||
frames(ship.cockpit, clock, ship.shots, 120);
|
||||
await settle();
|
||||
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'on the river', 0.5);
|
||||
// The next full read lands over tiles not loaded yet: every probe empty.
|
||||
assert.equal(reads.length, 2);
|
||||
answer(reads[1], undefined);
|
||||
await settle();
|
||||
frames(ship.cockpit, clock, ship.shots, 120);
|
||||
await settle();
|
||||
near(
|
||||
eyeHeightM(ship.shots.at(-1)) - seaEyeM,
|
||||
27,
|
||||
'an empty full read is no answer',
|
||||
0.5,
|
||||
);
|
||||
// While the next one runs, coarse loaded tiles read the sea.
|
||||
assert.equal(reads.length, 3);
|
||||
ship.viewer.scene.sampleHeight = () => 0.5;
|
||||
frames(ship.cockpit, clock, ship.shots, 240);
|
||||
await settle();
|
||||
assert.equal(reads.length, 3, 'still the one read in flight');
|
||||
near(
|
||||
eyeHeightM(ship.shots.at(-1)) - seaEyeM,
|
||||
27,
|
||||
'loaded tiles do not undo a full read',
|
||||
0.5,
|
||||
);
|
||||
});
|
||||
|
||||
test('a client clock minutes fast does not age the bridge into a stale feed', async (t) => {
|
||||
// The feed's newest fix is this ship's; the client clock runs 200 s
|
||||
// ahead of the feed and received the refresh just now.
|
||||
|
||||
+13
-13
@@ -109,7 +109,10 @@ export function update() {
|
||||
* full-detail read runs at a time (each is nine picks Cesium traverses
|
||||
* every frame until it settles): while one is still running, a refresh
|
||||
* reads the loaded tiles instead, and the slow read applies whenever it
|
||||
* lands unless a newer one has.
|
||||
* lands. Once a full-detail read has answered, only full-detail reads
|
||||
* change the height: a loaded-tile read stands in only until the first
|
||||
* lands, so one coarse probe cannot drop a river bridge to the sea, and a
|
||||
* read that sampled nothing keeps the height it had.
|
||||
* @param {{latitude: number, longitude: number}} [at] Where to read the
|
||||
* water: the dead-reckoned position the camera rides.
|
||||
* @returns {number} Ellipsoidal metres.
|
||||
@@ -133,15 +136,14 @@ function vesselSurfaceM(info, nowMs, at = info) {
|
||||
mmsi: info.mmsi,
|
||||
heightM: loadedWaterlineM(scene, probes, seaM),
|
||||
sampledMs: nowMs,
|
||||
// Full-detail reads by number: the last started, the last applied.
|
||||
startedSeq: 0,
|
||||
landedSeq: 0,
|
||||
// Whether a full-detail read has answered for this ship.
|
||||
landed: false,
|
||||
inFlight: false,
|
||||
};
|
||||
this.vesselWaterline = read;
|
||||
} else {
|
||||
read.sampledMs = nowMs;
|
||||
if (read.inFlight) {
|
||||
if (read.inFlight && !read.landed) {
|
||||
const loadedM = loadedWaterlineM(scene, probes, seaM);
|
||||
if (Number.isFinite(loadedM)) read.heightM = loadedM;
|
||||
}
|
||||
@@ -155,7 +157,6 @@ function vesselSurfaceM(info, nowMs, at = info) {
|
||||
|
||||
/** Start one full-detail water read for `read`; its answer lands on it. */
|
||||
function readWaterline(read, scene, probes, seaM) {
|
||||
const seq = ++read.startedSeq;
|
||||
read.inFlight = true;
|
||||
Promise.resolve()
|
||||
.then(() => scene.sampleHeightMostDetailed(probes))
|
||||
@@ -163,13 +164,12 @@ function readWaterline(read, scene, probes, seaM) {
|
||||
(samples) => {
|
||||
if (this.vesselWaterline !== read) return;
|
||||
read.inFlight = false;
|
||||
// However late it lands, it applies unless a newer read has.
|
||||
if (seq <= read.landedSeq) return;
|
||||
read.landedSeq = seq;
|
||||
read.heightM = vesselWaterlineM(
|
||||
seaM,
|
||||
(samples || []).map((sample) => sample?.height),
|
||||
);
|
||||
// However late it lands, it applies; one that sampled nothing (tiles
|
||||
// not loaded yet) is no answer and keeps the height already read.
|
||||
const heights = (samples || []).map((sample) => sample?.height);
|
||||
if (!heights.some(Number.isFinite)) return;
|
||||
read.landed = true;
|
||||
read.heightM = vesselWaterlineM(seaM, heights);
|
||||
},
|
||||
() => {
|
||||
if (this.vesselWaterline === read) read.inFlight = false;
|
||||
|
||||
Reference in new issue
Block a user