urish / muse-js

Muse 2016 EEG Headset JavaScript Library (using Web Bluetooth)
https://medium.com/@urish/reactive-brain-waves-af07864bb7d4
MIT License
280 stars 78 forks source link

Muse S2: timestamp jump #70

Open nuKs opened 9 months ago

nuKs commented 9 months ago

These lines of code seems no longer relevant with Muse 2S (I don't know where the limit lies but it is higher, if the mechanism is still the same). https://github.com/urish/muse-js/blob/4e864578c55dd7e26d85b429863f47ccabac54a0/src/muse.ts#L233C1-L256C1

As you can see in the screenshot, the index continues to increase in a regular manner. The timestamp drastically jump though.

Screenshot 2024-01-22 at 3 55 53 PM
nuKs commented 9 months ago

Limit still triggers when completely disabled.

What's weird is that the index jump seems fine, only the timestamp jump seems to have issue, and thus that bug should likely replicate across all muse device. Not sure what's going out here.

I am testing this as a fix, hopefully will get more insight.

this.firstIndex = [null, null, null, null];
this.firstTimestamp = [null, null, null, null];
this.lastIndex = [null, null, null, null];
this.indexTranslation = [0, 0, 0, 0];

(this.client as any).getTimestamp = (eventIndex: number, samplesPerReading: number, frequency: number) => {
    // We disable timestamp reading, as it seems not reliable. We will
    // handle it ourselves, per electrode.
    return null;
}
this.eegReadings = this.client.eegReadings.pipe(
    map(data => {
        const electrode = data.electrode;
        const index = data.index;

        // Handle first index timestamp initialization.
        if (this.firstIndex[electrode] === null || this.firstTimestamp[electrode] === null) {
            console.info('First index: ', index, index.toString(16), index.toString(2));
            this.firstIndex[electrode] = index;
            this.firstTimestamp[electrode] = new Date().getTime() - READING_DELTA;
            console.info('First timestamp: ', this.firstTimestamp[electrode]);
            console.info('First timestamp diff: ', data.timestamp - this.firstTimestamp[electrode]!);

            // Adapt data timestamp.
            data.timestamp = this.firstTimestamp[electrode]!;

            // Record last index.
            this.lastIndex[electrode] = index;
        }
        // Handle in order index.
        else if (index - this.lastIndex[electrode]! > 0) {
            // Adapt data index & timestamp.
            const translatedIndex = this.indexTranslation[electrode] + index;
            data.timestamp = this.firstTimestamp[electrode]! + READING_DELTA * (translatedIndex - this.firstIndex[electrode]!);
            data.index = translatedIndex;

            // Record last index.
            this.lastIndex[electrode] = index;
        }
        // Handle misordered index: we simply do nothing different if differences of index is below 4096 (* 12 samples) ~= 30s.
        else if (index - this.lastIndex[electrode]! <= 0 && index - this.lastIndex[electrode]! > -0x1000) {
            // Log error.
            console.error('found misordered index for electrode < 4096 (4096*12 samples at 256hz ~= 30s) ', electrode, ': ', index, index.toString(16), index.toString(2), ' previous was: ', this.lastIndex[electrode]!, this.lastIndex[electrode]!.toString(16), this.lastIndex[electrode]!.toString(2));

            // Adapt data index & timestamp.
            const translatedIndex = this.indexTranslation[electrode] + index;
            data.timestamp = this.firstTimestamp[electrode]! + READING_DELTA * (translatedIndex - this.firstIndex[electrode]!);
            data.index = translatedIndex;

            // Record last index.
            this.lastIndex[electrode] = index;
        }
        // Handle misordered index: Otherwise, we assume that the index has been reset, if there was an error or a delay,
        // it should have been sent/corrected by now so it must be a rotation.
        else {
            // Log error.
            console.error('misordered index for electrode >= 4096 (expecting index === 0 due to full number rotation)', electrode, ': ', index, index.toString(16), index.toString(2), ' previous was: ', this.lastIndex[electrode]!, this.lastIndex[electrode]!.toString(16), this.lastIndex[electrode]!.toString(2));

            // Increase index translation, due to rotation.
            this.indexTranslation[electrode] += this.lastIndex[electrode]!;

            // Adapt data index & timestamp.
            const translatedIndex = this.indexTranslation[electrode] + index;
            data.timestamp = this.firstTimestamp[electrode]! + READING_DELTA * (translatedIndex - this.firstIndex[electrode]!);
            data.index = translatedIndex;

            // Record last index.
            this.lastIndex[electrode] = index;
        }

        return data;
    }),
    publish()
) as ConnectableObservable<EEGReading>;