diff --git a/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_1.py b/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_1.py index 5f9181b..e01ed23 100644 --- a/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_1.py +++ b/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_1.py @@ -104,7 +104,7 @@ DEFAULT_REFERENCES: list[tuple[LiteralChannels, CHANNELREFERENCE]] = [ ("B", CHANNELREFERENCE.A), ("C", CHANNELREFERENCE.T0), # T0 ("D", CHANNELREFERENCE.C), - ("E", CHANNELREFERENCE.D), # D One extra pulse once shutter closes for MCS + ("E", CHANNELREFERENCE.B), # B One extra pulse once shutter closes for MCS ("F", CHANNELREFERENCE.E), # E + 1mu s ("G", CHANNELREFERENCE.T0), ("H", CHANNELREFERENCE.G), @@ -213,8 +213,24 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS): # NOTE Burst delay should be set to 0, don't remove as this will not be checked # Also set the burst count to 1 to only have a single pulse for DDG1. + # As the IOC may be out of sync with the HW, we make sure that we set the default parameters + # in the IOC to the expected values. In the past, we've experienced that IOC and HW can go out + # of sync. + self.burst_delay.put(1) + time.sleep(0.02) # Give HW time to process self.burst_delay.put(0) + time.sleep(0.02) + + self.burst_count.put(2) + time.sleep(0.02) self.burst_count.put(1) + time.sleep(0.02) + + self.burst_mode.put(0) + time.sleep(0.02) + self.burst_mode.put(1) + time.sleep(0.02) + def keep_shutter_open_during_scan(self, open: True) -> None: """ @@ -291,17 +307,28 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS): # Burst Period DDG1 # Set burst_period to shutter width # c/t0 + self._shutter_to_open_delay + exp_time * burst_count + # SHUTTER WIDTH timing consists of the delay for the shutter to open + # + the exposure time * frames per trigger shutter_width = ( self._shutter_to_open_delay + exp_time * frames_per_trigger - ) # Shutter starts closing at end of exposure + ) + # TOTAL EXPOSURE accounts for the shutter to open AND close. In addition, we add + # a short additional delay of 3e-6 to allow for the extra trigger through 'ef' + # (delay of 1e-6, width of 1e-6) + total_exposure_time = ( + 2*self._shutter_to_open_delay + exp_time*frames_per_trigger + 3e-6 + ) if self.burst_period.get() != shutter_width: - self.burst_period.put(shutter_width) + # The burst_period has to be slightly longer + self.burst_period.put(total_exposure_time) # Trigger DDG2 # a = t0 + 2ms, b = a + 1us # a has reference to t0, b has reference to a - # Add delay of self._shutter_to_open_delay to allow shutter to open - self.set_delay_pairs(channel="ab", delay=self._shutter_to_open_delay, width=1e-6) + # AB is delayed by the shutter opening time, and the falling edge indicates the shutter has + # fully closed, it has to be considered as the blocking signal for the next acquisition to start. + # PS: + 3e-6 + self.set_delay_pairs(channel="ab", delay=self._shutter_to_open_delay, width=shutter_width) # Trigger shutter # d = c/t0 + self._shutter_to_open_delay + exp_time * burst_count + 1ms @@ -321,7 +348,7 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS): if self.scan_info.msg.scan_type == "fly": self.set_delay_pairs(channel="ef", delay=0, width=0) else: - self.set_delay_pairs(channel="ef", delay=0, width=1e-6) + self.set_delay_pairs(channel="ef", delay=1e-6, width=1e-6) # NOTE Add additional sleep to make sure that the IOC and DDG HW process the values properly # This value has been choosen empirically after testing with the HW. It's diff --git a/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_2.py b/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_2.py index e9ee1da..1108970 100644 --- a/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_2.py +++ b/csaxs_bec/devices/epics/delay_generator_csaxs/ddg_2.py @@ -138,6 +138,24 @@ class DDG2(PSIDeviceBase, DelayGeneratorCSAXS): # Set burst config self.burst_config.put(BURSTCONFIG.FIRST_CYCLE.value) + # TODO As the IOC may be out of sync with the HW, we make sure that we set the default parameters + # in the IOC to the expected values. In the past, we've experienced that IOC and HW can go out + # of sync. + self.burst_delay.put(1) + time.sleep(0.02) # Give HW time to process + self.burst_delay.put(0) + time.sleep(0.02) + + self.burst_count.put(2) + time.sleep(0.02) + self.burst_count.put(1) + time.sleep(0.02) + + self.burst_mode.put(0) + time.sleep(0.02) + self.burst_mode.put(1) + time.sleep(0.02) + def on_stage(self) -> DeviceStatus | StatusBase | None: """