@wodzik/cubecore 0.1.12 → 0.1.13
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/bluetooth/clock.d.ts
CHANGED
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export declare class ClockSync {
|
|
11
11
|
private readonly window;
|
|
12
|
+
private readonly jump;
|
|
12
13
|
private samples;
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @param window how many recent samples the offset is estimated from (the cube clock drifts slowly).
|
|
16
|
+
* @param jump a cube clock that lost time (GAN gen2's 16-bit move intervals roll over after
|
|
17
|
+
* ~65 s idle): this many samples in a row that agree with each other (within `agree` ms)
|
|
18
|
+
* and are all over `jumpMs` later than the offset — the cube clock jumped, start again
|
|
19
|
+
* from them. A burst of late notifications (a backgrounded tab) doesn't agree: its
|
|
20
|
+
* samples spread over the moves' real intervals.
|
|
21
|
+
*/
|
|
22
|
+
constructor(window?: number, jump?: {
|
|
23
|
+
samples: number;
|
|
24
|
+
jumpMs: number;
|
|
25
|
+
agree: number;
|
|
26
|
+
});
|
|
15
27
|
/** Offset (local − cube) estimated so far, or null before the first sample. */
|
|
16
28
|
get offset(): number | null;
|
|
17
29
|
/** Feed one event: its cube time and, if it has one, the local time it arrived. */
|
package/bluetooth/clock.js
CHANGED
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export class ClockSync {
|
|
11
11
|
window;
|
|
12
|
+
jump;
|
|
12
13
|
samples = [];
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @param window how many recent samples the offset is estimated from (the cube clock drifts slowly).
|
|
16
|
+
* @param jump a cube clock that lost time (GAN gen2's 16-bit move intervals roll over after
|
|
17
|
+
* ~65 s idle): this many samples in a row that agree with each other (within `agree` ms)
|
|
18
|
+
* and are all over `jumpMs` later than the offset — the cube clock jumped, start again
|
|
19
|
+
* from them. A burst of late notifications (a backgrounded tab) doesn't agree: its
|
|
20
|
+
* samples spread over the moves' real intervals.
|
|
21
|
+
*/
|
|
22
|
+
constructor(window = 64, jump = { samples: 5, jumpMs: 1000, agree: 200 }) {
|
|
15
23
|
this.window = window;
|
|
24
|
+
this.jump = jump;
|
|
16
25
|
}
|
|
17
26
|
/** Offset (local − cube) estimated so far, or null before the first sample. */
|
|
18
27
|
get offset() {
|
|
@@ -25,6 +34,14 @@ export class ClockSync {
|
|
|
25
34
|
this.samples.push(localTime - cubeTime);
|
|
26
35
|
if (this.samples.length > this.window)
|
|
27
36
|
this.samples.shift();
|
|
37
|
+
const { samples: n, jumpMs, agree } = this.jump;
|
|
38
|
+
if (this.samples.length <= n)
|
|
39
|
+
return;
|
|
40
|
+
const recent = this.samples.slice(-n);
|
|
41
|
+
const best = Math.min(...this.samples.slice(0, -n));
|
|
42
|
+
const lo = Math.min(...recent);
|
|
43
|
+
if (lo - best > jumpMs && Math.max(...recent) - lo <= agree)
|
|
44
|
+
this.samples = recent;
|
|
28
45
|
}
|
|
29
46
|
/** The cube time on the page's clock (null until an offset is known). */
|
|
30
47
|
toLocal(cubeTime) {
|
|
@@ -202,6 +202,7 @@ declare class GanCubeClassicConnection implements GanCubeConnection, GanCubeRawC
|
|
|
202
202
|
declare class GanGen2ProtocolDriver implements GanProtocolDriver {
|
|
203
203
|
private lastSerial;
|
|
204
204
|
private lastMoveTimestamp;
|
|
205
|
+
private hadMove;
|
|
205
206
|
private cubeTimestamp;
|
|
206
207
|
createCommandMessage(command: GanCubeCommand): Uint8Array | undefined;
|
|
207
208
|
handleStateEvent(conn: GanCubeRawConnection, eventMessage: Uint8Array): Promise<GanCubeEvent[]>;
|
|
@@ -124,6 +124,7 @@ class GanProtocolMessageView {
|
|
|
124
124
|
class GanGen2ProtocolDriver {
|
|
125
125
|
lastSerial = -1;
|
|
126
126
|
lastMoveTimestamp = 0;
|
|
127
|
+
hadMove = false;
|
|
127
128
|
cubeTimestamp = 0;
|
|
128
129
|
createCommandMessage(command) {
|
|
129
130
|
var msg = new Uint8Array(20).fill(0);
|
|
@@ -190,6 +191,14 @@ class GanGen2ProtocolDriver {
|
|
|
190
191
|
if (elapsed == 0) { // In case of 16-bit cube timestamp register overflow
|
|
191
192
|
elapsed = timestamp - this.lastMoveTimestamp;
|
|
192
193
|
}
|
|
194
|
+
// After more than the 16-bit register holds (~65 s idle), the interval
|
|
195
|
+
// before the first of these moves rolls over: the local one is right.
|
|
196
|
+
// (cubecore: GAN i3 times ran a minute or more off after the cube lay idle)
|
|
197
|
+
if (i == diff - 1 && this.hadMove) {
|
|
198
|
+
const local = timestamp - this.lastMoveTimestamp;
|
|
199
|
+
if (local > 0xFFFF && elapsed < local)
|
|
200
|
+
elapsed = local;
|
|
201
|
+
}
|
|
193
202
|
this.cubeTimestamp += elapsed;
|
|
194
203
|
cubeEvents.push({
|
|
195
204
|
type: "MOVE",
|
|
@@ -203,6 +212,7 @@ class GanGen2ProtocolDriver {
|
|
|
203
212
|
});
|
|
204
213
|
}
|
|
205
214
|
this.lastMoveTimestamp = timestamp;
|
|
215
|
+
this.hadMove = true;
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.13",
|
|
7
7
|
"description": "A 3×3×3 cube library for the web, built around smart (Bluetooth) cubes: state, notation, methods, solvers and scrambles, a three.js renderer with skins, pictures, web components, React bindings.",
|
|
8
8
|
"license": "MPL-2.0",
|
|
9
9
|
"author": "Tomasz Wodzikowski <wodziszczakow@gmail.com>",
|