From c729d67763a409f8766d2a0f39accf1e99da03ea Mon Sep 17 00:00:00 2001 From: gac-x05la Date: Mon, 10 Feb 2025 16:41:31 +0100 Subject: [PATCH] Added flat acquisition --- .../device_configs/microxas_test_bed.yaml | 38 ++--- tomcat_bec/scans/__init__.py | 2 +- tomcat_bec/scans/tutorial_fly_scan.py | 57 ++++--- tomcat_bec/scripts/scans_fede.py | 151 +++++++++++++++--- 4 files changed, 184 insertions(+), 64 deletions(-) diff --git a/tomcat_bec/device_configs/microxas_test_bed.yaml b/tomcat_bec/device_configs/microxas_test_bed.yaml index d9fe00f..d364070 100644 --- a/tomcat_bec/device_configs/microxas_test_bed.yaml +++ b/tomcat_bec/device_configs/microxas_test_bed.yaml @@ -10,30 +10,20 @@ eyex: enabled: true readOnly: false softwareTrigger: false -# eyey: -# readoutPriority: baseline -# description: X-ray eye axis Y -# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC -# deviceConfig: -# prefix: MTEST-X05LA-ES2-XRAYEYE:M2 -# deviceTags: -# - xray-eye -# onFailure: buffer -# enabled: true -# readOnly: false -# softwareTrigger: false -# eyez: -# readoutPriority: baseline -# description: X-ray eye axis Z -# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC -# deviceConfig: -# prefix: MTEST-X05LA-ES2-XRAYEYE:M3 -# deviceTags: -# - xray-eye -# onFailure: buffer -# enabled: true -# readOnly: false -# softwareTrigger: false + +eyez: + readoutPriority: baseline + description: X-ray eye axis Z + deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC + deviceConfig: + prefix: MTEST-X05LA-ES2-XRAYEYE:M3 + deviceTags: + - xray-eye + onFailure: buffer + enabled: true + readOnly: false + softwareTrigger: false + femto_mean_curr: readoutPriority: monitored description: Femto mean current diff --git a/tomcat_bec/scans/__init__.py b/tomcat_bec/scans/__init__.py index 2eb3866..230e33e 100644 --- a/tomcat_bec/scans/__init__.py +++ b/tomcat_bec/scans/__init__.py @@ -1,2 +1,2 @@ -from .tutorial_fly_scan import AcquireDark, AcquireFlat, TutorialFlyScanContLine +from .tutorial_fly_scan import AcquireDark, AcquireWhite, TutorialFlyScanContLine from .tomcat_scans import TomcatStepScan, TomcatSnapNStep, TomcatSimpleSequence diff --git a/tomcat_bec/scans/tutorial_fly_scan.py b/tomcat_bec/scans/tutorial_fly_scan.py index b027af8..8470680 100644 --- a/tomcat_bec/scans/tutorial_fly_scan.py +++ b/tomcat_bec/scans/tutorial_fly_scan.py @@ -27,7 +27,7 @@ class AcquireDark(Acquire): image_height : int, optional ROI size in the y-direction [pixels] acq_mode : str, optional - Predefined acquisition mode (default=) + Predefined acquisition mode (default= 'default') file_path : str, optional File path for standard daq @@ -51,39 +51,58 @@ class AcquireDark(Acquire): yield from super().scan_core() -class AcquireFlat(Acquire): - scan_name = "acquire_flat" - required_kwargs = ["num"] - gui_config = {"Acquisition parameters": ["num"]} +class AcquireWhite(Acquire): + scan_name = "acquire_white" + required_kwargs = ["exp_burst", "sample_position_out", "sample_angle_out"] + gui_config = {"Acquisition parameters": ["exp_burst"]} - def __init__(self, num: int, out_position: float, **kwargs): + def __init__(self, exp_burst: int, sample_position_out: float, sample_angle_out: float, **kwargs): """ - Acquire a flat field image. This scan is used to acquire a flat field image. The flat field image is an image taken - with the shutter open but the sample out of the beam. The flat field image is used to correct the data images for + Acquire flat field images. This scan is used to acquire flat field images. The flat field image is an image taken + with the shutter open but the sample out of the beam. Flat field images are used to correct the data images for non-uniformity in the detector. Args: - num (int): number of flat field images to acquire - out_position (float): position to move the sample stage to take the flat field image + exp_burst : int + Number of flat field images to acquire (no default) + sample_position_out : float + Position to move the sample stage to position the sample out of beam and take flat field images + sample_angle_out : float + Angular position where to take the flat field images + exp_time : float, optional + Exposure time [ms]. If not specified, the currently configured value on the camera will be used + exp_period : float, optional + Exposure period [ms]. If not specified, the currently configured value on the camera will be used + image_width : int, optional + ROI size in the x-direction [pixels]. If not specified, the currently configured value on the camera will be used + image_height : int, optional + ROI size in the y-direction [pixels]. If not specified, the currently configured value on the camera will be used + acq_mode : str, optional + Predefined acquisition mode (default= 'default') + file_path : str, optional + File path for standard daq Returns: ScanReport Examples: - >>> scans.acquire_flat(5, 20) + >>> scans.acquire_white(5, 20) """ super().__init__(**kwargs) - self.burst_at_each_point = num - self.out_position = out_position - self.sample_stage = "samy" # change to the correct sample stage device - self.shutter = "hx" # change to the correct shutter device + self.burst_at_each_point = 1 + self.sample_position_out = sample_position_out + self.sample_angle_out = sample_angle_out + + self.scan_motors = ["eyex", "eyez", "es1_roty"] # change to the correct shutter device +# self.scan_motors = ["eyex", "eyez"] # change to the correct shutter device + self.dark_shutter_pos = 1 ### change with a variable + def scan_core(self): # open the shutter and move the sample stage to the out position - yield from self.stubs.set_and_wait( - device=[self.shutter, self.sample_stage], positions=[1, self.out_position] - ) + yield from self._move_scan_motors_and_wait([self.dark_shutter_pos, self.sample_position_out, self.sample_angle_out]) +# yield from self._move_scan_motors_and_wait([self.dark_shutter_pos, self.sample_position_out]) yield from super().scan_core() @@ -168,7 +187,7 @@ class TutorialFlyScanContLine(AsyncFlyScanBase): show_asap=True, rid=self.metadata.get("RID"), ) - flats = AcquireFlat( + flats = AcquireWhite( num=self.num_flats, exp_time=self.exp_time, out_position=self.sample_out, diff --git a/tomcat_bec/scripts/scans_fede.py b/tomcat_bec/scripts/scans_fede.py index dacfb6f..917194d 100644 --- a/tomcat_bec/scripts/scans_fede.py +++ b/tomcat_bec/scripts/scans_fede.py @@ -12,6 +12,11 @@ class Measurement: self.nimages_dark = 50 self.nimages_white = 100 + self.start_angle = 0 + self.sample_angle_out = 0 + self.sample_position_in = 0 + self.sample_position_out = 1 + # To be able to keep what is already set on the camera self.exposure_time = None self.exposure_period = None @@ -29,12 +34,20 @@ class Measurement: self.build_filename() - def build_filename(self): + def build_filename(self, acquisition_type='data'): """ Build and set filepath for bec Build filepath and file prefix for stddaq + + acquisition_type : string, optional + Type of images: a choice between dark, white, or data (default = data) """ + if acquisition_type != "data" and acquisition_type != "dark" and acquisition_type != "white": + print("WARNING: chosen acquisition type not permitted! \n") + print("The chosen acquitisition type as been set to \"data\"!") + acquisition_type == "data" + self.scan_sample_name = 'S' + str(bec.queue.next_scan_number) + '_' + self.sample_name # File path for bec @@ -44,11 +57,13 @@ class Measurement: self.file_path = os.path.join('/data/test/test-beamline',bec.system_config.file_directory, '_device_dat', self.device_name) # _device_dat does not work for now. # A hack for now to create the right permissions self.base_path = os.path.join('/data/test/test-beamline',self.data_path) - self.file_prefix = self.scan_sample_name + '_' + self.device_name + '_data_dark_' + self.file_prefix = self.scan_sample_name + '_' + self.device_name + '_' + acquisition_type + '_' def configure(self,sample_name=None, data_path=None, exposure_time=None, exposure_period=None, roix=None, roiy=None,nimages=None, - nimages_dark=None, nimages_white=None): + nimages_dark=None, nimages_white=None, + start_angle=None, sample_angle_out=None, + sample_position_in=None, sample_position_out=None): """ Reconfigure the measurement with any number of new parameter @@ -56,24 +71,34 @@ class Measurement: ---------- sample_name : string, optional Name of the sample or measurement. This name will be used to construct - the name of the measurement directory (default=None) + the name of the measurement directory (default = None) data_path : string, optional Information used to build the data directory for the measurement - (default=None) + (default = None) exposure_time : float, optional - Exposure time [ms] (default=None) + Exposure time [ms] (default = None) exposure_period : float, optional - Exposure period [ms] (default=None) + Exposure period [ms] (default = None) roix : int, optional - ROI size in the x-direction [pixels] (default=None) + ROI size in the x-direction [pixels] (default = None) roiy : int, optional - ROI size in the y-direction [pixels] (default=None) + ROI size in the y-direction [pixels] (default = None) nimages : int, optional - Number of images to acquire (default=None) + Number of images to acquire (default = None) nimages_dark : int, optional - Number of dark images to acquire (default=None) + Number of dark images to acquire (default = None) nimages_white : int, optional - Number of white images to acquire (default=None) + Number of white images to acquire (default = None) + start_angle : float, optional + The start angle for the scan [deg] (default = None) + sample_angle_out : float, optional + Sample rotation angle for sample out of the beam [deg] + (default = None) + sample_position_in : float, optional + Sample stage X position for sample in beam [um] (default = None) + sample_position_out : float, optional + Sample stage X position for sample out of the beam [um] + (default = None) """ if sample_name != None: @@ -94,6 +119,14 @@ class Measurement: self.roix = roix if roiy != None: self.roiy = roiy + if start_angle != None: + self.start_angle = start_angle + if sample_angle_out != None: + self.sample_angle_out = sample_angle_out + if sample_position_in != None: + self.sample_position_in = sample_position_in + if sample_position_out != None: + self.sample_position_out = sample_position_out self.build_filename() @@ -102,7 +135,7 @@ class Measurement: """ Show configuration - TODO: check active devices, write the config for each device and if none read from device + TODO: make it work for multiple devices """ print("Sample name: " + self.sample_name) @@ -110,11 +143,28 @@ class Measurement: print("Number of images: " + str(self.nimages)) print("Number of darks: " + str(self.nimages_dark)) print("Number of flats: " + str(self.nimages_white)) - print("Exposure time: " + str(self.exposure_time)) - print("Exposure period: " + str(self.exposure_period)) - print("Roix: " + str(self.roix)) - print("Roiy: " + str(self.roiy)) + if self.exposure_time == None: + print("Exposure time: " + str(self.det.cfgExposure.get())) + else: + print("Exposure time: " + str(self.exposure_time)) + if self.exposure_period == None: + print("Exposure peirod: " + str(self.det.cfgFramerate.get())) + else: + print("Exposure period: " + str(self.exposure_period)) + if self.roix == None: + print("Roix: " + str(self.det.cfgRoiX.get())) + else: + print("Roix: " + str(self.roix)) + if self.roiy == None: + print("Roiy: " + str(self.det.cfgRoiY.get())) + else: + print("Roiy: " + str(self.roiy)) + print("Start angle: " + str(self.start_angle)) + print("Sample angle out: " + str(self.sample_angle_out)) + print("Sample position in: " + str(self.sample_position_in)) + print("Sample position out: " + str(self.sample_position_out)) + def acquire_darks(self,nimages_dark, exposure_time=None, exposure_period=None, roix=None, roiy=None, acq_mode=None): """ @@ -137,7 +187,7 @@ class Measurement: Example: -------- - fede_darks(100, exposure_time=5) + m.acquire_darks(100, exposure_time=5) """ # dev.es1_tasks.enabled = False # dev.es1_psod.enabled = False @@ -158,10 +208,71 @@ class Measurement: if roiy != None: self.roiy = roiy - self.build_filename() + self.build_filename(acquisition_type='dark') ### TODO: camera reset print("Handing over to 'scans.acquire_dark") scans.acquire_dark(exp_burst=nimages_dark, exp_time=self.exposure_time, exp_period=self.exposure_period, image_width=self.roix, image_height=self.roiy, acq_mode=acq_mode, file_path=self.file_path, nr_writers=2, base_path=self.base_path, - file_prefix=self.file_prefix) \ No newline at end of file + file_prefix=self.file_prefix) + + def acquire_whites(self,nimages_whites, sample_angle_out=None, sample_position_out=None, + exposure_time=None, exposure_period=None, + roix=None, roiy=None, acq_mode=None): + """ + Acquire a set of whites images with shutters open and sample out of beam. + + Parameters + ---------- + nimages_whites : int + Number of white images to acquire (no default) + sample_angle_out : float, optional + Sample rotation angle for sample out of the beam [deg] + sample_position_out : float, optional + Sample stage X position for sample out of the beam [um] + exposure_time : float, optional + Exposure time [ms]. If not specified, the currently configured value on the camera will be used + exposure_period : float, optional + Exposure period [ms] + roix : int, optional + ROI size in the x-direction [pixels] + roiy : int, optional + ROI size in the y-direction [pixels] + acq_mode : str, optional + Predefined acquisition mode (default=None) + + Example: + -------- + m.acquire_whites(100, exposure_time=5) + """ + # dev.es1_tasks.enabled = False + # dev.es1_psod.enabled = False + # dev.es1_ddaq.enabled = False + # dev.es1_ismc.enabled = False + # dev.es1_roty.enabled = False + dev.gfcam.enabled = True + dev.gfdaq.enabled = True + dev.daq_stream0.enabled = True + dev.daq_stream1.enabled = False + + if sample_angle_out != None: + self.sample_angle_out = sample_angle_out + if sample_position_out != None: + self.sample_position_out = sample_position_out + if exposure_time != None: + self.exposure_time = exposure_time + if exposure_period != None: + self.exposure_period = exposure_period + if roix != None: + self.roix = roix + if roiy != None: + self.roiy = roiy + + self.build_filename(acquisition_type='white') + + ### TODO: camera reset + print("Handing over to 'scans.acquire_whites") + scans.acquire_white(exp_burst=nimages_whites, sample_angle_out=self.sample_angle_out, sample_position_out= self.sample_position_out, + exp_time=self.exposure_time, exp_period=self.exposure_period, image_width=self.roix, + image_height=self.roiy, acq_mode=acq_mode, file_path=self.file_path, nr_writers=2, base_path=self.base_path, + file_prefix=self.file_prefix) \ No newline at end of file