diff --git a/src/pymodaq_plugins_utils/hardware/camera_base.py b/src/pymodaq_plugins_utils/hardware/camera_base.py index 0a0981d..0cd2610 100644 --- a/src/pymodaq_plugins_utils/hardware/camera_base.py +++ b/src/pymodaq_plugins_utils/hardware/camera_base.py @@ -107,17 +107,17 @@ def wait_for_acquisition(self, mode: Grab): return continue - # in case hardware has a random buffer size (ex: Andor CCD cameras) - if new_frames.shape[0] != mode.nframes: - new_frames = np.expand_dims(new_frames[-mode.nframes:], axis=0) + # the camera may have kept more frames than the ones still missing (ex: nobody read while it + # was acquiring, or Andor CCD cameras with a random buffer size): keep the newest ones + n_missing = mode.nframes - ind_frames + new_frames = new_frames[-n_missing:] + n_new = new_frames.shape[0] if ind_average == 0 and ind_frames == 0: - shape = list(new_frames.shape[1:]) - shape = [mode.n_average, mode.nframes] + shape + shape = [mode.n_average, mode.nframes] + list(new_frames.shape[1:]) frames = np.zeros(shape, dtype=new_frames.dtype) - nacq = rng[1] - rng[0] - frames[ind_average, ind_frames:nacq, ...] = new_frames - ind_frames += nacq + frames[ind_average, ind_frames:ind_frames + n_new, ...] = new_frames + ind_frames += n_new ind_average += 1 self.data_sig.emit(frames) QtCore.QThread.msleep(10) diff --git a/tests/test_camera_base.py b/tests/test_camera_base.py index eef18e3..92f117f 100644 --- a/tests/test_camera_base.py +++ b/tests/test_camera_base.py @@ -93,6 +93,15 @@ def get_pixel_size(self): return 5.0, 10.0 +class FakeCameraSeveralFrames(FakeCamera): + """A camera that kept several frames since the last read (as when nobody reads while it acquires)""" + n_unread = 3 + + def read_multiple_images(self, rng=None, peek=False, missing_frame='skip', return_rng=False): + frames = np.repeat(self.read_newest_image()[None, ...], self.n_unread, axis=0) + return (frames, (0, self.n_unread)) if return_rng else frames + + class DAQ_2DViewer_Fake(CameraBase): driver_class = FakeCamera @@ -100,6 +109,10 @@ def ini_detector_custom(self, controller=None): self.controller = self.driver_class() if self.is_master else controller +class DAQ_2DViewer_FakeSeveralFrames(DAQ_2DViewer_Fake): + driver_class = FakeCameraSeveralFrames + + class DAQ_2DViewer_FakeWithPixelSize(DAQ_2DViewer_Fake): driver_class = FakeCameraWithPixelSize @@ -143,6 +156,17 @@ def set_binning(plugin, xbin: int, ybin: int): plugin.commit_settings(param) +class TestSeveralUnreadFrames: + def test_a_snap_gives_one_image_when_the_camera_kept_several_frames(self, qapp): + plugin = make_detector(DAQ_2DViewer_FakeSeveralFrames) + try: + dte = grab_and_wait(plugin) + assert dte[0].dim.name == 'Data2D' + assert dte[0].shape == (HEIGHT, WIDTH) + finally: + plugin.close() + + class TestDriverContract: @pytest.mark.parametrize('method, args', [ ('get_detector_size', ()), ('get_data_dimensions', ()), ('get_roi', ()), ('set_roi', ()),