108 lines
3 KiB
TypeScript
108 lines
3 KiB
TypeScript
export class Ntp {
|
|
|
|
__currentOffset: bigint | undefined;
|
|
__currentFluctuation: bigint | undefined;
|
|
// between 0 and 100
|
|
__currentAcceptanceRate: bigint | undefined;
|
|
|
|
__acceptedFluctuationFactor: bigint = 4n;
|
|
|
|
__latestOffsets: bigint[] = [];
|
|
__latestFluctuations: bigint[] = [];
|
|
__latestAcceptanceResults: bigint[] = [];
|
|
|
|
constructor() {
|
|
this.__currentOffset = undefined;
|
|
this.__currentFluctuation = undefined;
|
|
this.__latestOffsets = [];
|
|
this.__latestFluctuations = [];
|
|
this.__latestAcceptanceResults = [];
|
|
}
|
|
|
|
currentOffset() {
|
|
return this.__currentOffset;
|
|
}
|
|
|
|
currentAcceptanceRate() {
|
|
return this.__currentAcceptanceRate;
|
|
}
|
|
|
|
handleTimeSync(sentAt: bigint, receivedAt: bigint, time: bigint) {
|
|
let roundTripTime = receivedAt - sentAt;
|
|
let newOffset = time + roundTripTime / 2n - receivedAt;
|
|
this.__handleNewOffset(newOffset);
|
|
}
|
|
|
|
__handleNewOffset(newOffset: bigint) {
|
|
const offsetInMargin = this.__isOffsetInMargin(newOffset);
|
|
|
|
this.__handleAcceptanceResult(offsetInMargin);
|
|
|
|
if (!offsetInMargin) {
|
|
return;
|
|
}
|
|
|
|
this.__currentOffset = this.__pushValueAndCaluclateAverage(this.__latestOffsets, newOffset);
|
|
}
|
|
|
|
__handleAcceptanceResult(result: boolean) {
|
|
this.__currentAcceptanceRate = this.__pushValueAndCaluclateAverage(
|
|
this.__latestAcceptanceResults,
|
|
result ? 100n : 0n
|
|
);
|
|
|
|
if (this.__currentAcceptanceRate > 90n) {
|
|
this.__acceptedFluctuationFactor = 4n;
|
|
}
|
|
else if (this.__currentAcceptanceRate > 80n) {
|
|
this.__acceptedFluctuationFactor = 6n;
|
|
}
|
|
else if (this.__currentAcceptanceRate > 50n) {
|
|
this.__acceptedFluctuationFactor = 10n;
|
|
} else {
|
|
this.__acceptedFluctuationFactor = 100n;
|
|
}
|
|
|
|
}
|
|
|
|
__isOffsetInMargin(newOffset: bigint) {
|
|
if (!this.__currentOffset) {
|
|
return true;
|
|
}
|
|
|
|
let fluctuation = this.__currentOffset - newOffset;
|
|
if (fluctuation < 0) {
|
|
fluctuation = -fluctuation;
|
|
}
|
|
|
|
if (
|
|
this.__currentFluctuation &&
|
|
this.__latestFluctuations.length > 5 &&
|
|
fluctuation > this.__currentFluctuation * this.__acceptedFluctuationFactor
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
this.__currentFluctuation = this.__pushValueAndCaluclateAverage(this.__latestFluctuations, fluctuation);
|
|
|
|
if (this.__latestFluctuations.length < 10) {
|
|
return true;
|
|
}
|
|
|
|
return fluctuation < this.__currentFluctuation * 2n;
|
|
};
|
|
|
|
__pushValueAndCaluclateAverage(values: bigint[], newValue: bigint): bigint {
|
|
values.push(newValue);
|
|
if (values.length > 10) {
|
|
values.shift();
|
|
}
|
|
|
|
let sum = 0n;
|
|
for (let i = 0; i < values.length; i++) {
|
|
sum += values[i];
|
|
}
|
|
|
|
return sum / BigInt(values.length);
|
|
};
|
|
} |