def test_guess_mode(self): # test guess mode for ar sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) guess = self.optmngr.guessMode(ars) self.assertEqual(guess, "ar") guess = self.optmngr.guessMode(sas) self.assertEqual(guess, "ar") # test guess mode for spectral-dedicated sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) guess = self.optmngr.guessMode(specs) self.assertIn(guess, ("spectral", "spectral-dedicated")) guess = self.optmngr.guessMode(sps) self.assertIn(guess, ("spectral", "spectral-dedicated"))
def test_sync_sem_ccd(self): """ try acquisition with fairly complex SEM/CCD stream """ # Create the streams and streamTree semsur = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) sems = stream.SEMStream("test sem cl", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) semars = stream.SEMARMDStream("test SEM/AR", [sems, ars]) st = stream.StreamTree(streams=[semsur, semars]) # SEM survey settings are via the current hardware settings self.ebeam.dwellTime.value = self.ebeam.dwellTime.range[0] # SEM/AR settings are via the AR stream ars.roi.value = (0.1, 0.1, 0.8, 0.8) mx_brng = self.ccd.binning.range[1] binning = tuple(min(4, mx) for mx in mx_brng) # try binning 4x4 self.ccd.binning.value = binning self.ccd.exposureTime.value = 1 # s ars.repetition.value = (2, 3) num_ar = numpy.prod(ars.repetition.value) est_time = acq.estimateTime(st.getProjections()) # prepare callbacks self.start = None self.end = None self.updates = 0 self.done = 0 # Run acquisition start = time.time() f = acq.acquire(st.getProjections()) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data, e = f.result() dur = time.time() - start self.assertGreaterEqual(dur, est_time / 2) # Estimated time shouldn't be too small self.assertIsInstance(data[0], model.DataArray) self.assertIsNone(e) self.assertEqual(len(data), num_ar + 2) thumb = acq.computeThumbnail(st, f) self.assertIsInstance(thumb, model.DataArray) self.assertGreaterEqual(self.updates, 1) # at least one update at end self.assertLessEqual(self.end, time.time()) self.assertTrue(not f.cancelled()) time.sleep(0.1) self.assertEqual(self.done, 1)
def test_only_SEM_streams_with_zstack(self): sems = stream.SEMStream("sem", self.sed, self.sed.data, self.ebeam) self.streams = [sems] zlevels = {} est_time = acqmng.estimateZStackAcquisitionTime(self.streams, zlevels) # only one sem stream, so should be greater than or equal to 1 sec self.assertGreaterEqual(est_time, 1) # start the acquisition f = acqmng.acquireZStack(self.streams, zlevels) f.add_update_callback(self._on_progress_update) data, exp = f.result() self.assertIsNone(exp) for d in data: self.assertIsInstance(d, model.DataArray) # even if zstack, it's only SEM, so the center has 2 components self.assertEqual(len(d.metadata[model.MD_POS]), 2) # even if zstack, it's SEM, so the pixel size has 2 components self.assertEqual(len(d.metadata[model.MD_PIXEL_SIZE]), 2) # 1 streams, so 1 acquisitions self.assertEqual(len(data), 1) # 1 streams, 1 updates per stream, so 1 updates self.assertGreaterEqual(self._nb_updates, 1)
def test_acq_fine_align(self): """ try acquisition with SEM + Optical + overlay streams """ # Create the streams sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) # SEM settings are via the current hardware settings self.ebeam.dwellTime.value = self.ebeam.dwellTime.range[0] fs1 = stream.FluoStream("test orange", self.ccd, self.ccd.data, self.light, self.light_filter) fs1.excitation.value = fs1.excitation.range[0] + 5e-9 fs1.emission.value = fs1.emission.range[0] + 5e-9 fs2 = stream.FluoStream("test blue", self.ccd, self.ccd.data, self.light, self.light_filter) fs2.excitation.value = fs2.excitation.range[1] - 5e-9 fs2.emission.value = fs2.emission.range[1] - 5e-9 self.ccd.exposureTime.value = 0.1 # s ovrl = stream.OverlayStream("overlay", self.ccd, self.ebeam, self.sed) ovrl.dwellTime.value = 0.3 ovrl.repetition.value = (7, 7) streams = [sems, fs1, fs2, ovrl] est_time = acq.estimateTime(streams) sum_est_time = sum(s.estimateAcquisitionTime() for s in streams) self.assertGreaterEqual(est_time, sum_est_time) # prepare callbacks self.past = None self.left = None self.updates = 0 self.done = 0 # Run acquisition start = time.time() f = acq.acquire(streams) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data = f.result() dur = time.time() - start self.assertGreater(dur, est_time / 2) # Estimated time shouldn't be too small self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data), len(streams) - 1) # No overlay correction metadata anywhere (it has all been merged) for d in data: for k in [model.MD_ROTATION_COR, model.MD_PIXEL_SIZE_COR, model.MD_POS_COR]: self.assertNotIn(k, d.metadata) # thumb = acq.computeThumbnail(st, f) # self.assertIsInstance(thumb, model.DataArray) self.assertGreaterEqual(self.updates, 1) # at least one update at end self.assertEqual(self.left, 0) self.assertEqual(self.done, 1) self.assertTrue(not f.cancelled())
def test_drift_stream(self): escan = self.ebeam detector = self.sed ccd = self.ccd # Create the stream sems = stream.SEMStream("test sem", detector, detector.data, escan) ars = stream.ARSettingsStream("test ar", ccd, ccd.data, escan) sas = stream.SEMARMDStream("test sem-ar", [sems, ars]) # Long acquisition ccd.exposureTime.value = 1e-02 # s dc = leech.AnchorDriftCorrector(escan, detector) dc.period.value = 5 dc.roi.value = (0.525, 0.525, 0.6, 0.6) dc.dwellTime.value = 1e-04 sems.leeches.append(dc) escan.dwellTime.value = 1e-02 ars.roi.value = (0.4, 0.4, 0.6, 0.6) ars.repetition.value = (5, 5) start = time.time() for l in sas.leeches: l.series_start() f = sas.acquire() x = f.result() for l in sas.leeches: l.series_complete(x) dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done())
def test_drift_stream(self): escan = self.ebeam detector = self.sed ccd = self.ccd # Create the stream sems = stream.SEMStream("test sem", detector, detector.data, escan) ars = stream.ARStream("test ar", ccd, ccd.data, escan) sas = stream.SEMARMDStream("test sem-ar", sems, ars) # Long acquisition ccd.exposureTime.value = 1e-02 # s sems.dcPeriod.value = 5 sems.dcRegion.value = (0.525, 0.525, 0.6, 0.6) sems.dcDwellTime.value = 1e-04 escan.dwellTime.value = 1e-02 ars.roi.value = (0.4, 0.4, 0.6, 0.6) ars.repetition.value = (5, 5) start = time.time() f = sas.acquire() x = f.result() dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done())
def test_set_path_stream(self): # test guess mode for ar sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) self.optmngr.setPath(ars).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "ar") self.assert_pos_as_in_mode(self.slit, "ar") self.assert_pos_as_in_mode(self.specgraph, "ar") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) self.assertAlmostEqual(self.spec_sel.position.value["x"], 0.022) # Change positions back self.optmngr.setPath("mirror-align").result() self.optmngr.setPath(sas).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "ar") self.assert_pos_as_in_mode(self.slit, "ar") self.assert_pos_as_in_mode(self.specgraph, "ar") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) self.assertAlmostEqual(self.spec_sel.position.value["x"], 0.022) # test guess mode for spectral-dedicated sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) self.optmngr.setPath(specs).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "spectral") # No slit/spectrograph as they are not affecting the detector self.assertAlmostEqual(self.spec_sel.position.value["x"], 0.026112848) # Change positions back self.optmngr.setPath("chamber-view").result() self.optmngr.setPath(sps).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "spectral") self.assertAlmostEqual(self.spec_sel.position.value["x"], 0.026112848)
def test_progressive_future(self): """ Test .acquire interface (should return a progressive future with updates) """ self.image = None self.done = False self.updates = 0 # Create the stream sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) ars.roi.value = (0.1, 0.1, 0.8, 0.8) self.ccd.binning.value = (4, 4) # hopefully always supported # Long acquisition self.ccd.exposureTime.value = 0.2 # s ars.repetition.value = (2, 3) exp_shape = ars.repetition.value[::-1] num_ar = numpy.prod(ars.repetition.value) # Start acquisition timeout = 1 + 1.5 * sas.estimateAcquisitionTime() f = sas.acquire() f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data = f.result(timeout) self.assertEqual(len(data), num_ar + 1) self.assertEqual(data[0].shape, exp_shape) self.assertGreaterEqual(self.updates, 4) # at least a couple of updates self.assertEqual(self.left, 0) self.assertTrue(self.done) self.assertTrue(not f.cancelled()) # short acquisition self.done = False self.updates = 0 self.ccd.exposureTime.value = 0.02 # s ars.repetition.value = (5, 4) exp_shape = ars.repetition.value[::-1] num_ar = numpy.prod(ars.repetition.value) # Start acquisition timeout = 1 + 1.5 * sas.estimateAcquisitionTime() f = sas.acquire() f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data = f.result(timeout) self.assertEqual(len(data), num_ar + 1) self.assertEqual(data[0].shape, exp_shape) self.assertGreaterEqual(self.updates, 5) # at least a few updates self.assertEqual(self.left, 0) self.assertTrue(self.done) self.assertTrue(not f.cancelled())
def __init__(self, microscope, main_app): """ :param microscope: (Microscope or None) The main back-end component. :param main_app: (wx.App) The main GUI component. """ super(CLAcqPlugin, self).__init__(microscope, main_app) # Can only be used with a microscope if not microscope: return else: # Check which stream the microscope supports self.main_data = self.main_app.main_data if not (self.main_data.ccd and self.main_data.ebeam): return self.exposureTime = self.main_data.ccd.exposureTime self.binning = self.main_data.ccd.binning # Trick to pass the component (ccd to binning_1d_from_2d()) self.vaconf["binning"]["choices"] = ( lambda cp, va, cf: cutil.binning_1d_from_2d( self.main_data.ccd, va, cf)) self._survey_stream = None self._optical_stream = acqstream.BrightfieldStream( "Optical", self.main_data.ccd, self.main_data.ccd.data, emitter=None, focuser=self.main_data.focus) self._secom_cl_stream = SECOMCLSettingsStream("Secom-CL", self.main_data.ccd, self.main_data.ccd.data, self.main_data.ebeam) self._sem_stream = acqstream.SEMStream( "Secondary electrons concurrent", self.main_data.sed, self.main_data.sed.data, self.main_data.ebeam) self._secom_sem_cl_stream = SECOMCLSEMMDStream( "SECOM SEM CL", [self._sem_stream, self._secom_cl_stream]) self._driftCorrector = leech.AnchorDriftCorrector( self.main_data.ebeam, self.main_data.sed) self.conf = get_acqui_conf() self.expectedDuration = model.VigilantAttribute(1, unit="s", readonly=True) self.exposureTime.subscribe(self._update_exp_dur) self.filename = self._secom_sem_cl_stream.filename # duplicate VA self.filename.subscribe(self._on_filename) self.addMenu("Acquisition/CL acquisition...", self.start)
def start_sem(self): main_data = self.main_app.main_data sem_stream = stream.SEMStream( "Secondary electrons survey", main_data.sed, main_data.sed.data, main_data.ebeam, focuser=main_data.ebeam_focus, emtvas=get_local_vas(main_data.ebeam), detvas=get_local_vas(main_data.sed), ) self.start(sem_stream)
def test_guess_mode(self): # test guess mode for ar sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) guess = self.optmngr.guessMode(ars) self.assertEqual(guess, "ar") guess = self.optmngr.guessMode(sas) self.assertEqual(guess, "ar") # test guess mode for spectral sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) guess = self.optmngr.guessMode(specs) self.assertEqual(guess, "spectral") guess = self.optmngr.guessMode(sps) self.assertEqual(guess, "spectral") # test guess mode for cli sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) cls = stream.SpectrumSettingsStream("test cl", self.cld, self.cld.data, self.ebeam) sls = stream.SEMSpectrumMDStream("test sem-cl", sems, cls) guess = self.optmngr.guessMode(cls) self.assertEqual(guess, "cli") guess = self.optmngr.guessMode(sls) self.assertEqual(guess, "cli")
def setUpClass(cls): try: test.start_backend(ENZEL_CONFIG) except LookupError: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return except IOError as exp: logging.error(str(exp)) raise # create some streams connected to the backend cls.microscope = model.getMicroscope() cls.ccd = model.getComponent(role="ccd") cls.ebeam = model.getComponent(role="e-beam") cls.sed = model.getComponent(role="se-detector") cls.light = model.getComponent(role="light") cls.focus = model.getComponent(role="focus") cls.light_filter = model.getComponent(role="filter") cls.stage = model.getComponent(role="stage") # Make sure the lens is referenced cls.focus.reference({'z'}).result() # The 5DoF stage is not referenced automatically, so let's do it now stage_axes = set(cls.stage.axes.keys()) cls.stage.reference(stage_axes).result() # Create 1 sem stream and 2 fm streams to be used in testing ss1 = stream.SEMStream( "sem1", cls.sed, cls.sed.data, cls.ebeam, emtvas={"dwellTime", "scale", "magnification", "pixelSize"}) cls.ccd.exposureTime.value = cls.ccd.exposureTime.range[0] # go fast fs1 = stream.FluoStream("fluo1", cls.ccd, cls.ccd.data, cls.light, cls.light_filter, focuser=cls.focus) fs1.excitation.value = sorted(fs1.excitation.choices)[0] fs2 = stream.FluoStream("fluo2", cls.ccd, cls.ccd.data, cls.light, cls.light_filter, focuser=cls.focus) fs2.excitation.value = sorted(fs2.excitation.choices)[-1] cls.sem_streams = [ss1] cls.fm_streams = [fs1, fs2]
def test_metadata(self): """ Check if extra metadata are saved """ settings_obs = SettingsObserver(model.getComponents()) detvas = {"binning", "exposureTime"} sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam, detvas=detvas) sps = stream.SEMSpectrumMDStream("test sem-spec", [sems, specs]) specs.roi.value = (0, 0, 1, 1) specs.repetition.value = (2, 3) specs.detBinning.value = (2, specs.detBinning.value[1]) specs.detExposureTime.value = 0.1 specs2 = stream.SpectrumSettingsStream("test spec2", self.spec, self.spec.data, self.ebeam, detvas=detvas) sps2 = stream.SEMSpectrumMDStream("test sem-spec2", [sems, specs2]) specs2.roi.value = (0, 0, 1, 1) specs2.repetition.value = (2, 3) specs2.detBinning.value = (4, specs2.detBinning.value[1]) specs2.detExposureTime.value = 0.05 f = acqmng.acquire([sps, sps2], settings_obs) data = f.result() spec1_data = data[0][1] spec2_data = data[0][3] self.assertEqual( spec1_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name] ['binning'], [(2, specs.detBinning.value[1]), 'px']) self.assertEqual( spec2_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name] ['binning'], [(4, specs2.detBinning.value[1]), 'px']) self.assertEqual( spec1_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name] ['exposureTime'], [0.1, 's']) self.assertEqual( spec2_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name] ['exposureTime'], [0.05, 's'])
def test_overview_acquisition(self): s = stream.SEMStream( "Single beam", self.sed, self.sed.data, self.ebeam, focuser=self. efocuser, # Not used during acquisition, but done by the GUI hwemtvas={"scale", "dwellTime", "horizontalFoV"}) # This should be used by the acquisition s.dwellTime.value = 1e-6 # s # Use random settings and check they are overridden by the overview acquisition s.scale.value = (2, 2) s.horizontalFoV.value = 20e-6 # m # Known position of the center scintillator scintillator5_area = (-0.007, -0.007, 0.007, 0.007) # l, b, r, t # Small area for DEBUG (3x3) # scintillator5_area = (-0.002, -0.002, 0.002, 0.002) # l, b, r, t est_time = fastem.estimateTiledAcquisitionTime(s, self.stage, scintillator5_area) # don't use for DEBUG example self.assertGreater(est_time, 10) # It should take more than 10s! (expect ~5 min) before_start_t = time.time() f = fastem.acquireTiledArea(s, self.stage, scintillator5_area) time.sleep(1) start_t, end_t = f.get_progress() self.assertGreater(start_t, before_start_t) # don't use for DEBUG example self.assertGreater(end_t, time.time() + 10) # Should report still more than 10s overview_da = f.result() self.assertGreater(overview_da.shape[0], 2000) self.assertGreater(overview_da.shape[1], 2000) # Check the final area fits the requested area, with possibly a little bit of margin bbox = img.getBoundingBox(overview_da) fov = bbox[2] - bbox[0], bbox[3] - bbox[1] logging.debug("Got image of size %s, with FoV %s = %s", overview_da.shape, fov, bbox) self.assertLessEqual(bbox[0], scintillator5_area[0]) # Left self.assertLessEqual(bbox[1], scintillator5_area[1]) # Bottom self.assertGreaterEqual(bbox[2], scintillator5_area[2]) # Right self.assertGreaterEqual(bbox[3], scintillator5_area[3]) # Top
def test_FM_and_SEM_with_zstack(self): s1 = stream.FluoStream("fluo1", self.ccd, self.ccd.data, self.light, self.light_filter, focuser=self.fm_focuser) s1.excitation.value = sorted(s1.excitation.choices)[0] sems = stream.SEMStream("sem", self.sed, self.sed.data, self.ebeam) self.streams = [s1, sems] zlevels_list = generate_zlevels(self.fm_focuser, [-2e-6, 2e-6], 1e-6) zlevels = {} for s in self.streams: if isinstance(s, stream.FluoStream): zlevels[s] = list(zlevels_list) est_time = acqmng.estimateZStackAcquisitionTime(self.streams, zlevels) # about 5 seconds for fm streams, and 1 sec for sem stream, so should be # greater than or equal 5 sec self.assertGreaterEqual(est_time, 4) # start the acquisition f = acqmng.acquireZStack(self.streams, zlevels) f.add_update_callback(self._on_progress_update) data, exp = f.result() self.assertIsNone(exp) for i, d in enumerate(data): self.assertIsInstance(d, model.DataArray) if d.ndim > 2 and d.shape[-3] > 1: # 3D data (FM) # if zstack, so the center has 3 components self.assertEqual(len(d.metadata[model.MD_POS]), 3) # if zstack, so the pixel size has 3 components self.assertEqual(len(d.metadata[model.MD_PIXEL_SIZE]), 3) else: # 2D data (SEM) # even if zstack, it's SEM, so the center has 2 components self.assertEqual(len(d.metadata[model.MD_POS]), 2) # even if zstack, it's SEM, so the pixel size has 2 components self.assertEqual(len(d.metadata[model.MD_PIXEL_SIZE]), 2) # 2 streams, so 2 acquisitions self.assertEqual(len(data), 2) # 2 streams, 2 updates per stream, so >= 2 updates self.assertGreaterEqual(self._nb_updates, 2)
def test_guess_mode(self): # test guess mode for spectral sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) guess = self.optmngr.guessMode(specs) self.assertEqual(guess, "spectral") guess = self.optmngr.guessMode(sps) self.assertEqual(guess, "spectral") with self.assertRaises(LookupError): guess = self.optmngr.guessMode(sems)
def test_sync_future_cancel(self): self.image = None # Create the stream sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) ars.roi.value = (0.1, 0.1, 0.8, 0.8) self.ccd.binning.value = (4, 4) # hopefully always supported # Long acquisition self.updates = 0 self.ccd.exposureTime.value = 0.2 # s ars.repetition.value = (2, 3) # Start acquisition f = sas.acquire() f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) time.sleep(self.ccd.exposureTime.value) # wait a bit f.cancel() self.assertGreaterEqual(self.updates, 1) # at least at the end self.assertEqual(self.left, 0) self.assertTrue(f.cancelled()) # short acquisition self.updates = 0 self.ccd.exposureTime.value = 0.02 # s ars.repetition.value = (5, 4) # Start acquisition f = sas.acquire() f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) time.sleep(self.ccd.exposureTime.value) # wait a bit f.cancel() self.assertGreaterEqual(self.updates, 1) # at least at the end self.assertEqual(self.left, 0) self.assertTrue(f.cancelled())
def setUpClass(cls): try: test.start_backend(CRYOSECOM_CONFIG) except LookupError: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return except IOError as exp: logging.error(str(exp)) raise # create some streams connected to the backend cls.microscope = model.getMicroscope() cls.ccd = model.getComponent(role="ccd") cls.ebeam = model.getComponent(role="e-beam") cls.sed = model.getComponent(role="se-detector") cls.light = model.getComponent(role="light") cls.focus = model.getComponent(role="focus") cls.light_filter = model.getComponent(role="filter") cls.stage = model.getComponent(role="stage") # Create 1 sem stream and 2 fm streams to be used in testing ss1 = stream.SEMStream( "sem1", cls.sed, cls.sed.data, cls.ebeam, emtvas={"dwellTime", "scale", "magnification", "pixelSize"}) fs1 = stream.FluoStream("fluo1", cls.ccd, cls.ccd.data, cls.light, cls.light_filter, focuser=cls.focus) fs1.excitation.value = sorted(fs1.excitation.choices)[0] fs2 = stream.FluoStream("fluo2", cls.ccd, cls.ccd.data, cls.light, cls.light_filter) fs2.excitation.value = sorted(fs2.excitation.choices)[-1] cls.sem_streams = [ss1] cls.fm_streams = [fs1, fs2]
def addSEMBSD(self, **kwargs): """ Creates a new backscattered electron stream and panel in the stream bar returns (StreamPanel): the panel created """ if self._main_data_model.role == "delphi": # For the Delphi, the SEM stream needs to be more "clever" because # it needs to run a simple spot alignment every time the stage has # moved before starting to acquire. s = acqstream.AlignedSEMStream("Backscattered electrons", self._main_data_model.bsd, self._main_data_model.bsd.data, self._main_data_model.ebeam, self._main_data_model.ccd, self._main_data_model.stage, shiftebeam="Ebeam shift") # Select between "Metadata update" and "Stage move" # TODO: use shiftebeam once the phenom driver supports it else: s = acqstream.SEMStream("Backscattered electrons", self._main_data_model.bsd, self._main_data_model.bsd.data, self._main_data_model.ebeam) return self._addStream(s, **kwargs)
def test_set_path_stream(self): sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) self.optmngr.setPath(ars).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "ar") self.assert_pos_as_in_mode(self.slit, "ar") self.assert_pos_as_in_mode(self.specgraph, "ar") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) # Change positions back self.optmngr.setPath("mirror-align").result() self.optmngr.setPath(sas).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "ar") self.assert_pos_as_in_mode(self.slit, "ar") self.assert_pos_as_in_mode(self.specgraph, "ar") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) self.optmngr.setPath(specs).result() # Assert that actuator was moved according to mode given self.assertEqual(self.spec_det_sel.position.value, {'rx': 1.5707963267948966}) self.assertEqual(self.cl_det_sel.position.value, {'x': 0.01}) # Change positions back self.optmngr.setPath("chamber-view").result() self.optmngr.setPath(sps).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "spectral") self.assert_pos_as_in_mode(self.slit, "spectral") self.assert_pos_as_in_mode(self.specgraph, "spectral") self.assertEqual(self.spec_det_sel.position.value, {'rx': 1.5707963267948966}) self.assertEqual(self.cl_det_sel.position.value, {'x': 0.01}) sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumSettingsStream("test spec", self.spec_integrated, self.spec_integrated.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) # Change positions back self.optmngr.setPath("chamber-view").result() self.optmngr.setPath(specs).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "spectral-integrated") self.assert_pos_as_in_mode(self.slit, "spectral-integrated") self.assert_pos_as_in_mode(self.specgraph, "spectral-integrated") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) self.assertEqual(self.cl_det_sel.position.value, {'x': 0.01}) # Check the focus is remembered before going to chamber-view orig_focus = self.focus.position.value # Change positions back self.optmngr.setPath("chamber-view").result() self.focus.moveRel({"z": 1e-3}).result() self.optmngr.setPath(sps).result() # Assert that actuator was moved according to mode given self.assert_pos_as_in_mode(self.lenswitch, "spectral") self.assert_pos_as_in_mode(self.slit, "spectral") self.assert_pos_as_in_mode(self.specgraph, "spectral") self.assertEqual(self.spec_det_sel.position.value, {'rx': 0}) self.assertEqual(self.cl_det_sel.position.value, {'x': 0.01}) self.assertEqual(self.focus.position.value, orig_focus)
def acquire_timelapse(num, period, filename): """ num (int or None): if None, will never stop, unless interrupted """ # Find components by their role ccd = model.getComponent(role="ccd") ebeam = model.getComponent(role="e-beam") sed = model.getComponent(role="se-detector") light = model.getComponent(role="light") light_filter = model.getComponent(role="filter") stage = model.getComponent(role="stage") focus = model.getComponent(role="focus") # Prepare the streams and acquisition manager # The settings of the emissions and excitation are based on the current # hardware settings. stfm = stream.FluoStream("Fluorescence image", ccd, ccd.data, light, light_filter) # Force the excitation light using that command: # stfm.excitation.value = (4.72e-07, 4.79e-07, 4.85e-07, 4.91e-07, 4.97e-07) stem = stream.SEMStream("Secondary electrons", sed, sed.data, ebeam) # Special stream that will run the overlay and update the metadata based on this # Note: if more complex overlay is needed (eg, with background subtraction, # or with saving the CCD image), we'd need to directly call FindOverlay()) stovl = stream.OverlayStream("Overlay", ccd, ebeam, sed) stovl.dwellTime.value = OVERLAY_DT acq_streams = [stem, stfm, stovl] # Prepare to save each acquisition in a separate file exporter = dataio.find_fittest_converter(filename) basename, ext = os.path.splitext(filename) fn_pattern = basename + "%04d" + ext fn_pos = basename + "pos.csv" fpos = open(fn_pos, "a") fpos.write("time\tX\tY\tZ\n") # Run acquisition every period try: i = 1 while True: logging.info("Acquiring image %d", i) start = time.time() # Acquire all the images f = acq.acquire(acq_streams) data, e = f.result() if e: logging.error("Acquisition failed with %s", e) # It can partially fail, so still allow to save the data successfully acquired # Note: the actual time of the position is the one when the position was read # by the pigcs driver. spos = stage.position.value fpos.write("%.20g\t%g\t%g\t%g\n" % (time.time(), spos["x"], spos["y"], focus.position.value["z"])) # Save the file if data: exporter.export(fn_pattern % (i,), data) # TODO: run autofocus from time to time? left = period - (time.time() - start) if left < 0: logging.warning("Acquisition took longer than the period (%g s overdue)", -left) else: logging.info("Sleeping for another %g s", left) time.sleep(left) if i == num: # will never be True if num is None break i += 1 except KeyboardInterrupt: logging.info("Closing after only %d images acquired", i) except Exception: logging.exception("Failed to acquire all the images.") raise fpos.close()
def test_leech(self): """ try acquisition with leech """ # Create the streams and streamTree semsur = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) sems = stream.SEMStream("test sem cl", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam) semars = stream.SEMARMDStream("test SEM/AR", [sems, ars]) st = stream.StreamTree(streams=[semsur, semars]) pcd = Fake0DDetector("test") pca = ProbeCurrentAcquirer(pcd) sems.leeches.append(pca) semsur.leeches.append(pca) # SEM survey settings are via the current hardware settings self.ebeam.dwellTime.value = self.ebeam.dwellTime.range[0] # SEM/AR settings are via the AR stream ars.roi.value = (0.1, 0.1, 0.8, 0.8) mx_brng = self.ccd.binning.range[1] binning = tuple(min(4, mx) for mx in mx_brng) # try binning 4x4 self.ccd.binning.value = binning self.ccd.exposureTime.value = 1 # s ars.repetition.value = (2, 3) num_ar = numpy.prod(ars.repetition.value) pca.period.value = 10 # Only at beginning and end est_time = acq.estimateTime(st.getProjections()) # prepare callbacks self.start = None self.end = None self.updates = 0 self.done = 0 # Run acquisition start = time.time() f = acq.acquire(st.getProjections()) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data, e = f.result() dur = time.time() - start self.assertGreaterEqual(dur, est_time / 2) # Estimated time shouldn't be too small self.assertIsInstance(data[0], model.DataArray) self.assertIsNone(e) self.assertEqual(len(data), num_ar + 2) thumb = acq.computeThumbnail(st, f) self.assertIsInstance(thumb, model.DataArray) self.assertGreaterEqual(self.updates, 1) # at least one update at end self.assertLessEqual(self.end, time.time()) self.assertTrue(not f.cancelled()) time.sleep(0.1) self.assertEqual(self.done, 1) for da in data: pcmd = da.metadata[model.MD_EBEAM_CURRENT_TIME] self.assertEqual(len(pcmd), 2)
def test_sync_path_guess(self): """ try synchronized acquisition using the Optical Path Manager """ # Create the streams and streamTree opmngr = path.OpticalPathManager(self.microscope) semsur = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) sems = stream.SEMStream("test sem cl", self.sed, self.sed.data, self.ebeam) ars = stream.ARSettingsStream("test ar", self.ccd, self.ccd.data, self.ebeam, opm=opmngr) semars = stream.SEMARMDStream("test SEM/AR", [sems, ars]) specs = stream.SpectrumSettingsStream("test spec", self.spec, self.spec.data, self.ebeam, opm=opmngr) sps = stream.SEMSpectrumMDStream("test sem-spec", [sems, specs]) st = stream.StreamTree(streams=[semsur, semars, sps]) # SEM survey settings are via the current hardware settings self.ebeam.dwellTime.value = self.ebeam.dwellTime.range[0] # SEM/AR/SPEC settings are via the AR stream ars.roi.value = (0.1, 0.1, 0.8, 0.8) specs.roi.value = (0.2, 0.2, 0.7, 0.7) mx_brng = self.ccd.binning.range[1] binning = tuple(min(4, mx) for mx in mx_brng) # try binning 4x4 self.ccd.binning.value = binning self.ccd.exposureTime.value = 1 # s ars.repetition.value = (2, 3) specs.repetition.value = (3, 2) num_ar = numpy.prod(ars.repetition.value) est_time = acq.estimateTime(st.getProjections()) # prepare callbacks self.start = None self.end = None self.updates = 0 self.done = 0 # Run acquisition start = time.time() f = acq.acquire(st.getProjections()) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) data, e = f.result() dur = time.time() - start self.assertGreaterEqual(dur, est_time / 2) # Estimated time shouldn't be too small self.assertIsInstance(data[0], model.DataArray) self.assertIsNone(e) self.assertEqual(len(data), num_ar + 4) thumb = acq.computeThumbnail(st, f) self.assertIsInstance(thumb, model.DataArray) self.assertGreaterEqual(self.updates, 1) # at least one update at end self.assertLessEqual(self.end, time.time()) self.assertTrue(not f.cancelled()) # assert optical path configuration exp_pos = path.SPARC_MODES["spectral"][1] self.assertEqual(self.lenswitch.position.value, exp_pos["lens-switch"]) self.assertEqual(self.spec_det_sel.position.value, exp_pos["spec-det-selector"]) self.assertEqual(self.ar_spec_sel.position.value, exp_pos["ar-spec-selector"]) time.sleep(0.1) self.assertEqual(self.done, 1)
def test_histogram(self): """ Check the histogram updates correctly, including if the BPP changes """ ebeam = FakeEBeam("ebeam") se = FakeDetector("se") ss = stream.SEMStream("test", se, se.data, ebeam) # without data, the histogram should be empty, and the intensity range # based on the depth of the detector h = ss.histogram.value ir = ss.intensityRange.range self.assertEqual(len(h), 0) self.assertEqual((ir[0][0], ir[1][1]), (0, se.shape[0] - 1)) # "start" the stream, so it expects data ss.should_update.value = True ss.is_active.value = True # send a simple 8 bit image (with correct metadata) => # * The intensity range should update to 8-bit # * The histogram should be 256 long d = numpy.zeros(ebeam.shape[::-1], "uint8") md = { model.MD_BPP: 8, model.MD_PIXEL_SIZE: (1e-6, 2e-5), # m/px model.MD_POS: (1e-3, -30e-3), # m } da = model.DataArray(d, md) se.data.notify(da) time.sleep(0.5) # make sure all the delayed code is executed self.assertIsInstance(ss.image.value, model.DataArray) h = ss.histogram.value ir = ss.intensityRange.range self.assertEqual(len(h), 256) self.assertEqual((ir[0][0], ir[1][1]), (0, (2**8) - 1)) # Send a 16 bit image with 16 BPP => # * The intensity range should update to 16-bit # * The histogram should stay not too long (<=1024 values) d = numpy.zeros(ebeam.shape[::-1], "uint16") md = { model.MD_BPP: 16, model.MD_PIXEL_SIZE: (1e-6, 2e-5), # m/px model.MD_POS: (1e-3, -30e-3), # m } da = model.DataArray(d, md) se.data.notify(da) time.sleep(0.5) # make sure all the delayed code is executed self.assertIsInstance(ss.image.value, model.DataArray) h = ss.histogram.value ir = ss.intensityRange.range self.assertLessEqual(len(h), 1024) self.assertEqual((ir[0][0], ir[1][1]), (0, (2**16) - 1)) # Send a 16 bit image with 12 BPP => # * The intensity range should update to 12-bit # * The histogram should stay not too long (<=1024 values) d = numpy.zeros(ebeam.shape[::-1], "uint16") md = { model.MD_BPP: 12, model.MD_PIXEL_SIZE: (1e-6, 2e-5), # m/px model.MD_POS: (1e-3, -30e-3), # m } da = model.DataArray(d, md) se.data.notify(da) time.sleep(0.5) # make sure all the delayed code is executed self.assertIsInstance(ss.image.value, model.DataArray) h = ss.histogram.value ir = ss.intensityRange.range self.assertLessEqual(len(h), 1024) self.assertEqual((ir[0][0], ir[1][1]), (0, (2**12) - 1))
def test_acq_ar(self): """ Test short & long acquisition for AR """ # Create the stream sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) ars = stream.ARStream("test ar", self.ccd, self.ccd.data, self.ebeam) sas = stream.SEMARMDStream("test sem-ar", sems, ars) ars.roi.value = (0.1, 0.1, 0.8, 0.8) self.ccd.binning.value = (4, 4) # hopefully always supported # Long acquisition (small rep to avoid being too long) # The acquisition method is different for time > 0.1 s, but we had bugs # with dwell time > 4s, so let's directly test both. self.ccd.exposureTime.value = 5 # s ars.repetition.value = (2, 3) exp_shape = ars.repetition.value[::-1] num_ar = numpy.prod(ars.repetition.value) # Start acquisition timeout = 1 + 1.5 * sas.estimateAcquisitionTime() start = time.time() f = sas.acquire() # wait until it's over data = f.result(timeout) dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done()) self.assertEqual(len(data), len(sems.raw) + len(ars.raw)) self.assertEqual(len(sems.raw), 1) self.assertEqual(sems.raw[0].shape, exp_shape) self.assertEqual(len(ars.raw), num_ar) md = ars.raw[0].metadata self.assertIn(model.MD_POS, md) self.assertIn(model.MD_AR_POLE, md) # Short acquisition (< 0.1s) self.ccd.exposureTime.value = 0.03 # s ars.repetition.value = (30, 20) exp_shape = ars.repetition.value[::-1] num_ar = numpy.prod(ars.repetition.value) # Start acquisition timeout = 1 + 1.5 * sas.estimateAcquisitionTime() start = time.time() f = sas.acquire() # wait until it's over data = f.result(timeout) dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done()) self.assertEqual(len(data), len(sems.raw) + len(ars.raw)) self.assertEqual(len(sems.raw), 1) self.assertEqual(sems.raw[0].shape, exp_shape) self.assertEqual(len(ars.raw), num_ar) md = ars.raw[0].metadata self.assertIn(model.MD_POS, md) self.assertIn(model.MD_AR_POLE, md)
def acquire_spec(wls, wle, res, dt, filename): """ wls (float): start wavelength in m wle (float): end wavelength in m res (int): number of points to acquire dt (float): dwell time in seconds filename (str): filename to save to """ # TODO: take a progressive future to update and know if it's the end ebeam = model.getComponent(role="e-beam") sed = model.getComponent(role="se-detector") mchr = model.getComponent(role="monochromator") try: sgrh = model.getComponent(role="spectrograph") except LookupError: sgrh = model.getComponent(role="spectrograph-dedicated") opm = acq.path.OpticalPathManager(model.getMicroscope()) prev_dt = ebeam.dwellTime.value prev_res = ebeam.resolution.value prev_scale = ebeam.scale.value prev_trans = ebeam.translation.value prev_wl = sgrh.position.value["wavelength"] # Create a stream for monochromator scan mchr_s = MonochromatorScanStream("Spectrum", mchr, ebeam, sgrh, opm=opm) mchr_s.startWavelength.value = wls mchr_s.endWavelength.value = wle mchr_s.numberOfPixels.value = res mchr_s.dwellTime.value = dt mchr_s.emtTranslation.value = ebeam.translation.value # Create SEM survey stream survey_s = stream.SEMStream( "Secondary electrons survey", sed, sed.data, ebeam, emtvas={"translation", "scale", "resolution", "dwellTime"}, ) # max FoV, with scale 4 survey_s.emtTranslation.value = (0, 0) survey_s.emtScale.value = (4, 4) survey_s.emtResolution.value = (v / 4 for v in ebeam.resolution.range[1]) survey_s.emtDwellTime.value = 10e-6 # 10µs is hopefully enough # Acquire using the acquisition manager # Note: the monochromator scan stream is unknown to the acquisition manager, # so it'll be done last expt = acq.estimateTime([survey_s, mchr_s]) f = acq.acquire([survey_s, mchr_s]) try: # Note: the timeout is important, as it allows to catch KeyboardInterrupt das, e = f.result(2 * expt + 1) except KeyboardInterrupt: logging.info("Stopping before end of acquisition") f.cancel() return finally: logging.debug("Restoring hardware settings") if prev_res != (1, 1): ebeam.resolution.value = prev_res ebeam.dwellTime.value = prev_dt sgrh.moveAbs({"wavelength": prev_wl}) ebeam.scale.value = prev_scale ebeam.translation.value = prev_trans if prev_res != (1, 1): ebeam.resolution.value = prev_res ebeam.dwellTime.value = prev_dt if e: logging.error("Acquisition failed: %s", e) if das: # Save the file exporter = dataio.find_fittest_converter(filename) exporter.export(filename, das) logging.info("Spectrum successfully saved to %s", filename) input("Press Enter to close.")
def setUp(self): self.stream = stream.SEMStream("Single beam", self.sed, self.sed.data, self.ebeam, focuser=self.efocuser, # Not used during acquisition, but done by the GUI hwemtvas={"scale", "dwellTime", "horizontalFoV"})
def __init__(self, microscope, main_app): super(QuickCLPlugin, self).__init__(microscope, main_app) # Can only be used with a SPARC with CL detector (or monochromator) if not microscope: return main_data = self.main_app.main_data if not main_data.ebeam or not (main_data.cld or main_data.monochromator): return self.conf = get_acqui_conf() self.filename = model.StringVA("") self.filename.subscribe(self._on_filename) self.expectedDuration = model.VigilantAttribute(1, unit="s", readonly=True) self.hasDatabar = model.BooleanVA(False) # Only put the VAs that do directly define the image as local, everything # else should be global. The advantage is double: the global VAs will # set the hardware even if another stream (also using the e-beam) is # currently playing, and if the VAs are changed externally, the settings # will be displayed correctly (and not reset the values on next play). emtvas = set() hwemtvas = set() for vaname in get_local_vas(main_data.ebeam, main_data.hw_settings_config): if vaname in ("resolution", "dwellTime", "scale"): emtvas.add(vaname) else: hwemtvas.add(vaname) self._sem_stream = stream.SEMStream( "Secondary electrons", main_data.sed, main_data.sed.data, main_data.ebeam, focuser=main_data.ebeam_focus, hwemtvas=hwemtvas, hwdetvas=None, emtvas=emtvas, detvas=get_local_vas(main_data.sed, main_data.hw_settings_config), ) # This stream is used both for rendering and acquisition. # LiveCLStream is more or less like a SEMStream, but ensures the icon in # the merge slider is correct, and provide a few extra. if main_data.cld: self._cl_stream = LiveCLStream( "CL intensity", main_data.cld, main_data.cld.data, main_data.ebeam, focuser=main_data.ebeam_focus, emtvas=emtvas, detvas=get_local_vas(main_data.cld, main_data.hw_settings_config), opm=main_data.opm, ) # TODO: allow to type in the resolution of the CL? # TODO: add the cl-filter axis (or reset it to pass-through?) self.logScale = self._cl_stream.logScale if hasattr(self._cl_stream, "detGain"): self._cl_stream.detGain.subscribe(self._on_cl_gain) # Update the acquisition time when it might change (ie, the scan settings # change) self._cl_stream.emtDwellTime.subscribe(self._update_exp_dur) self._cl_stream.emtResolution.subscribe(self._update_exp_dur) # Note: for now we don't really support SPARC with BOTH CL-detector and # monochromator. if main_data.monochromator: self._mn_stream = LiveCLStream( "Monochromator", main_data.monochromator, main_data.monochromator.data, main_data.ebeam, focuser=main_data.ebeam_focus, emtvas=emtvas, detvas=get_local_vas(main_data.monochromator, main_data.hw_settings_config), opm=main_data.opm, ) self._mn_stream.emtDwellTime.subscribe(self._update_exp_dur) self._mn_stream.emtResolution.subscribe(self._update_exp_dur) # spg = self._getAffectingSpectrograph(main_data.spectrometer) # TODO: show axes self._dlg = None self.addMenu("Acquisition/Quick CL...\tF2", self.start)
def test_acq_spec(self): """ Test short & long acquisition for Spectrometer """ # Create the stream sems = stream.SEMStream("test sem", self.sed, self.sed.data, self.ebeam) specs = stream.SpectrumStream("test spec", self.spec, self.spec.data, self.ebeam) sps = stream.SEMSpectrumMDStream("test sem-spec", sems, specs) specs.roi.value = (0.15, 0.6, 0.8, 0.8) # Long acquisition (small rep to avoid being too long) > 0.1s self.spec.exposureTime.value = 0.3 # s specs.repetition.value = (5, 6) exp_shape = specs.repetition.value[::-1] # Start acquisition timeout = 1 + 1.5 * sps.estimateAcquisitionTime() start = time.time() f = sps.acquire() # wait until it's over data = f.result(timeout) dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done()) self.assertEqual(len(data), len(sems.raw) + len(specs.raw)) self.assertEqual(len(sems.raw), 1) self.assertEqual(sems.raw[0].shape, exp_shape) self.assertEqual(len(specs.raw), 1) sshape = specs.raw[0].shape self.assertEqual(len(sshape), 5) self.assertGreater(sshape[0], 1) # should have at least 2 wavelengths sem_md = sems.raw[0].metadata spec_md = specs.raw[0].metadata self.assertAlmostEqual(sem_md[model.MD_POS], spec_md[model.MD_POS]) self.assertAlmostEqual(sem_md[model.MD_PIXEL_SIZE], spec_md[model.MD_PIXEL_SIZE]) # Short acquisition (< 0.1s) self.spec.exposureTime.value = 0.01 # s specs.repetition.value = (25, 60) exp_shape = specs.repetition.value[::-1] # Start acquisition timeout = 1 + 1.5 * sps.estimateAcquisitionTime() start = time.time() f = sps.acquire() # wait until it's over data = f.result(timeout) dur = time.time() - start logging.debug("Acquisition took %g s", dur) self.assertTrue(f.done()) self.assertEqual(len(data), len(sems.raw) + len(specs.raw)) self.assertEqual(len(sems.raw), 1) self.assertEqual(sems.raw[0].shape, exp_shape) self.assertEqual(len(specs.raw), 1) sshape = specs.raw[0].shape self.assertEqual(len(sshape), 5) self.assertGreater(sshape[0], 1) # should have at least 2 wavelengths sem_md = sems.raw[0].metadata spec_md = specs.raw[0].metadata self.assertAlmostEqual(sem_md[model.MD_POS], spec_md[model.MD_POS]) self.assertAlmostEqual(sem_md[model.MD_PIXEL_SIZE], spec_md[model.MD_PIXEL_SIZE])