From d53eee18f10bc52fa85e585d9b33da1e18ba0674 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Thu, 7 Aug 2025 13:45:00 -0400 Subject: [PATCH] Use hub clock instead of frame clock for hub 0 and passthrough devices - Add note describing the current behavior and the plans in ONI 2.0 to change the behavior --- Source/Devices/AnalogIO.cpp | 7 ++++++- Source/Devices/DigitalIO.cpp | 9 +++++++-- Source/Devices/HarpSyncInput.cpp | 9 +++++++-- Source/Devices/MemoryMonitor.cpp | 6 +++++- Source/Devices/Neuropixels1e.cpp | 11 ++++++++--- Source/Devices/Neuropixels2e.cpp | 8 +++++++- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Source/Devices/AnalogIO.cpp b/Source/Devices/AnalogIO.cpp index 5c0cc0d..11ead98 100644 --- a/Source/Devices/AnalogIO.cpp +++ b/Source/Devices/AnalogIO.cpp @@ -234,7 +234,12 @@ void AnalogIO::processFrame (uint64_t eventWord) currentAverageFrame = 0; - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; + + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (*hubClock); sampleNumbers[currentFrame] = sampleNumber++; eventCodes[currentFrame] = eventWord; diff --git a/Source/Devices/DigitalIO.cpp b/Source/Devices/DigitalIO.cpp index acd6de9..2cbf99d 100644 --- a/Source/Devices/DigitalIO.cpp +++ b/Source/Devices/DigitalIO.cpp @@ -133,14 +133,19 @@ void DigitalIO::processFrames() { size_t offset = 0; - uint16_t* dataPtr = (uint16_t*) frame->data; + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (*hubClock); sampleNumbers[currentFrame] = sampleNumber++; constexpr int inputDataOffset = 4; constexpr int buttonDataOffset = inputDataOffset + 1; + uint16_t* dataPtr = (uint16_t*) frame->data; + uint64_t inputState = *(dataPtr + inputDataOffset); uint64_t buttonState = *(dataPtr + buttonDataOffset); diff --git a/Source/Devices/HarpSyncInput.cpp b/Source/Devices/HarpSyncInput.cpp index 9e24cc8..81b7b81 100644 --- a/Source/Devices/HarpSyncInput.cpp +++ b/Source/Devices/HarpSyncInput.cpp @@ -82,9 +82,14 @@ void HarpSyncInput::processFrames() oni_frame_t* frame; while (frameQueue.try_dequeue (frame)) { - uint32_t* dataPtr = (uint32_t*) frame->data; + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; + + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (*hubClock); - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds (frame->time); + uint32_t* dataPtr = (uint32_t*) frame->data; harpTimeSamples[currentFrame] = *(dataPtr + 2) + 1; diff --git a/Source/Devices/MemoryMonitor.cpp b/Source/Devices/MemoryMonitor.cpp index 8843852..6ecc52f 100644 --- a/Source/Devices/MemoryMonitor.cpp +++ b/Source/Devices/MemoryMonitor.cpp @@ -152,8 +152,12 @@ void MemoryMonitor::processFrames() while (frameQueue.try_dequeue (frame)) { + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; + auto t = deviceContext->convertTimestampToSeconds (*hubClock); uint32_t* dataPtr = (uint32_t*) frame->data; - auto t = deviceContext->convertTimestampToSeconds (frame->time); auto p = 100.0f * float (*(dataPtr + 2)) / totalMemory; lastPercentUsedValue = p; oni_destroy_frame (frame); diff --git a/Source/Devices/Neuropixels1e.cpp b/Source/Devices/Neuropixels1e.cpp index ae9a995..f78653a 100644 --- a/Source/Devices/Neuropixels1e.cpp +++ b/Source/Devices/Neuropixels1e.cpp @@ -287,12 +287,17 @@ void Neuropixels1e::processFrames() oni_frame_t* frame; while (frameQueue.try_dequeue (frame)) { - uint16_t* dataPtr = (uint16_t*) frame->data; - dataPtr += dataOffset; + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; - apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds (frame->time); + apTimestamps[superFrameCount] = deviceContext->convertTimestampToSeconds (*hubClock); apSampleNumbers[superFrameCount] = apSampleNumber++; + uint16_t* dataPtr = (uint16_t*) frame->data; + dataPtr += dataOffset; + for (size_t i = 0; i < framesPerSuperFrame; i++) { if (i == 0) // LFP data diff --git a/Source/Devices/Neuropixels2e.cpp b/Source/Devices/Neuropixels2e.cpp index fbce27b..0867cc0 100644 --- a/Source/Devices/Neuropixels2e.cpp +++ b/Source/Devices/Neuropixels2e.cpp @@ -535,7 +535,13 @@ void Neuropixels2e::processFrames() uint16_t* amplifierData = dataPtr + 9; sampleNumbers[probeIndex][frameCount[probeIndex]] = sampleNumber[probeIndex]++; - timestamps[probeIndex][frameCount[probeIndex]] = deviceContext->convertTimestampToSeconds (frame->time); + + // NB: In ONI v1.0 frame clock is when the frame is created, not necessarily when the data is received. + // For local and passthrough devices, we will instead use the hub clock for the timestamp; in + // ONI v2.0 this behavior may change, and frame->time can be used instead for consistency across devices. + auto hubClock = (uint64_t*) frame->data; + + timestamps[probeIndex][frameCount[probeIndex]] = deviceContext->convertTimestampToSeconds (*hubClock); for (int i = 0; i < FramesPerSuperFrame; i++) {