Skip to content

Commit

Permalink
Adds feat to detect outlier counts using countdown-based anomaly dete…
Browse files Browse the repository at this point in the history
…ction.
  • Loading branch information
mntone committed Jun 20, 2024
1 parent ff3aed0 commit 108d33b
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 17 deletions.
50 changes: 50 additions & 0 deletions src/modules/telemetry/utils/CounterAnomalyDetector.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
52 changes: 35 additions & 17 deletions src/modules/telemetry/utils/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -11,8 +12,9 @@ export class TelemetryProcessor {
// New context flag
#newContext: boolean = true

// Quota mode counter
// Data processing instances
readonly #quotaCounter: FrequencyCounter<number> = new FrequencyCounter()
readonly #countAnomalyDetector: CounterAnomalyDetector = new CounterAnomalyDetector()

// Base count; use this value as base of this.#waveData.startTimestamp
#baseCount: number = 110
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -94,6 +97,7 @@ export class TelemetryProcessor {
return // DISPOSE!!
}

this.#countAnomalyDetector.reset()
this.#quotaCounter.reset(ev.quota)
this.#baseCount = ev.count
this.#currentWave = currentWave
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 108d33b

Please sign in to comment.