@@ -380,3 +380,122 @@ TEST_F (LfpDisplayNodeTests, DataIntegrityTest)
380380
381381 processor->stopAcquisition ();
382382}
383+
384+ class LfpDisplayNodeLowRateTests : public LfpDisplayNodeTests
385+ {
386+ protected:
387+ void SetUp () override
388+ {
389+ sampleRate = 10 .0f ; // Override before base SetUp reads it
390+ LfpDisplayNodeTests::SetUp ();
391+ }
392+ };
393+
394+ // Checks if low sampling rate signals receive a sufficient buffer legnth
395+ TEST_F (LfpDisplayNodeLowRateTests, LowRateDisplayBufferHasMinimumSize)
396+ {
397+ Array<LfpViewer::DisplayBuffer*> displayBuffers = processor->getDisplayBuffers ();
398+ ASSERT_GT (displayBuffers.size (), 0 );
399+ EXPECT_GE (displayBuffers[0 ]->getNumSamples (), 1000 );
400+ }
401+
402+ // Checks that low sampling rate signals with numSamples / pixels < 1.0 still display.
403+ TEST_F (LfpDisplayNodeLowRateTests, LowRateSignalIsNotFlatLine)
404+ {
405+ const int canvasX = 600 ;
406+ const int canvasY = 800 ;
407+ const int numSamples = 20 ;
408+
409+ std::unique_ptr<LfpViewer::LfpDisplayCanvas> canvas =
410+ std::make_unique<LfpViewer::LfpDisplayCanvas> (processor, LfpViewer::SplitLayouts::SINGLE , false );
411+ canvas->updateSettings ();
412+ canvas->setSize (canvasX, canvasY);
413+ canvas->resized ();
414+ canvas->setVisible (true );
415+ setExpectedImageParameters (canvas.get ());
416+
417+ processor->startAcquisition ();
418+ canvas->beginAnimation ();
419+
420+ auto inputBuffer = createBufferSinusoidal (1 , numChannels, numSamples, 100 );
421+ writeBlock (inputBuffer);
422+ canvas->refreshState ();
423+
424+ Rectangle<int > canvasSnapshot (x, y, width, height);
425+ Image canvasImage = canvas->createComponentSnapshot (canvasSnapshot);
426+
427+ const int firstChannelHeight = height / numChannels;
428+ const int midlineRow = firstChannelHeight / 2 ;
429+ const int midlineBand = 4 ; // tolerance for midline so zero-buffer flat line cannot pass
430+
431+ bool hasOffMidlineSignal = false ;
432+ for (int px = 0 ; px < width && !hasOffMidlineSignal; px++)
433+ {
434+ for (int py = 0 ; py < firstChannelHeight && !hasOffMidlineSignal; py++)
435+ {
436+ if (std::abs (py - midlineRow) <= midlineBand)
437+ continue ;
438+ if (canvasImage.getPixelAt (px, py) == channelColours[0 ])
439+ hasOffMidlineSignal = true ;
440+ }
441+ }
442+
443+ EXPECT_TRUE (hasOffMidlineSignal)
444+ << " No off-midline signal found: canvas likely rendered a zero-valued flat line" ;
445+
446+ processor->stopAcquisition ();
447+ }
448+
449+ // A connected trace produces signal in almost every pixel column; isolated dots produce
450+ // signal in only ~numSamples columns. The midline band is excluded so a zero-buffer
451+ // flat line scores zero columns.
452+ TEST_F (LfpDisplayNodeLowRateTests, LowRateTraceIsConnected)
453+ {
454+ const int canvasX = 600 ;
455+ const int canvasY = 800 ;
456+ const int numSamples = 20 ;
457+
458+ std::unique_ptr<LfpViewer::LfpDisplayCanvas> canvas =
459+ std::make_unique<LfpViewer::LfpDisplayCanvas> (processor, LfpViewer::SplitLayouts::SINGLE , false );
460+ canvas->updateSettings ();
461+ canvas->setSize (canvasX, canvasY);
462+ canvas->resized ();
463+ canvas->setVisible (true );
464+ setExpectedImageParameters (canvas.get ());
465+
466+ processor->startAcquisition ();
467+ canvas->beginAnimation ();
468+
469+ auto inputBuffer = createBufferSinusoidal (1 , numChannels, numSamples, 100 );
470+ writeBlock (inputBuffer);
471+ canvas->refreshState ();
472+
473+ Rectangle<int > canvasSnapshot (x, y, width, height);
474+ Image canvasImage = canvas->createComponentSnapshot (canvasSnapshot);
475+
476+ const int firstChannelHeight = height / numChannels;
477+ const int midlineRow = firstChannelHeight / 2 ;
478+ const int midlineBand = 4 ; // tolerance for midline so zero-buffer flat line cannot pass
479+
480+ int signalColumns = 0 ;
481+ for (int px = 0 ; px < width; px++)
482+ {
483+ for (int py = 0 ; py < firstChannelHeight; py++)
484+ {
485+ if (std::abs (py - midlineRow) <= midlineBand)
486+ continue ;
487+ if (canvasImage.getPixelAt (px, py) == channelColours[0 ])
488+ {
489+ signalColumns++;
490+ break ;
491+ }
492+ }
493+ }
494+
495+ // With connected rendering almost every column has off-midline signal.
496+ EXPECT_GT (signalColumns, numSamples * 5 )
497+ << " Trace appears disjointed: " << signalColumns
498+ << " off-midline signal columns, expected > " << numSamples * 5 ;
499+
500+ processor->stopAcquisition ();
501+ }
0 commit comments