diff --git a/src/modules/telemetry/utils/CounterAnomalyDetector.ts b/src/modules/telemetry/utils/CounterAnomalyDetector.ts new file mode 100644 index 0000000..65c81fe --- /dev/null +++ b/src/modules/telemetry/utils/CounterAnomalyDetector.ts @@ -0,0 +1,50 @@ +enum CounterAnomalyDetectorState { + IdleStart = 1, + IdleEnd = 2, + CountDown = 3, +} + +export class CounterAnomalyDetector { + #state: CounterAnomalyDetectorState = CounterAnomalyDetectorState.IdleStart + #prevValue = 100 + #prevTimestamp = 0 + + reset(): void { + this.#state = CounterAnomalyDetectorState.IdleStart + this.#prevValue = 100 + this.#prevTimestamp = 0 + } + + isAnomalous(value: number, timestamp: number): boolean { + switch (this.#state) { + case CounterAnomalyDetectorState.CountDown: { + const diffValue = this.#prevValue - value + const diffTimestamp = Math.ceil(0.25 + timestamp - this.#prevTimestamp) + if (diffValue < 0 || diffValue > diffTimestamp) { + return true + } + + if (value <= 0 || value >= 1000) { + this.#state = CounterAnomalyDetectorState.IdleEnd + } + break + } + case CounterAnomalyDetectorState.IdleStart: + if (value < 100) { + this.#state = CounterAnomalyDetectorState.CountDown + } + break + case CounterAnomalyDetectorState.IdleEnd: + if (value !== 0) { + return true + } + break + } + + // Update values + this.#prevValue = value + this.#prevTimestamp = timestamp + + return false + } +} diff --git a/src/modules/telemetry/utils/processor.ts b/src/modules/telemetry/utils/processor.ts index 488016a..55bc9b9 100644 --- a/src/modules/telemetry/utils/processor.ts +++ b/src/modules/telemetry/utils/processor.ts @@ -3,6 +3,7 @@ import { DefaultWaveType } from '@/core/utils/wave' import { ShakeDefaultWave, ShakeExtraWave, ShakeTelemetry, ShakeUpdate } from '../models/data' import { ShakeEvent, ShakeGameUpdateEvent } from '../models/telemetry' +import { CounterAnomalyDetector } from './CounterAnomalyDetector' import { FrequencyCounter } from './frequencyCounter' export class TelemetryProcessor { @@ -11,8 +12,9 @@ export class TelemetryProcessor { // New context flag #newContext: boolean = true - // Quota mode counter + // Data processing instances readonly #quotaCounter: FrequencyCounter = new FrequencyCounter() + readonly #countAnomalyDetector: CounterAnomalyDetector = new CounterAnomalyDetector() // Base count; use this value as base of this.#waveData.startTimestamp #baseCount: number = 110 @@ -45,6 +47,7 @@ export class TelemetryProcessor { // Reset initial state this.#newContext = true + this.#countAnomalyDetector.reset() this.#quotaCounter.reset() this.#baseCount = 110 this.#currentWave = 0 @@ -94,6 +97,7 @@ export class TelemetryProcessor { return // DISPOSE!! } + this.#countAnomalyDetector.reset() this.#quotaCounter.reset(ev.quota) this.#baseCount = ev.count this.#currentWave = currentWave @@ -120,23 +124,37 @@ export class TelemetryProcessor { // Update wave const currentWaveData = this.#waveData - switch (ev.count) { - case 0: - if (currentWaveData.endTimestamp === undefined) { - this.#baseCount = 0 - currentWaveData.startTimestamp = ev.timestamp - 100 - currentWaveData.endTimestamp = ev.timestamp - } - break - case undefined: - break - default: - if (this.#baseCount >= 100) { - this.#baseCount = ev.count - currentWaveData.startTimestamp = ev.timestamp - (100 - ev.count) + + let count = ev.count + if (count === undefined) { + // Calc count + count = Math.floor(100 + currentWaveData.startTimestamp - ev.timestamp) + } else { + const isAnomalous = this.#countAnomalyDetector.isAnomalous(count, ev.timestamp) + if (isAnomalous) { + // Correct count + count = Math.floor(100 + currentWaveData.startTimestamp - ev.timestamp) + } else { + // Update timestamp + switch (count) { + case 0: + if (currentWaveData.endTimestamp === undefined) { + this.#baseCount = 0 + currentWaveData.startTimestamp = ev.timestamp - 100 + currentWaveData.endTimestamp = ev.timestamp + } + break + default: + if (this.#baseCount >= 100) { + this.#baseCount = count + currentWaveData.startTimestamp = ev.timestamp - (100 - count) + } + break + } } - break } + + // Update quota currentWaveData.quota = this.#quotaCounter.add(ev.quota).mode // Check diff >= 0 @@ -167,7 +185,7 @@ export class TelemetryProcessor { // Create new update data const newUpdateData: ShakeUpdate = { timestamp: ev.timestamp, - count: ev.count ?? Math.floor(currentWaveData.startTimestamp - ev.timestamp + 100), + count, amount: ev.amount, unstable: ev.unstable, } as const