Commit 7d55accc authored by Emmanuel Papillon's avatar Emmanuel Papillon
Browse files

add shutter calibration scan and fit to shutter object

parent 1a80708a
#!/usr/bin/env python
from __future__ import print_function, division
import sys, numpy as np, pylab as pl
def fitshutter( x, y, exptime, plot_title=None ):
"""
x = time data
y = signal data
exptime = programmed exposure time
plot_title = None for quiet, a title for your plot
"""
yc, xc, s, e = fitshutter_all( x, y )
opening_time = xc[s>0][0]
opening_time_err = e[s>0][0]
closers = xc[s<0]
cl_errs = e[s<0]
closing_time = closers[ closers>exptime ][0] - exptime
closing_time_err = cl_errs[ closers>exptime ][0]
if plot_title is not None:
fitshutterplot( x, y, xc, yc, s, opening_time, closing_time,
exptime, plot_title)
return opening_time, closing_time, opening_time_err, closing_time_err
def fitshutter_all( x, y ):
"""
Finds all the cutting points in a shutter scan
returns: y_cut, xcuts[N], signs[N], xerrs[N]
"""
print(x)
print(y)
first_cut = 0.5*( y.max() + y.min() ) # mid-height
beam_on = y[ y > first_cut ] # y-vals in beam
beam_off = y[ y < first_cut ] # y-vals in dark
avg_on = beam_on.mean()
avg_off = beam_off.mean()
y_cut = 0.5*(avg_on + avg_off) # cut at middle
beam = 1*(y > first_cut) # mask cut on vs off
crossing = (beam[1:]-beam[:-1])!=0 # spikes where switch on/off
xlo = x[:-1][crossing]*1. # select points at cuts
xhi = x[1:][crossing]*1.
ylo = y[:-1][crossing]*1.
yhi = y[1:][crossing]*1.
# linear interpolation in the gaps
grads = (xhi - xlo)/(yhi - ylo)
x_cut = xlo + (y_cut - ylo)*grads
signs = np.sign(yhi - ylo)
cuterr = np.sqrt(beam_on.std()**2 + beam_off.std()**2)/2
xerrs = cuterr * abs(grads)
return y_cut, x_cut, signs, xerrs
def fitshutterplot( x, y, xc, yc, s, o, c, expt, fname ):
""" Makes a nice plot for the shutter fitting """
import pylab as pl
pl.figure()
pl.subplot(211)
pl.plot( x, y, "+-", label="signal" )
pl.plot( xc[s>0], [yc,]*len(xc[s>0]), "og", label="Up" )
pl.plot( xc[s<0], [yc,]*len(xc[s<0]), "or", label="Down" )
pl.plot( [ o, expt+c] , [yc, yc], "ko", ms=10, label="Chosen" )
pl.plot( [0, o] , [yc , yc], "k-")
pl.plot( [expt, expt+c] , [yc , yc],"k-")
pl.text(0,yc," cut=%.1f"%(yc))
pl.legend(loc=0)
pl.title( fname )
ax = pl.subplot(223)
pl.title("Opening")
pl.plot( x, y, "+-", label="signal" )
pl.plot( xc[s>0], [yc,]*len(xc[s>0]), "og", label="Up" )
pl.plot( xc[s<0], [yc,]*len(xc[s<0]), "or", label="Down" )
pl.plot( [0, o] , [yc , yc], "k-")
pl.plot( [expt, expt+c] , [yc , yc],"k-")
pl.text(0,yc*0.4,"%.6f"%(o))
pl.plot( [0,0], [0,yc*2], "k-" )
pl.xlim( -o, o*2 )
ax.ticklabel_format( useOffset = False )
ax = pl.subplot(224)
pl.title("Closing")
pl.plot( x, y, "+-", label="signal" )
pl.plot( xc[s>0], [yc,]*len(xc[s>0]), "og", label="Up" )
pl.plot( xc[s<0], [yc,]*len(xc[s<0]), "or", label="Down" )
pl.plot( [0, o] , [yc , o], "k-")
pl.plot( [expt, expt+c] , [yc , yc],"k-")
pl.plot( [expt,expt], [0,yc*2], "k-" )
pl.text(expt,yc*0.4,"%.6f"%(c))
pl.xlim( expt-c, expt+c*2)
ax.ticklabel_format( useOffset = False )
pl.show()
def usage():
print("Usage: %s filename:exptime [filename:exptime ...]"%(sys.argv[0]))
print(" eg: %s /data/id11/nanoscope/Commissioning/bliss/seb2/pump_on.npy:10"%(
sys.argv[0]))
def main():
import sys
if len(sys.argv)==1:
usage()
for arg in sys.argv[1:]:
try:
fname, expt = arg.split(":")
expt = float(expt)
data = np.load( fname )
except:
usage()
raise
o, c, oe, ce = fitshutter( data['time'],
data['mon'],
expt,
plot_title=fname)
print("%s Open: %f +/- %f , Close %f +/- %f"%( fname, o, oe, c, ce ))
if __name__=="__main__":
main()
from bliss.config.settings import HashObjSetting
from bliss.controllers.ct2.client import CT2CounterTimer
from bliss import current_session
from .fshuttercalibration import FastShutterCalibration
class FastShutter:
def __init__(self, name, config):
......@@ -107,28 +107,3 @@ class FastShutter:
def measure(self):
self.calibration.measure()
class FastShutterCalibration(object):
def __init__(self, shutter, config):
self.shutter = shutter
self.__musst = config["musst"]
self.__p201 = config["p201_card"]
self.__diode_name = config["p201_diode_name"]
def __check_counters(self):
self.__diode = None
self.__timer = None
for counter in self.__p201.counters:
if counter.name == self.__diode_name:
self.__diode = counter
elif isinstance(counter, CT2CounterTimer):
self.__timer = counter
if self.__diode is None:
raise RuntimeError(f"Could find {self.__diode_name} counter on {self.__p201.name}")
if self.__timer is None:
raise RuntimeError(f"Could not find timer on {self.__p201.name}")
def measure(self):
self.__check_counters()
import numpy
from bliss.scanning.chain import AcquisitionChain
from bliss.scanning.scan import Scan
from bliss.scanning.acquisition.musst import MusstAcquisitionMaster
from bliss.scanning.acquisition.calc import CalcChannelAcquisitionSlave
from bliss.scanning.channel import AcquisitionChannel
from bliss.scanning.acquisition.ct2 import CT2AcquisitionMaster,CT2CounterAcquisitionSlave
from bliss.controllers.ct2.device import AcqMode
from bliss.controllers.ct2.client import CT2CounterTimer
from .fitshutter import *
_MUSST_PRG = """
UNSIGNED START_DELAY
UNSIGNED GATEWIDTH
PROG SHUTTER
CTSTOP TIMER
CTRESET TIMER
BTRIG 0 // Close shutter
STORELIST TIMER
EMEM 0 AT 0
@TIMER = START_DELAY
CTSTART TIMER
AT TIMER DO STORE ATRIG BTRIG
@TIMER = $TIMER + GATEWIDTH
AT TIMER DO BTRIG
ENDPROG
"""
class FastShutterCalibration(object):
def __init__(self, shutter, config):
self.__shutter = shutter
self.__musst = config["musst"]
self.__p201 = config["p201_card"]
self.__diode_name = config["p201_diode_name"]
self.__data = None
@property
def data(self):
return self.__data
def __check_counters(self):
self.__diode = None
self.__timer = None
for counter in self.__p201.counters:
if counter.name == self.__diode_name:
self.__diode = counter
elif isinstance(counter, CT2CounterTimer):
self.__timer = counter
if self.__diode is None:
raise RuntimeError(f"Could find {self.__diode_name} counter on {self.__p201.name}")
if self.__timer is None:
raise RuntimeError(f"Could not find timer on {self.__p201.name}")
def measure(self, open_time=1., acq_time=100e-6, delay=200e-3):
self.__check_counters()
chain = AcquisitionChain()
musst_time_factor = musst.get_timer_factor()
musst_master = MusstAcquisitionMaster(self.__musst, program_data=_MUSST_PRG,
program_start_name="SHUTTER",
vars={'START_DELAY':int(delay*musst_time_factor),
'GATEWIDTH':int(open_time*musst_time_factor)})
npoints = int((delay + open_time + 2*self.__shutter.shutter_time)/acq_time)
p201_master = CT2AcquisitionMaster(self.__p201,
npoints=npoints,
acq_mode=AcqMode.ExtTrigSingle,
acq_expo_time=acq_time-1e-6,
acq_point_period=acq_time)
chain.add(musst_master,p201_master)
acq_device = CT2CounterAcquisitionSlave(self.__diode, self.__timer,
npoints=npoints, count_time=acq_time)
chain.add(p201_master, acq_device)
#calc
class _CumSum(object):
def __init__(self, cnt_name):
self.previous_time = 0
self.cnt_name = cnt_name
def __call__(self, sender, data_dict):
data = data_dict.get(self.cnt_name)
if data is None:
return {}
data = numpy.cumsum(data) + self.previous_time
self.previous_time = data[-1]
return {'time': data}
calc_device = CalcChannelAcquisitionSlave('time_cumsum',(acq_device,),
_CumSum(self.__timer.name),
(AcquisitionChannel('time',numpy.float,()),))
chain.add(p201_master, calc_device)
self.__shutter.mode = "BTRIG"
mux = self.__shutter.multiplexer
mux.switch('TRIGGER_MODE','MUSST')
mux.switch('MUSST','BTRIG',synchronous=True)
scan = Scan(chain, name='fshutter_measure')
print("Running fshutter_measure scan ...")
scan.run()
print("Scan finished.")
data= scan.get_data()
self.measurement_fit(data, open_time)
def measurement_fit(self, data, exp_time):
data_timer = data[self.__timer.name]
data_diode = data[self.__diode.name]
o, c, oe, ce = fitshutter(data_timer, data_diode, exp_time, plot_title="Fast Shutter Calibration")
print("Shutter Open: %f +/- %f , Close %f +/- %f"%(o, oe, c, ce ))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment