From cfaae2d6994ba12c9f46abb2b2cf091e6902c68e Mon Sep 17 00:00:00 2001 From: Chris Meyer <34664+cmeyer@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:39:00 -0800 Subject: [PATCH 1/4] Reduce timeouts and sleeps where possible to optimize tests. --- nion/device_kit/CameraDevice.py | 8 ++++---- nion/instrumentation/Acquisition.py | 4 ++-- .../test/AcquisitionTestContextConfiguration.py | 6 +++--- nion/instrumentation/test/CameraControl_test.py | 6 +++--- nion/instrumentation/test/HardwareSource_test.py | 12 ++++++------ .../test/SynchronizedAcquisition_test.py | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/nion/device_kit/CameraDevice.py b/nion/device_kit/CameraDevice.py index bf5faf8..34de472 100755 --- a/nion/device_kit/CameraDevice.py +++ b/nion/device_kit/CameraDevice.py @@ -433,7 +433,7 @@ def grab_partial(self, *, update_period: float = 1.0) -> typing.Tuple[bool, bool class CameraSettings(camera_base.CameraSettings): - def __init__(self, camera_id: str): + def __init__(self, camera_id: str, base_exposure: float = 0.1): # these events must be defined self.current_frame_parameters_changed_event = Event.Event() self.record_frame_parameters_changed_event = Event.Event() @@ -456,9 +456,9 @@ def __init__(self, camera_id: str): # configure profiles self.__settings = [ - camera_base.CameraFrameParameters({"exposure_ms": 100, "binning": 2}), - camera_base.CameraFrameParameters({"exposure_ms": 200, "binning": 2}), - camera_base.CameraFrameParameters({"exposure_ms": 500, "binning": 1}), + camera_base.CameraFrameParameters({"exposure_ms": 1 * 1000 * base_exposure, "binning": 2}), + camera_base.CameraFrameParameters({"exposure_ms": 2 * 1000 * base_exposure, "binning": 2}), + camera_base.CameraFrameParameters({"exposure_ms": 5 * 1000 * base_exposure, "binning": 1}), ] self.__current_settings_index = 0 diff --git a/nion/instrumentation/Acquisition.py b/nion/instrumentation/Acquisition.py index 4ecdd58..e14ed28 100644 --- a/nion/instrumentation/Acquisition.py +++ b/nion/instrumentation/Acquisition.py @@ -3036,7 +3036,7 @@ def acquire(data_stream: DataStream, *, error_handler: typing.Optional[typing.Ca last_progress = next_progress last_progress_time = time.time() assert time.time() - last_progress_time < TIMEOUT - time.sleep(0.05) # play nice with other threads + time.sleep(0.005) # play nice with other threads if data_stream.is_finished: assert data_stream.progress == 1.0 except Exception as e: @@ -3110,7 +3110,7 @@ def wait_acquire(self, timeout: float = 60.0, *, on_periodic: typing.Callable[[] start = time.time() while self.__task and not self.__task.done() and time.time() - start < timeout: on_periodic() - time.sleep(0.05) # don't take all the CPU + time.sleep(0.005) # don't take all the CPU on_periodic() # one more periodic for clean up @property diff --git a/nion/instrumentation/test/AcquisitionTestContextConfiguration.py b/nion/instrumentation/test/AcquisitionTestContextConfiguration.py index 36a1c81..6139bd5 100644 --- a/nion/instrumentation/test/AcquisitionTestContextConfiguration.py +++ b/nion/instrumentation/test/AcquisitionTestContextConfiguration.py @@ -121,7 +121,7 @@ def get_dimensional_calibrations(self, readout_area: typing.Optional[Geometry.In def get_frame_data(self, readout_area: Geometry.IntRect, binning_shape: Geometry.IntSize, exposure_s: float, scan_context: stem_controller.ScanContext, probe_position: typing.Optional[Geometry.FloatPoint]) -> DataAndMetadata.DataAndMetadata: self.__data_value += 1 shape = self.__sensor_dimensions if self.__sensor_dimensions else readout_area.size - data = numpy.random.randn(shape.height // binning_shape.height, shape.width // binning_shape.width) * exposure_s + data = numpy.random.rand(shape.height // binning_shape.height, shape.width // binning_shape.width) * exposure_s return DataAndMetadata.new_data_and_metadata(data) @@ -211,7 +211,7 @@ def __init__(self) -> None: self.eels_camera_device_id = "test_eels_camera" self.instrument = InstrumentDevice.Instrument(self.instrument_id, ValueManager(), AxisManager()) self.scan_module = ScanModule(self.instrument, "test_scan_device", ScanDataGenerator()) - self.ronchigram_camera_settings = CameraDevice.CameraSettings(self.ronchigram_camera_device_id) - self.eels_camera_settings = CameraDevice.CameraSettings(self.eels_camera_device_id) + self.ronchigram_camera_settings = CameraDevice.CameraSettings(self.ronchigram_camera_device_id, 0.005) + self.eels_camera_settings = CameraDevice.CameraSettings(self.eels_camera_device_id, 0.005) self.ronchigram_camera_device = CameraDevice.Camera(self.ronchigram_camera_device_id, "ronchigram", "Ronchigram", CameraSimulator(None), self.instrument) self.eels_camera_device = CameraDevice.Camera(self.eels_camera_device_id, "eels", "EELS", CameraSimulator(Geometry.IntSize(256, 1024)), self.instrument) diff --git a/nion/instrumentation/test/CameraControl_test.py b/nion/instrumentation/test/CameraControl_test.py index 937c3e1..384bcc1 100644 --- a/nion/instrumentation/test/CameraControl_test.py +++ b/nion/instrumentation/test/CameraControl_test.py @@ -144,7 +144,7 @@ def setUp(self): AcquisitionTestContext.begin_leaks() self.app = Application.Application(TestUI.UserInterface(), set_global=False) self.source_image = numpy.random.randn(1024, 1024).astype(numpy.float32) - self.exposure = 0.04 + self.exposure = 0.005 def tearDown(self): AcquisitionTestContext.end_leaks(self) @@ -1142,7 +1142,7 @@ def test_acquisition_panel_sequence_acquisition(self): start_time = time.time() while ac.is_acquiring_model.value: document_controller.periodic() - time.sleep(0.1) + time.sleep(1/200) self.assertTrue(time.time() - start_time < TIMEOUT) self.assertFalse(ac.is_error) # only one data item will be created: the sequence. the view data item does not exist since acquiring @@ -1220,7 +1220,7 @@ def display_data_item(document_controller: DocumentController.DocumentController if progress > last_progress: last_progress = progress last_progress_time = time.time() - time.sleep(0.05) + time.sleep(0.005) self.assertEqual(expected_error, acquisition.is_error) self.assertTrue(acquisition.is_finished) # useful for debugging diff --git a/nion/instrumentation/test/HardwareSource_test.py b/nion/instrumentation/test/HardwareSource_test.py index 4bea067..1ef8e54 100644 --- a/nion/instrumentation/test/HardwareSource_test.py +++ b/nion/instrumentation/test/HardwareSource_test.py @@ -73,7 +73,7 @@ def _acquire_data_elements(self): class SimpleHardwareSource(HardwareSource.ConcreteHardwareSource): - def __init__(self, sleep=0.05): + def __init__(self, sleep=0.005): super().__init__("simple_hardware_source", "SimpleHardwareSource") self.add_data_channel() self.sleep = sleep @@ -119,7 +119,7 @@ def _acquire_data_elements(self): class LinePlotHardwareSource(HardwareSource.ConcreteHardwareSource): - def __init__(self, shape, processed, sleep=0.05): + def __init__(self, shape, processed, sleep=0.005): super().__init__("described_hardware_source", "DescribedHardwareSource") self.add_data_channel() if processed: @@ -137,7 +137,7 @@ def _create_acquisition_record_task(self, **kwargs) -> LinePlotAcquisitionTask: class SummedHardwareSource(HardwareSource.ConcreteHardwareSource): - def __init__(self, sleep=0.05): + def __init__(self, sleep=0.005): super().__init__("summed_hardware_source", "SummedHardwareSource") self.add_data_channel() self.add_channel_processor(0, HardwareSource.SumProcessor()) @@ -242,7 +242,7 @@ def _stop_acquisition(self) -> None: class ScanHardwareSource(HardwareSource.ConcreteHardwareSource): - def __init__(self, sleep=0.02): + def __init__(self, sleep=0.005): super().__init__("scan_hardware_source", "ScanHardwareSource") self.add_data_channel("a", "A") self.add_data_channel("b", "B") @@ -1258,9 +1258,9 @@ def test_data_item_created_during_acquisition_is_write_delayed_during_and_not_af hardware_source = simple_test_context.hardware_source document_controller = simple_test_context.document_controller document_model = simple_test_context.document_model - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: - time.sleep(0.6) + time.sleep(0.02) document_controller.periodic() self.assertEqual(1, len(document_model.data_items)) self.assertTrue(document_model.data_items[0].is_write_delayed) diff --git a/nion/instrumentation/test/SynchronizedAcquisition_test.py b/nion/instrumentation/test/SynchronizedAcquisition_test.py index 8cc8c78..84506e9 100644 --- a/nion/instrumentation/test/SynchronizedAcquisition_test.py +++ b/nion/instrumentation/test/SynchronizedAcquisition_test.py @@ -670,7 +670,7 @@ def do_grab(): while t.is_alive(): document_controller.periodic() import time - time.sleep(0.1) + time.sleep(1/200) t.join() # ensure graphic is still the original one and hasn't flickered with a replacement self.assertEqual(drift_graphic, display_item.graphics[-1]) @@ -717,7 +717,7 @@ def do_grab(): while t.is_alive(): document_controller.periodic() import time - time.sleep(0.1) + time.sleep(1/200) t.join() # ensure graphic is still the original one and hasn't flickered with a replacement self.assertEqual(drift_graphic, display_item.graphics[-1]) From 554f688327e323189992509fe4fbde28ac2a0c64 Mon Sep 17 00:00:00 2001 From: Chris Meyer <34664+cmeyer@users.noreply.github.com> Date: Sun, 29 Dec 2024 22:30:15 -0800 Subject: [PATCH 2/4] Avoid races in start_playing, use improved time accuracy to improve camera test reliability. --- .../test/CameraControl_test.py | 64 ++++++++----------- .../test/HardwareSource_test.py | 7 +- .../instrumentation/test/MultiAcquire_test.py | 6 +- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/nion/instrumentation/test/CameraControl_test.py b/nion/instrumentation/test/CameraControl_test.py index 384bcc1..3c177b3 100644 --- a/nion/instrumentation/test/CameraControl_test.py +++ b/nion/instrumentation/test/CameraControl_test.py @@ -283,7 +283,7 @@ def test_change_to_profile_with_different_size_during_acquisition_should_produce frame_parameters_1.binning = 1 hardware_source.set_frame_parameters(1, frame_parameters_1) hardware_source.set_selected_profile_index(0) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: self.assertEqual(hardware_source.get_next_xdatas_to_start()[0].data.shape, hardware_source.get_expected_dimensions(2)) time.sleep(self.exposure * 1.1) @@ -306,7 +306,7 @@ def test_changing_frame_parameters_during_view_does_not_affect_current_acquisiti frame_parameters = hardware_source.get_frame_parameters(profile_index) frame_parameters.binning = 4 frame_time = frame_parameters.exposure_ms / 1000.0 - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: # the time taken to start playing is unpredictable, # so first make sure the camera is playing. @@ -336,7 +336,7 @@ def test_capturing_during_view_captures_new_data_items(self): document_model = test_context.document_model hardware_source = test_context.camera_hardware_source state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -354,7 +354,7 @@ def test_capturing_during_view_captures_eels_2d(self): document_model = test_context.document_model hardware_source = test_context.camera_hardware_source state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -374,7 +374,7 @@ def test_capturing_during_view_captures_eels_1d(self): document_model = test_context.document_model hardware_source = test_context.camera_hardware_source state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -395,7 +395,7 @@ def test_capturing_during_view_captures_session(self): hardware_source = test_context.camera_hardware_source state_controller = self.__create_state_controller(test_context) ApplicationData.get_session_metadata_model().microscopist = "Ned Flanders" - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -416,7 +416,7 @@ def test_ability_to_start_playing_with_custom_parameters(self): frame_parameters_0 = hardware_source.get_frame_parameters(0) frame_parameters_0.binning = 4 hardware_source.set_current_frame_parameters(frame_parameters_0) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(10) finally: @@ -483,7 +483,7 @@ def test_first_view_uses_correct_mode(self): state_controller = self.__create_state_controller(test_context) state_controller.handle_change_profile("Snap") state_controller.handle_binning_changed("4") - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: time.sleep(self.exposure * 0.5) hardware_source.get_next_xdatas_to_finish() # view again @@ -505,11 +505,12 @@ def test_first_view_uses_correct_exposure(self): state_controller.handle_change_profile("Snap") state_controller.handle_binning_changed("4") state_controller.handle_exposure_changed(long_exposure) - start = time.time() - hardware_source.start_playing() + start = time.perf_counter() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish() # view again - elapsed = time.time() - start + time.sleep(0.001) # try to avoid Windows timer low precision issues + elapsed = time.perf_counter() - start document_controller.periodic() self.assertEqual(document_model.data_items[0].data_shape, hardware_source.get_expected_dimensions(4)) self.assertTrue(elapsed > long_exposure) @@ -525,23 +526,19 @@ def test_view_followed_by_frame_uses_correct_exposure(self): state_controller.handle_binning_changed("4") state_controller.handle_exposure_changed(long_exposure) state_controller.handle_change_profile("Run") - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: time.sleep(self.exposure * 0.5) data_and_metadata = hardware_source.get_next_xdatas_to_finish()[0] # view again self.assertEqual(data_and_metadata.data_shape, hardware_source.get_expected_dimensions(2)) finally: - hardware_source.stop_playing() - start_time = time.time() - while hardware_source.is_playing: - time.sleep(self.exposure) - self.assertTrue(time.time() - start_time < TIMEOUT) + hardware_source.stop_playing(sync_timeout=3.0) state_controller.handle_change_profile("Snap") - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: - start = time.time() + start = time.perf_counter() data_and_metadata = hardware_source.get_next_xdatas_to_finish()[0] # frame now - elapsed = time.time() - start + elapsed = time.perf_counter() - start finally: hardware_source.abort_playing() self.assertEqual(data_and_metadata.data_shape, hardware_source.get_expected_dimensions(4)) @@ -564,7 +561,7 @@ def raise_exception(): raise Exception("Error during acquisition") hardware_source._test_acquire_hook = raise_exception hardware_source._test_acquire_exception = lambda *args: None - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish() document_controller.periodic() @@ -613,7 +610,7 @@ def processed_data_item_changed(): data_items[1] = state_controller.processed_data_item_reference.data_item data_item_reference_changed_listener = state_controller.data_item_reference.data_item_reference_changed_event.listen(display_data_item_changed) processed_data_item_reference_changed_listener = state_controller.processed_data_item_reference.data_item_reference_changed_event.listen(processed_data_item_changed) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: for _ in range(4): hardware_source.get_next_xdatas_to_finish() @@ -640,23 +637,18 @@ def processed_data_item_changed(): data_item_reference_changed_listener = state_controller.data_item_reference.data_item_reference_changed_event.listen(display_data_item_changed) processed_data_item_reference_changed_listener = state_controller.processed_data_item_reference.data_item_reference_changed_event.listen(processed_data_item_changed) # first acquisition - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: for _ in range(4): hardware_source.get_next_xdatas_to_finish() document_controller.periodic() finally: - hardware_source.abort_playing() + hardware_source.abort_playing(sync_timeout=3.0) document_model.recompute_all() - # make sure really stopped - start_time = time.time() - while hardware_source.is_playing: - time.sleep(self.exposure) - self.assertTrue(time.time() - start_time < TIMEOUT) self.assertEqual(len(document_model.data_items), 2) # second acquisition first_data_items = copy.copy(data_items) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: for _ in range(4): hardware_source.get_next_xdatas_to_finish() @@ -767,7 +759,7 @@ def test_consecutive_frames_have_unique_data(self): self.source_image = numpy.random.randn(1024, 1024).astype(numpy.float32) with self._test_context() as test_context: hardware_source = test_context.camera_hardware_source - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: data = hardware_source.get_next_xdatas_to_finish()[0].data last_hash = zlib.crc32(data) @@ -788,7 +780,7 @@ def test_integrating_frames_updates_frame_count_by_integration_count(self): frame_parameters = hardware_source.get_frame_parameters(0) frame_parameters.integration_count = 4 hardware_source.set_current_frame_parameters(frame_parameters) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: frame0_integration_count = hardware_source.get_next_xdatas_to_finish()[0].metadata["hardware_source"]["integration_count"] frame1_integration_count = hardware_source.get_next_xdatas_to_finish()[0].metadata["hardware_source"]["integration_count"] @@ -1073,7 +1065,7 @@ def test_acquisition_state_updates_during_acquisition(self): hardware_source = test_context.camera_hardware_source state_controller = self.__create_state_controller(test_context) self.assertEqual(state_controller.acquisition_state_model.value, "stopped") - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -1092,7 +1084,7 @@ def raise_exception(): hardware_source._test_start_hook = raise_exception hardware_source._test_acquire_exception = lambda *args: None state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -1110,7 +1102,7 @@ def raise_exception(): hardware_source._test_acquire_hook = raise_exception hardware_source._test_acquire_exception = lambda *args: None state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) document_controller.periodic() @@ -1514,7 +1506,7 @@ def test_eels_summed_controller_is_recognized_when_dropping_data_into_display_pa document_controller = test_context.document_controller document_model = test_context.document_model state_controller = self.__create_state_controller(test_context) - hardware_source.start_playing() + hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish(5) state_controller.use_processed_data = True diff --git a/nion/instrumentation/test/HardwareSource_test.py b/nion/instrumentation/test/HardwareSource_test.py index 1ef8e54..f430769 100644 --- a/nion/instrumentation/test/HardwareSource_test.py +++ b/nion/instrumentation/test/HardwareSource_test.py @@ -1260,8 +1260,11 @@ def test_data_item_created_during_acquisition_is_write_delayed_during_and_not_af document_model = simple_test_context.document_model hardware_source.start_playing(sync_timeout=3.0) try: - time.sleep(0.02) - document_controller.periodic() + start_time = time.time() + while len(document_model.data_items) == 0: + time.sleep(0.001) + document_controller.periodic() + self.assertTrue(time.time() - start_time < 3.0) self.assertEqual(1, len(document_model.data_items)) self.assertTrue(document_model.data_items[0].is_write_delayed) finally: diff --git a/nion/instrumentation/test/MultiAcquire_test.py b/nion/instrumentation/test/MultiAcquire_test.py index 4012464..ba5ade1 100755 --- a/nion/instrumentation/test/MultiAcquire_test.py +++ b/nion/instrumentation/test/MultiAcquire_test.py @@ -49,10 +49,8 @@ def test_acquire_multi_eels_spectrum_works_and_finishes_in_time(self): with self.__test_context(is_eels=True) as test_context: total_acquisition_time = 0.0 for parms in parameters: - # the simulator cant go super fast, so make sure we give it enough time - total_acquisition_time += parms['frames']*max(parms['exposure_ms'], 100)/1000 - # add some extra overhead time - total_acquisition_time += 0.30 + # give the simulator enough time + overhead + total_acquisition_time += parms['frames']*max(parms['exposure_ms'], 100)/1000 + 0.50 total_acquisition_time += settings['x_shift_delay']*2 total_acquisition_time += settings['x_shift_delay']*2 total_acquisition_time += settings['blanker_delay']*2 if settings['auto_dark_subtract'] else 0 From fb8e5902848e874ccdf5e80603516e00cb38b947 Mon Sep 17 00:00:00 2001 From: Chris Meyer <34664+cmeyer@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:41:41 -0800 Subject: [PATCH 3/4] Improve the device kit exposure timing. --- nion/device_kit/CameraDevice.py | 7 +++++-- nion/device_kit/ScanDevice.py | 4 ++-- nion/instrumentation/test/CameraControl_test.py | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nion/device_kit/CameraDevice.py b/nion/device_kit/CameraDevice.py index 34de472..2523a5a 100755 --- a/nion/device_kit/CameraDevice.py +++ b/nion/device_kit/CameraDevice.py @@ -304,7 +304,7 @@ def __direct_acquire(self, cancel_event: threading.Event, do_sync: bool = False) # if do_sync is True, the scan device will sync with its acquisition thread before # advancing the pixel. this avoids race conditions of when the pixel count gets reset. # this will be set only for the first frame in a sequence. - start = time.time() + start = time.perf_counter() readout_area = self.readout_area binning_shape = Geometry.IntSize(self.__binning, self.__binning if self.__symmetric_binning else 1) # scan device is only set during synchronized acquisition @@ -312,9 +312,12 @@ def __direct_acquire(self, cancel_event: threading.Event, do_sync: bool = False) self.__scan_device.advance_pixel(do_sync=do_sync) xdata = self.__simulator.get_frame_data(Geometry.IntRect.from_tlbr(*readout_area), binning_shape, self.__exposure, self.__instrument.scan_context, self.__instrument.probe_position) self.__acquired_one_event.set() - elapsed = time.time() - start + elapsed = time.perf_counter() - start wait_s = max(self.__exposure - elapsed, 0) if not cancel_event.wait(wait_s): + actual_elapsed_s = time.perf_counter() - start + if actual_elapsed_s < self.__exposure: + time.sleep(self.__exposure - actual_elapsed_s) # adjust in case the wait returns early (observed on python 3.13/windows) # thread event was not triggered during wait; signal that we have data xdata._set_timestamp(DateTime.utcnow()) self.__xdata_buffer = xdata diff --git a/nion/device_kit/ScanDevice.py b/nion/device_kit/ScanDevice.py index 0105712..e15ce27 100644 --- a/nion/device_kit/ScanDevice.py +++ b/nion/device_kit/ScanDevice.py @@ -42,7 +42,7 @@ def __init__(self, frame_number: int, channels: typing.List[Channel], frame_para self.complete = False self.bad = False self.data_count = 0 - self.start_time = time.time() + self.start_time = time.perf_counter() self.scan_data: typing.Optional[typing.List[_NDArray]] = None @@ -298,7 +298,7 @@ def read_partial(self, frame_number: typing.Optional[int], pixels_to_skip: int) pixels_remaining = min(total_pixels - current_frame.data_count, int(time_slice * 1e6 / frame_parameters.pixel_time_us) + 1) pixel_wait = min(pixels_remaining * frame_parameters.pixel_time_us / 1E6, time_slice) time.sleep(pixel_wait) - target_count = min(int((time.time() - current_frame.start_time) / (frame_parameters.pixel_time_us / 1E6)), total_pixels) + target_count = min(int((time.perf_counter() - current_frame.start_time) / (frame_parameters.pixel_time_us / 1E6)), total_pixels) if (new_pixels := target_count - self.__scan_simulator.current_pixel_flat) > 0: self.__scan_simulator._advance_pixel(new_pixels) diff --git a/nion/instrumentation/test/CameraControl_test.py b/nion/instrumentation/test/CameraControl_test.py index 3c177b3..4a54018 100644 --- a/nion/instrumentation/test/CameraControl_test.py +++ b/nion/instrumentation/test/CameraControl_test.py @@ -509,7 +509,6 @@ def test_first_view_uses_correct_exposure(self): hardware_source.start_playing(sync_timeout=3.0) try: hardware_source.get_next_xdatas_to_finish() # view again - time.sleep(0.001) # try to avoid Windows timer low precision issues elapsed = time.perf_counter() - start document_controller.periodic() self.assertEqual(document_model.data_items[0].data_shape, hardware_source.get_expected_dimensions(4)) From 4ace6fb08deb1e3372b6bc7183400537c0f7d366 Mon Sep 17 00:00:00 2001 From: Chris Meyer <34664+cmeyer@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:34:02 -0800 Subject: [PATCH 4/4] Minor test speed-ups. --- nion/instrumentation/test/ScanControl_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nion/instrumentation/test/ScanControl_test.py b/nion/instrumentation/test/ScanControl_test.py index 18662c8..49fa55d 100644 --- a/nion/instrumentation/test/ScanControl_test.py +++ b/nion/instrumentation/test/ScanControl_test.py @@ -445,9 +445,9 @@ def test_enabling_channel_during_acquisition_results_in_write_delayed_data_item( scan_hardware_source = test_context.scan_hardware_source scan_hardware_source.set_channel_enabled(0, True) frame_time = scan_hardware_source.get_current_frame_time() - scan_hardware_source.start_playing() + scan_hardware_source.start_playing(sync_timeout=3.0) try: - time.sleep(frame_time * 2.1) + time.sleep(frame_time * 1.2) test_context.document_controller.periodic() self.assertEqual(1, len(test_context.document_model.data_items)) self.assertTrue(test_context.document_model.data_items[0].is_write_delayed) @@ -770,16 +770,16 @@ def test_consecutive_frames_have_unique_data(self): with self._test_context() as test_context: scan_hardware_source = test_context.scan_hardware_source frame_parameters_0 = scan_hardware_source.get_frame_parameters(0) - frame_parameters_0.size = Geometry.IntSize(256, 256) + frame_parameters_0.size = Geometry.IntSize(16, 16) frame_parameters_0.pixel_time_us = 2 scan_hardware_source.set_frame_parameters(0, frame_parameters_0) scan_hardware_source.start_playing() data_list = list() - for i in range(16): + for i in range(6): data = scan_hardware_source.get_next_xdatas_to_finish()[0].data data_list.append(data) - for row in range(0, 256, 32): - s = slice(row, row+32), slice(0, 256) + for row in range(0, len(data_list)): + s = slice(row, row+1), slice(0, 16) for i, data in enumerate(data_list[1:]): self.assertFalse(numpy.array_equal(data_list[0][s], data[s])) finally: