Commit f9749468 authored by Clemence Muzelle's avatar Clemence Muzelle
Browse files

Merge branch '30-restructuration' into 'master'

Resolve "Restructuration"

Closes #30 and #10

See merge request !3
parents 088a5c04 7ba7f1e6
......@@ -49,6 +49,7 @@ class SoftTriggerMusstAcquisitionMaster(MusstAcquisitionMaster):
pass
def trigger(self):
gevent.sleep(0.2)
self.musst.run(self.program_abort_name)
while self.musst.STATE == self.musst.RUN_STATE:
gevent.idle()
......
This diff is collapsed.
This diff is collapsed.
......@@ -7,7 +7,7 @@ import bliss
from bliss import global_map, current_session
from bliss.common import session
from bliss import setup_globals
from bliss.common.logtools import log_info,log_debug
from bliss.common.logtools import log_info, log_debug
class TomoMetaData:
......@@ -26,121 +26,127 @@ class TomoMetaData:
contains all info about tomo (hardware, parameters)
"""
def __init__(self, tomo_name, tomo):
# init logging
self.name = tomo_name+".metadata"
self.name = tomo_name + ".metadata"
global_map.register(self, tag=self.name)
log_info(self,"__init__() entering")
log_info(self, "__init__() entering")
self.tomo = tomo
def tomo_scan_info(self):
"""
Builds and returns the nested metadata dictionary.
Metadata are divided into several categories: scan, scan flags (for options),
detector, optic, saving, reference and dark.
"""
scan_info = {}
# Basic tomo scan parameters
scan = {}
scan['name'] = current_session.scan_saving.dataset.name
alias_name = 'None'
scan["name"] = current_session.scan_saving.dataset.name
alias_name = "None"
if setup_globals.ALIASES.get_alias(self.tomo.rotation_axis.name) is not None:
alias_name = setup_globals.ALIASES.get_alias(self.tomo.rotation_axis.name)
scan['motor'] = (self.tomo.rotation_axis.name,alias_name)
scan['scan_type'] = self.tomo.parameters.scan_type.name
scan['scan_range'] = self.tomo.parameters.end_pos - self.tomo.parameters.start_pos
scan['tomo_n'] = self.tomo.parameters.tomo_n
scan["motor"] = (self.tomo.rotation_axis.name, alias_name)
scan["scan_type"] = self.tomo.parameters.scan_type.name
scan["scan_range"] = (
self.tomo.parameters.end_pos - self.tomo.parameters.start_pos
)
scan["tomo_n"] = self.tomo.parameters.tomo_n
### for step scan one additional image is taken for each ref group
### asked by DAU to adapt tomo_n value for coherency
if self.tomo.parameters.scan_type.name == 'STEP':
### asked by DAU to adapt tomo_n value for coherency
if self.tomo.parameters.scan_type.name == "STEP":
self.tomo.active_tomo.calculate_parameters()
scan['tomo_n'] += self.tomo.in_pars['nb_groups']
scan["tomo_n"] += self.tomo.in_pars["nb_groups"]
###
scan['ref_on'] = self.tomo.parameters.ref_on
scan['exposure_time'] = self.tomo.parameters.exposure_time * 1000.0
scan['exposure_time@units'] = "ms"
scan['latency_time'] = self.tomo.parameters.latency_time * 1000.0
scan['latency_time@units'] = "ms"
scan['shutter_time'] = self.tomo.shutter.fast_shutter_closing_time() * 1000.0
scan['shutter_time@units'] = "ms"
scan['energy'] = self.tomo.parameters.energy
scan['energy@units'] = "keV"
scan['source_sample_distance'] = self.tomo.parameters.source_sample_distance
scan['source_sample_distance@units'] = "mm"
scan['sample_detector_distance'] = self.tomo.parameters.sample_detector_distance
scan['sample_detector_distance@units'] = "mm"
scan['sequence'] = self.tomo.sequence
scan['field_of_view'] = self.tomo.field_of_view
scan["ref_on"] = self.tomo.parameters.ref_on
scan["exposure_time"] = self.tomo.parameters.exposure_time * 1000.0
scan["exposure_time@units"] = "ms"
scan["latency_time"] = self.tomo.parameters.latency_time * 1000.0
scan["latency_time@units"] = "ms"
if self.tomo.shutter.sync_shutter:
scan["shutter_time"] = self.tomo.shutter.sync_shutter_time
else:
scan["shutter_time"] = (
self.tomo.shutter.fast_shutter_closing_time() * 1000.0
)
scan["shutter_time@units"] = "ms"
scan["energy"] = self.tomo.parameters.energy
scan["energy@units"] = "keV"
scan["source_sample_distance"] = self.tomo.parameters.source_sample_distance
scan["source_sample_distance@units"] = "mm"
scan["sample_detector_distance"] = self.tomo.parameters.sample_detector_distance
scan["sample_detector_distance@units"] = "mm"
scan["sequence"] = self.tomo.sequence
scan["field_of_view"] = self.tomo.field_of_view
# Missing
# comment
scan['comment'] = self.tomo.saving.parameters.comment
scan_info['scan'] = scan
scan["comment"] = self.tomo.saving.parameters.comment
scan_info["scan"] = scan
# Scan configuration flags
scan_flags = {}
scan_flags['half_acquisition'] = self.tomo.parameters.half_acquisition
scan_flags['dark_images_at_start'] = self.tomo.parameters.dark_images_at_start
scan_flags['dark_images_at_end'] = self.tomo.parameters.dark_images_at_end
scan_flags['ref_images_at_start'] = self.tomo.parameters.ref_images_at_start
scan_flags['ref_images_at_end'] = self.tomo.parameters.ref_images_at_end
scan_flags['no_reference_groups'] = not self.tomo.parameters.reference_groups
scan_flags['no_return_images'] = not self.tomo.parameters.return_images
scan_flags['return_images_aligned_to_refs'] = self.tomo.parameters.return_images_aligned_to_refs
scan_flags['return_to_start_pos'] = self.tomo.parameters.return_to_start_pos
scan_info['scan_flags'] = scan_flags
scan_flags["half_acquisition"] = self.tomo.parameters.half_acquisition
scan_flags["dark_images_at_start"] = self.tomo.parameters.dark_images_at_start
scan_flags["dark_images_at_end"] = self.tomo.parameters.dark_images_at_end
scan_flags["ref_images_at_start"] = self.tomo.parameters.ref_images_at_start
scan_flags["ref_images_at_end"] = self.tomo.parameters.ref_images_at_end
scan_flags["no_reference_groups"] = not self.tomo.parameters.reference_groups
scan_flags["no_return_images"] = not self.tomo.parameters.return_images
scan_flags[
"return_images_aligned_to_refs"
] = self.tomo.parameters.return_images_aligned_to_refs
scan_flags["return_to_start_pos"] = self.tomo.parameters.return_to_start_pos
scan_info["scan_flags"] = scan_flags
# Detector image parameters
# read image parameters
image_params = self.tomo.tomo_ccd.get_image_parameters()
scan_info['detector'] = image_params
scan_info["detector"] = image_params
# optic parameters
optic = {}
optic['name'] = self.tomo.optic.description
optic['type'] = self.tomo.optic.type
optic['magnification'] = self.tomo.optic.magnification
optic['sample_pixel_size'] = self.tomo.optic.image_pixel_size
optic['sample_pixel_size@units'] = "um"
optic['scintillator'] = self.tomo.optic.scintillator
scan_info['optic'] = optic
optic["name"] = self.tomo.optic.description
optic["type"] = self.tomo.optic.type
optic["magnification"] = self.tomo.optic.magnification
optic["sample_pixel_size"] = self.tomo.optic.image_pixel_size
optic["sample_pixel_size@units"] = "um"
optic["scintillator"] = self.tomo.optic.scintillator
scan_info["optic"] = optic
# saving parameters
saving={}
saving['path'] = current_session.scan_saving.get_path()
saving['image_file_format'] = self.tomo.tomo_ccd.detector.saving.file_format
saving['frames_per_file'] = self.tomo.tomo_ccd.detector.saving.frames_per_file
scan_info['saving'] = saving
saving = {}
saving["path"] = current_session.scan_saving.get_path()
saving["image_file_format"] = self.tomo.tomo_ccd.detector.saving.file_format
saving["frames_per_file"] = self.tomo.tomo_ccd.detector.saving.frames_per_file
scan_info["saving"] = saving
# dark parameters
dark={}
dark['dark_n'] = self.tomo.parameters.dark_n
scan_info['dark'] = dark
dark = {}
dark["dark_n"] = self.tomo.parameters.dark_n
scan_info["dark"] = dark
# reference displacement
# get motor names
names=[]
names = []
for i in self.tomo.reference.ref_motors:
names.append(i.name)
ref={}
ref['ref_n'] = self.tomo.parameters.ref_n
ref['motors'] = names
ref['displacement'] = self.tomo.reference.parameters.out_of_beam_displacement
ref['displacement@units'] = "mm"
scan_info['reference'] = ref
ref = {}
ref["ref_n"] = self.tomo.parameters.ref_n
ref["motors"] = names
ref["displacement"] = self.tomo.reference.parameters.out_of_beam_displacement
ref["displacement@units"] = "mm"
scan_info["reference"] = ref
# Create the entry in the HDF5 file
meta_data = {'type2' : 'tomo','technique' : scan_info}
meta_data = {"type2": "tomo", "technique": scan_info}
return meta_data
<
from bliss import setup_globals,global_map
from bliss import setup_globals, global_map
from bliss.scanning.acquisition.musst import *
from bliss.scanning.acquisition.calc import CalcChannelAcquisitionSlave
from bliss.common import session
......@@ -7,10 +7,13 @@ from bliss.common.logtools import log_info
import tomo
from enum import Enum
class TriggerType(Enum):
POSITION = 0
POSITION = 0
TIME = 1
class TomoMusst:
"""
Class for tomo musst object.
......@@ -35,11 +38,11 @@ class TomoMusst:
musst clock value before tomo acquisition
"""
def __init__(self, tomo_name, card, motor, *args, **kwargs):
def __init__(self, tomo_name, card, motor, sync_shut_time=None, *args, **kwargs):
self.name = tomo_name+".musst"
self.name = tomo_name + ".musst"
global_map.register(self, tag=self.name)
log_info(self,"__init__() entering")
log_info(self, "__init__() entering")
self.tomo_name = tomo_name
......@@ -54,22 +57,36 @@ class TomoMusst:
except RuntimeError:
self.enc_channel = None
self.mot_chan = None
print("No musst channel associated to this motor! \
\nOnly step scans or continuous scans with soft synchronization are possible!")
print(
"No musst channel associated to this motor! \
\nOnly step scans or continuous scans with soft synchronization are possible!"
)
self.storelist = ["timer_raw",f"{self.motor.name}_raw"]
self.storelist = ["timer_raw", f"{self.motor.name}_raw"]
self.tmrcfg = None
log_info(self,"__init__() leaving")
self.sync_shutter_active = False
if sync_shut_time is not None and sync_shut_time > 0:
self.sync_shutter_active = True
self.sync_shut_time = sync_shut_time
log_info(self, "__init__() leaving")
def sync_motor(self):
"""
Sets musst channel value with motor encoder value.
"""
self.motor.wait_move()
self.enc_channel.value = int(round((self.motor.position*self.motor.steps_per_unit*self.motor.sign+0.5)//1))
def set_timer_clock(self,scan_time):
self.enc_channel.value = int(
round(
(
self.motor.position * self.motor.steps_per_unit * self.motor.sign
+ 0.5
)
// 1
)
)
def set_timer_clock(self, scan_time):
"""
Sets musst clock with the most adapted value (highest precision with
long enough duration) for tomo acquisition.
......@@ -82,29 +99,41 @@ class TomoMusst:
clock_index -= 1
self.card.TMRCFG = clock_cmds[clock_index]
def set_timer_back(self):
"""
Sets musst clock to previous value (before tomo acquisition).
"""
if self.tmrcfg is not None:
self.card.TMRCFG = self.tmrcfg[0]
def prepare_continuous(self, start_pos, exposure_time, latency_time, nb_points, scan_step_size, scan_point_time, scan_time, accumulation=1, trigger_type=TriggerType.TIME):
def prepare_continuous(
self,
start_pos,
exposure_time,
latency_time,
nb_points,
scan_step_size,
scan_point_time,
scan_time,
accumulation=1,
trigger_type=TriggerType.TIME,
):
"""
Configures musst for hard continuous scan.
Musst generates pulses in position or in time and gates in time to control eventually a shutter.
Musst program handles accumulation mode.
"""
log_info(self,"prepare_continuous() entering")
log_info(self, "prepare_continuous() entering")
self.card.CLEAR
self.sync_motor()
# mot_zero is a list of encoder values corresponding to
# mot_zero is a list of encoder values corresponding to
# start position tomo parameter
mot_start = int(round((start_pos * self.motor.steps_per_unit * self.motor.sign+0.5)//1))
mot_start = int(
round((start_pos * self.motor.steps_per_unit * self.motor.sign + 0.5) // 1)
)
self.tmrcfg = self.card.TMRCFG
self.set_timer_clock(scan_time)
......@@ -112,162 +141,204 @@ class TomoMusst:
trig_delta = 0
if accumulation > 1:
# convert exposure time to timer ticks
trig_delta = (scan_point_time - latency_time) / accumulation
gatewidth = int(round(((((accumulation - 1) * trig_delta + exposure_time / accumulation) * self.card.get_timer_factor())+0.5)//1))
trig_delta = (scan_point_time - latency_time) / accumulation
gatewidth = int(
round(
(
(
((accumulation - 1) * trig_delta + exposure_time / accumulation)
* self.card.get_timer_factor()
)
+ 0.5
)
// 1
)
)
trig_delta = int(round(trig_delta * self.card.get_timer_factor() + 0.5) // 1)
trig_delta = int(round(trig_delta * self.card.get_timer_factor() +0.5)//1)
# --- position mode
if trigger_type == TriggerType.POSITION:
pos_delta = scan_step_size * self.motor.steps_per_unit * self.motor.sign
# musst will correct step size at each image
enc_delta = int(pos_delta)
mot_error = abs(int((enc_delta - pos_delta) * 2**31))
mot_error = abs(int((enc_delta - pos_delta) * 2 ** 31))
# configure musst variables used by 'ftomo_acc' program
# corresponding to a reference group
# configure musst variables used by 'ftomo_acc' program
# corresponding to a reference group
musst_vars = {
"MOTSTART" : int(mot_start),
"NPTS" : int(nb_points),
"PTMODE" : 1,
"PTDELTA" : int(pos_delta),
"GATEWIDTH" : gatewidth,
"MOTERR" : mot_error,
"NTRIGS" : int(accumulation),
"TRIGDELTA" : trig_delta
}
"MOTSTART": int(mot_start),
"NPTS": int(nb_points),
"PTMODE": 1,
"PTDELTA": int(pos_delta),
"GATEWIDTH": gatewidth,
"MOTERR": mot_error,
"NTRIGS": int(accumulation),
"TRIGDELTA": trig_delta,
}
# --- time mode
if trigger_type == TriggerType.TIME:
time_delta = scan_point_time*self.card.get_timer_factor()*self.motor.sign
time_delta = (
scan_point_time * self.card.get_timer_factor() * self.motor.sign
)
musst_vars = {
"MOTSTART" : mot_start,
"NPTS" : int(nb_points),
"PTMODE" : 0,
"PTDELTA" : int(time_delta),
"GATEWIDTH" : gatewidth,
"NTRIGS" : int(accumulation),
"TRIGDELTA" : trig_delta
}
"MOTSTART": mot_start,
"NPTS": int(nb_points),
"PTMODE": 0,
"PTDELTA": int(time_delta),
"GATEWIDTH": gatewidth,
"NTRIGS": int(accumulation),
"TRIGDELTA": trig_delta,
}
# allows to replace alias 'MOTOR_CHANNEL' in 'ftomo_acc' program with channel associated to motor 'CH#'
template_replacement= { "$MOTOR_CHANNEL$": "CH%d"%self.mot_chan}
template_replacement = {"$MOTOR_CHANNEL$": "CH%d" % self.mot_chan}
# MUSST acquisition master
musst_master = MusstAcquisitionMaster(self.card,
program = 'ftomo_acc.mprg',
program_start_name = 'TOMO',
program_abort_name = 'TOMO_CLEAN',
program_template_replacement=template_replacement,
vars=musst_vars)
# MUSST acquisition slave
musst_slave = MusstAcquisitionSlave(self.card, store_list=self.storelist) # store encoder and timer data
log_info(self,"prepare_continuous() leaving")
musst_master = MusstAcquisitionMaster(
self.card,
program="ftomo_acc.mprg",
program_start_name="TOMO",
program_abort_name="TOMO_CLEAN",
program_template_replacement=template_replacement,
vars=musst_vars,
)
# MUSST acquisition slave
musst_slave = MusstAcquisitionSlave(
self.card, store_list=self.storelist
) # store encoder and timer data
log_info(self, "prepare_continuous() leaving")
return musst_master, musst_slave
def prepare_sweep(self, start_pos, nb_points, scan_step_size, undershoot, scan_time):
def prepare_sweep(
self, start_pos, nb_points, scan_step_size, undershoot, scan_time
):
"""
Configures musst for sweep scan.
Musst generates gates in position.
"""
log_info(self,"prepare_sweep() entering")
log_info(self, "prepare_sweep() entering")
self.sync_motor()
# mot_zero is a list of encoder values corresponding to
# mot_zero is a list of encoder values corresponding to
# start position tomo parameter
mot_zero = int(round((start_pos * self.motor.steps_per_unit * self.motor.sign+0.5)//1))
mot_zero = int(
round((start_pos * self.motor.steps_per_unit * self.motor.sign + 0.5) // 1)
)
self.tmrcfg = self.card.TMRCFG
self.set_timer_clock(scan_time)
pos_delta = scan_step_size * self.motor.steps_per_unit * self.motor.sign
# musst will correct step size at each image
enc_delta = int(pos_delta)
mot_error = abs(int((enc_delta - pos_delta) * 2**31))
mot_error = abs(int((enc_delta - pos_delta) * 2 ** 31))
# configure musst variables used by 'ftomo_wo_gap' program
# configure musst variables used by 'ftomo_wo_gap' program
# corresponding to a reference group
first_iter_vars = {"POSSTART" : mot_zero,
"POSDELTA" : enc_delta,
"NPULSES" : nb_points,
"MOTERR" : mot_error,
"UNDERSHOOT" : int(undershoot * self.motor.steps_per_unit * 0.9) # target value used for motor back movement to update target register
first_iter_vars = {
"POSSTART": mot_zero,
"POSDELTA": enc_delta,
"NPULSES": nb_points,
"MOTERR": mot_error,
"UNDERSHOOT": int(
undershoot * self.motor.steps_per_unit * 0.9
), # target value used for motor back movement to update target register
}
musst_vars = first_iter_vars
# allows to replace alias 'MOTOR_CHANNEL' in 'ftomo_wo_gap' program with channel associated to motor 'CH#'
template_replacement= { "$MOTOR_CHANNEL$": "CH%d"%self.mot_chan}
template_replacement = {"$MOTOR_CHANNEL$": "CH%d" % self.mot_chan}
# MUSST acquisition master
musst_master = MusstAcquisitionMaster(self.card,
program = 'ftomo_wo_gap.mprg',
program_start_name = 'SWEEP',
program_abort_name = 'SWEEPCLEAN',
program_template_replacement=template_replacement,
vars=musst_vars)
# MUSST acquisition slave
musst_slave = MusstAcquisitionSlave(self.card, store_list=self.storelist) # store encoder and timer data
log_info(self,"prepare_sweep() leaving")
musst_master = MusstAcquisitionMaster(
self.card,
program="ftomo_wo_gap.mprg",
program_start_name="SWEEP",
program_abort_name="SWEEPCLEAN",
program_template_replacement=template_replacement,
vars=musst_vars,
)
# MUSST acquisition slave
musst_slave = MusstAcquisitionSlave(
self.card, store_list=self.storelist
) # store encoder and timer data
log_info(self, "prepare_sweep() leaving")
return musst_master, musst_slave
def prepare_soft(self, exposure_time, nb_points, scan_time=None):
"""
Configures musst for soft continuous and step scan.
Uses RUNCT musst command.
"""
log_info(self,"prepare_soft() entering")
log_info(self, "prepare_soft() entering")
self.tmrcfg = self.card.TMRCFG
if scan_time is not None:
self.set_timer_clock(scan_time)