This commit is contained in:
x03daop
2015-09-01 10:09:12 +02:00
parent e104c934af
commit f3c57cbe5a
26 changed files with 789 additions and 11 deletions

View File

@@ -0,0 +1,25 @@
"""
Demonstrate the use of Line Scan: one or multiple positioners move together linearly.
"""
#This optional preference limits the displayed plots
#set_preference(Preference.ENABLED_PLOTS, [ai1, ai2,])
#This optional preference displays wf1 as a 1d plot at each scan point, instead of a matrix plot
#set_preference(Preference.PLOT_TYPES, {wf1:1})
#Execute the scan: 200 steps, a1 from 0 to 40
a= lscan(ao1, (ai1,ai2,wf1,im1), 0, 40, 200, 0.01)
#Alternative: Steps of size 0.1, a1 from 0 to 40
#a= lscan(ao1, (ai1,ai2,wf1,im1), 0, 40, 0.5, 0.01)
#2 positioners moving together in 200 steps, a1 from 0 to 40 and a2 from 0 to 100
#a= lscan((ao1,ao2), (ai1,ai2,wf1,im1), (0, 0), (40, 100), 200, 0.01)
#Setting attributes to the scan group
path = get_current_group()
set_attribute(path, "AttrString", "Value")
set_attribute(path, "AttrInteger", 1)
set_attribute(path, "AttrDouble", 2.0)
set_attribute(path, "AttrBoolean", True)

View File

@@ -0,0 +1,20 @@
"""
Demonstrate use of scan callbacks to trigger a detector at falling edge.
"""
def BeforeReadout():
ao1.write(1)
ao1.write(0)
#Example with an epics direct channel access
#caput("CHANNEL_NAME", 1)
#caput("CHANNEL_NAME", 0)
index=0
def AfterReadout():
global index
print "Aquired frame: " + str(index)
index=index+1
a= lscan((m1,m2), (ai1, ai2), (0,0), (4,8), steps=20, latency = 0.01, before_read=BeforeReadout, after_read=AfterReadout)

View File

@@ -0,0 +1,21 @@
"""
Processing and plotting scan data.
"""
ao1.write(0.0)
scan1= lscan(ao1, (ai1,ai2,wf1), 0, 40, 40, 0.01, False, "Scan 1")
scan2= lscan(ao1, (ai1,ai2,wf1), 0, 40, 40, 0.01, False, "Scan 2")
from operator import add
result = map(add, scan1.getReadable(0), scan2.getReadable(0))
#Alternative:
#result=[]
#for i in range(len(scan1.records)):
# result.append(scan1.records[i].values[0]+scan2.records[i].values[0])
plot(result)
print result

View File

@@ -0,0 +1,12 @@
"""
Area Scan: Multiple positioners, each one is one dimension.
"""
#This optional preference displays wf1 as a 1d plot at each scan point, instead of a matrix plot
#set_preference(Preference.PLOT_TYPES, {wf1:1})
#The second sensor is an array. In the plot window it is overwritten in every same x position.
#The data window never displays 3d data, but the 3d data can be accesses during the scan in the Data tab.
ascan((m1,m2), (ai1,wf1), (0.0,0.0), (2.0,1.0), (20,20))

View File

@@ -0,0 +1,12 @@
"""
Demonstrate use of Relative Line Scan.
The arguments start and end are relative to the current position.
After the scan the positioner(s) move back to the initial position.
"""
print "Initial position = " + str(m1.position)
a= lscan(m1, (ai1,ai2,wf1,im1), start = -2, end =2, steps = 20, relative = True)
print "Final position = " + str(m1.position)

View File

@@ -0,0 +1,22 @@
"""
Demonstrate use of Vector Scan: one or multiple positioners set according to a position vector.
"""
#1D vector scan, plot to 1D Vector tab
vector = [ 1, 3, 5, 10, 25, 40, 45, 47, 49]
a= vscan(ao1,(ai1,ai2),vector,False, 0.5, context = "1D Vector")
#2D vector scan, plot to 2D Vector tab
vector = [ [1,1] , [1,2] , [1,3] , [1,4] ,
[1.5,2.5] ,
[2,1] , [2,2] , [2,3] , [2,4] ,
[2.5,2.5] ,
[3,1] , [3,2] , [3,3] , [3,4] ]
a= vscan((m1,m2),(ai1,ai2),vector,False, 0.1, context = "2D Vector")

View File

@@ -0,0 +1,58 @@
"""
Function fitting and peak search with mathutils.py
"""
from mathutils import fit_polynomial,fit_gaussian, fit_harmonic, calculate_peaks
from mathutils import PolynomialFunction, Gaussian, HarmonicOscillator
import math
start = 0
end = 10
step_size = 0.1
result= lscan(ao1,ai1,start,end,[step_size,],0.01)
readable = result.getReadable(0)
positions = result.getPositions(0)
def get_function_data(function, start, end, resolution):
ret = []
for x in frange(start, end, resolution, True):
fit_polinomial.append(function.value(x))
pars_polynomial = (a0, a1, a2, a3, a4, a5, a6) = fit_polynomial(readable, positions, 6)
fitted_polynomial_function = PolynomialFunction(pars_polynomial)
print pars_polynomial
(normalization, mean, sigma) = fit_gaussian(readable, positions, True)
fitted_gaussian_function = Gaussian(normalization, mean, sigma)
print (normalization, mean, sigma)
(amplitude, angular_frequency, phase) = fit_harmonic(readable, positions)
fitted_harmonic_function = HarmonicOscillator(amplitude, angular_frequency, phase)
print (amplitude, angular_frequency, phase)
resolution = step_size/100
fit_polinomial = []
fit_gaussian = []
fit_harmonic = []
for x in frange(start,end,resolution, True):
fit_polinomial.append(fitted_polynomial_function.value(x))
fit_gaussian.append(fitted_gaussian_function.value(x))
fit_harmonic.append(fitted_harmonic_function.value(x))
x = frange(start, end+resolution, resolution)
peaks = calculate_peaks(fitted_polynomial_function)
plots = plot([readable, fit_polinomial, fit_gaussian, fit_harmonic] ,
["data", "polinomial", "gaussian", "harmonic"], xdata = [positions,x,x,x], context="Data")
for p in peaks:
print "Max: " + str(p)
plots[0].addMarker(p, None, "Max=" + str(round(p,2)), None)
import java.awt.Color
plots[0].addMarker(mean, None, "Mean=" + str(round(mean,2)), java.awt.Color.LIGHT_GRAY)

View File

@@ -0,0 +1,37 @@
"""
Multiple Gaussians peak search
"""
from mathutils import estimate_peak_indexes, fit_gaussians, create_fit_point_list
start = 0
end = 50
step_size = 0.2
result= lscan(ao1,ai1,start,end,[step_size,])
readable = result.getReadable(0)
positions = result.getPositions(0)
threshold = (min(readable) + max(readable))/2
min_peak_distance = 5.0
peaks = estimate_peak_indexes(readable, positions, threshold, min_peak_distance)
print "Peak indexes: " + str(peaks)
print "Peak x: " + str(map(lambda x:positions[x], peaks))
print "Peak y: " + str(map(lambda x:readable[x], peaks))
gaussians = fit_gaussians(readable, positions, peaks)
plots = plot([readable],["sin"],[positions], context="Data" )
for i in range(len(peaks)):
peak = peaks[i]
(norm, mean, sigma) = gaussians[i]
if abs(mean - positions[peak]) < min_peak_distance:
print "Peak -> " + str(mean)
plots[0].addMarker(mean, None, "N="+str(round(norm,2)), None)
else:
print "Invalid gaussian fit: " + str(mean)

View File

@@ -0,0 +1,26 @@
"""
Using pseudo-device to :
- Add calculations to scan data.
- Execute logic during scan
"""
class Clock(Readable):
def read(self):
return time.clock()
class Averager(Readable):
def read(self):
arr = wf1.take() #Gets the CACHED waveform
return reduce(lambda x, y: x + y, arr) / len(arr)
class Positioner(Writable):
def write(self,pos):
print "Step = " + str(pos)
averager=Averager()
clock=Clock()
positioner=Positioner()
a= lscan((ao1,positioner),(ai2,wf1,averager,clock),(0,0),(40,20),20,0.1)

View File

@@ -0,0 +1,25 @@
"""
Create a device listener to interrupt the scan
"""
import java.lang.InterruptedException
class Listener (DeviceListener):
def onStateChanged(self, device, state, former):
pass
def onValueChanged(self, device, value, former):
if value > 1.01:
print "Value over limit-> aborting"
abort()
listener = Listener()
ai1.addListener(listener)
try:
lscan(ao1, (ai1), 0, 40, 200, 0.01)
except java.lang.InterruptedException:
print "Aborted"
finally:
ai1.removeListener(listener)

View File

@@ -0,0 +1,22 @@
"""
Settign script parameters and return value
"""
#Providing an array of global variables
#run ("tutorial/11_ParametersAndReturn", {"start":10.0, "end":50.0, "step":40})
#Providing the locals dictionary
# The parameters are not set as globals, and nor script definitions
#run ("tutorial/11_ParametersAndReturn", locals={"start":10.0, "end":50.0, "step":40})
#Setting sys.argv:
#run ("tutorial/11_ParametersAndReturn", [10.0, 50.0, 40])
#start = sys.argv[0]
#end = sys.argv[1]
#step = sys.argv[2]
a= lscan(ao1, ai1, start, end, step, 0.1)
a.getReadable(0)
set_return(a.getReadable(0))

View File

@@ -0,0 +1,33 @@
"""
Manual scan: Manually setting positioners and reading back sensors, but still using
the standard data handling and plotting of built-in scans.
"""
MOTOR_RANGE = (0.0, 8.0)
OUTPUT_SETPOINTS = (1.0, 2.0, 3.0)
FIXED_X = True
writables_names = [m1.getName(), ao1.getName()]
readable_names = [ai1.getName(), ai2.getName()]
start = [ MOTOR_RANGE[0] if FIXED_X else -1, OUTPUT_SETPOINTS[0]]
stop = [ MOTOR_RANGE[1] if FIXED_X else -1, OUTPUT_SETPOINTS[-1]]
steps = [int(MOTOR_RANGE[1]-MOTOR_RANGE[0]), len(OUTPUT_SETPOINTS)-1 if FIXED_X else -1]
scan = ManualScan(writables_names, readable_names ,start, stop, steps)
#This option is to plot the foe each output value one 1D series, intead of all in a matrix plot
set_preference(Preference.PLOT_TYPES, {ai1:1,ai2:1})
scan.start()
m1.setSpeed(10.0)
for setpoint1 in frange(MOTOR_RANGE[0], MOTOR_RANGE[1], 1.0, True):
m1.move(setpoint1)
for setpoint2 in OUTPUT_SETPOINTS:
ao1.write(setpoint2)
scan.append ([setpoint1, setpoint2], [m1.read(), ao1.read()], [ai1.read(), ai2.read()])
scan.end()

View File

@@ -0,0 +1,19 @@
"""
Custom plot: Example of creating a 1D plot for a 2D scan where each scanned row is a series
"""
#Setting the 1d preference would create in the place of the matrix plot, a 1d plot where
#each scanned column is a series
#set_preference(Preference.PLOT_TYPES, {'ai1':1})
p = plot(None, context="1d Plot")[0]
def AfterReadout(record, scan):
if record.setpoints[1] == scan.getStart()[1]:
p.addSeries(LinePlotSeries(str(record.positions[0])))
p.getSeries(p.numberOfSeries-1).appendData(record.positions[1], record.values[0])
ascan((ao1,ao2), (ai1), (0,10), (20,30), (20,20), 0.1, after_read=AfterReadout)

View File

@@ -0,0 +1,68 @@
"""
Data Manipulation: Using the data access API to generate and retrieve data
"""
#Creating a 1D dataset from an array
path="group/data1"
data1d = [1.0, 2.0, 3.0, 4.0, 5.0]
save_dataset(path, data1d)
#Reading ii back
read =load_data(path)
print read.tolist()
assert data1d==read.tolist()
plot(read)
#Creating a 2D dataset from an array with some attributes
data2d = [ [1.0, 2.0, 3.0, 4.0, 5.0], [2.0, 3.0, 4.0, 5.0, 6.0, ], [3.0, 4.0, 5.0, 6.0, 7.0]]
path="group/data2"
save_dataset(path, data2d)
set_attribute(path, "AttrString", "Value")
set_attribute(path, "AttrInteger", 1)
set_attribute(path, "AttrDouble", 2.0)
set_attribute(path, "AttrBoolean", True)
#Reading it back
read =load_data(path)
print read.tolist()
plot(read)
#Creating a 3D dataset from an array
data3d = [ [ [1,2,3,4,5], [2,3,4,5,6], [3,4,5,6,7]], [ [3,2,3,4,5], [4,3,4,5,6], [5,4,5,6,7]]]
path="group/data3"
save_dataset(path, data3d)
#Reading it back
read =load_data(path,0)
print read.tolist()
read =load_data(path,1)
print read.tolist()
#Creating a INT dataset adding elements one by one
path = "group/data4"
create_dataset(path, 'i')
for i in range(10):
append_dataset(path,i)
#Creating a 2D data FLOAT dataset adding lines one by one
path = "group/data5"
create_dataset(path, 'd', False, (0,0))
for row in data2d:
append_dataset(path, row)
#Creating a Table (compund type)
path = "group/data6"
names = ["a", "b", "c", "d"]
types = ["d", "d", "d", "[d"]
lenghts = [0,0,0,5]
table = [ [1,2,3,[0,1,2,3,4]],
[2,3,4,[3,4,5,6,7]],
[3,4,5,[6,7,8,9,4]] ]
create_table(path, names, types, lenghts)
for row in table:
append_table(path, row)
flush_data()
#Read it back
read =load_data(path)
print read

View File

@@ -0,0 +1,51 @@
"""
Using Pararellization API to execute tasks concurrently
"""
import traceback
#Simple parallization
def task1():
m1.moveRel(1.0)
return m1.getPosition()
def task2():
m2.moveRel(1.0)
return m1.getPosition()
def task3():
return ai1.read()
ret = parallelize(task1, task2, task3)
print ret
#Fork amd join
ret = fork(task1, task2, task3)
print ai1.read()
ret = join(ret)
print ret
#Functions with parameters
def moveRelative(motor, step):
print "Moving " + motor.getName() + " step = " + str(step)
motor.moveRel(step)
return motor.getPosition()
ret = parallelize((moveRelative,(m1,-2)), (moveRelative,(m2,-2)))
print ret
#Exception in parallel task is thrown back to script
try:
parallelize((moveRelative,(ai1,1)), (moveRelative,(ai2,1)))
except:
print "Ok, caught exception:"
traceback.print_exc()

View File

@@ -0,0 +1,15 @@
"""
Example on running simultaneous scans. They should not manipulate same writables
"""
def scan1():
print "scan1"
return lscan(ao1, ai1, 0, 40, 20, 0.1, context = "scan1")
def scan2():
print "scan2"
return lscan(ao2, wf1, 0, 40, 20, 0.1, context = "scan2")
parallelize(scan1, scan2)

View File

@@ -0,0 +1,31 @@
"""
EPICS direct channel access.
EPICS devices implemented are included in PShell, package ch.psi.pshell.epics.
However direct channel access builtin functions are available.
"""
channel_name = "TESTIOC:TESTCALCOUT:Output"
#reading/writing to a channel
print (caget(channel_name))
caput(channel_name, 0.0)
#Put with no wait
caput(channel_name, 0.0)
print (caget(channel_name))
#waiting for a channel valur
cawait(channel_name, 0.0, timeout = 10.0)
#If many IO it is better to keep the same CA connection
channel = Channel(channel_name, 'd')
for i in range(100):
print channel.get()
lscan(channel, (ai2,channel), 0, 10, 0.1)
#The channel class implements Readable and Writable and therefore can be used in scans
lscan(channel, ai2, 0, 10, 0.1)
#Or else we can use a Device
import ch.psi.pshell.epics.ChannelDouble as ChannelDouble
channel = ChannelDouble("My Channel", channel_name)
channel.initialize()

View File

@@ -0,0 +1,27 @@
"""
Demonstrate creation of an image filter, applying it to a source and creating a new source
based on the filter.
"""
import ch.psi.pshell.imaging.Filter as Filter
from ch.psi.pshell.imaging.Utils import *
class MyFilter(Filter):
def process(self, image, data):
image = grayscale(image)
image = blur(image)
image = sobel(image)
return image
#Setting the filter to a source
src1.setFilter(MyFilter())
#Creating a new source with the filter
src2.setFilter(None)
add_device(MyFilter("f1"), True)
#f1.passive = True
src2.addListener(f1)

View File

@@ -0,0 +1,15 @@
"""
Demonstrate the use of Continuous Scan Scan: a Linear Scan with continuous motor move and
sampling on the fly.
"""
#A single motor at current speed
a= cscan(m1, (ai1,ai2), -2, 3 , steps=100, relative=True)
#A single motor in a given time
a= cscan(m1, (ai1,ai2), -2.0, 3.0, steps=100 ,time = 4.0, relative=True)
#Multiple motors in a given time
a= cscan((m1, m2), (ai1,ai2), (-2.0, -3), (3.0, 5.0), steps=100,time = 4.0, relative=True)

View File

@@ -0,0 +1,46 @@
"""
Interlocks: example on creating and installing device interlock rules.
"""
class MyInterlock1 (Interlock):
#Motor and Positioners
def __init__(self):
Interlock.__init__(self, (m1, p1))
def check(self, (m, p)):
if p<500 and (m<5 and m>4):
return False
return True
interlock1 = MyInterlock1()
"""
#Motor group
class MyInterlock2(Interlock):
def __init__(self):
Interlock.__init__(self, (mg1, p1))
def check(self, ((m1,m2), p)):
if p<500 and (m1>4 and m2>4):
return False
return True
interlock2 = MyInterlock2()
"""
"""
#Discrete Positioner
class MyInterlock3(Interlock):
def __init__(self):
Interlock.__init__(self, (dp1, p1))
def check(self, (dp, p)):
if p<500 and dp=="Out":
return False
return True
#interlock3 = MyInterlock3()
"""

195
script/tutorial/devices.py Normal file
View File

@@ -0,0 +1,195 @@
import random
import ch.psi.pshell.device.DummyMotor as DummyMotor
import ch.psi.pshell.device.DummyRegister as DummyRegister
import ch.psi.pshell.device.DummyPositionable as DummyPositionable
import ch.psi.pshell.device.RegisterBase as RegisterBase
import ch.psi.pshell.device.MotorGroupBase as MotorGroupBase
import ch.psi.pshell.device.MotorGroupDiscretePositioner as MotorGroupDiscretePositioner
import ch.psi.pshell.device.ReadonlyRegisterBase as ReadonlyRegisterBase
import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterArray as ReadonlyRegisterArray
import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterMatrix as ReadonlyRegisterMatrix
import ch.psi.pshell.imaging.RegisterMatrixSource as RegisterMatrixSource
####################################################################################################
# Simulated Devices
####################################################################################################
class AnalogOutput(RegisterBase):
def doRead(self):
return self.val if hasattr(self, 'val') else 0.0
def doWrite(self, val):
self.val = val
class AnalogInput(ReadonlyRegisterBase):
def doRead(self):
time.sleep(0.01)
self.val = to_array(self.calc(), 'd')
return self.val
class Waveform(ReadonlyRegisterBase, ReadonlyRegisterArray):
def doRead(self):
time.sleep(0.01)
self.val = to_array(self.calc(), 'd')
return self.val
def getSize(self):
return len(self.take(-1)) #only reads if cache is None
class Image(ReadonlyRegisterBase, ReadonlyRegisterMatrix):
def doRead(self):
time.sleep(0.01)
self.val = to_array(self.calc(), 'd')
return self.val
def getWidth(self):
return len(self.take(-1)[0])
def getHeight(self):
return len(self.take(-1))
class Random(AnalogInput):
def calc(self):
return random.random()
class SinusoidSample(AnalogInput):
def calc(self):
self.x = self.x + 0.1 if hasattr(self, 'x') else 0.0
noise = (random.random() - 0.5) / 10.0
return math.sin(self.x) + noise
class SinusoidTime(AnalogInput):
def calc(self):
noise = (random.random() - 0.5) / 10.0
return math.sin(time.time()) + noise
class SinusoidWaveform(Waveform):
def calc(self):
ret = []
x = random.random()
for i in range (20):
ret.append(math.sin(x))
x = x + 0.1
return ret
class SinusoidImage(Image):
def calc(self):
(width, height) = (200, 100)
ret = []
x = random.random();
base = []
for i in range (width):
base.append( math.sin(x))
x = x + 0.05
for i in range (height):
noise = (random.random() - 0.5)/5.0
ret.append([x+noise for x in base])
return ret
#Defintion
add_device(DummyMotor("m1"), True)
add_device(DummyMotor("m2"), True)
add_device(DummyRegister("reg1",3), True)
add_device(AnalogOutput("ao1"), True)
add_device(AnalogOutput("ao2"), True)
add_device(SinusoidSample("ai1"), True)
add_device(SinusoidTime("ai2"), True)
add_device(Random("ai3"), True)
add_device(SinusoidWaveform("wf1"), True)
add_device(SinusoidImage("im1"), True)
add_device(DummyPositionable("p1"),True)
add_device(MotorGroupBase("mg1", m1, m2), True)
add_device(MotorGroupDiscretePositioner("dp1", mg1), True)
#Initial Configuration
p1.config.minValue = 0.0 #Not persisted
p1.config.maxValue = 1000.0
if dp1.config.positions is None:
dp1.config.positions = ["Park","Ready","Out","Clear"]
dp1.config.motor1 = ["0.0","4.0","8.0" ,"0.0"]
dp1.config.motor2 = ["0.0","5.0","3.0" ,"NaN"]
dp1.config.save()
dp1.initialize()
#Update
m1.setMonitored(True)
m2.setMonitored(True)
####################################################################################################
# Simple Readable / Writable objects can be created and used in scans
####################################################################################################
class WritableScalar(Writable):
def write(self, value):
pass
class ReadableScalar(Readable):
def read(self):
return random.random()
class ReadableWaveform(ReadableArray):
def getSize(self):
return 20
def read(self):
ret = []
for i in range (self.getSize()):
ret.append(random.random())
return ret
class ReadableImage(ReadableMatrix):
def read(self):
ret = []
for i in range (self.getHeight()):
ret.append([random.random()] * self.getWidth())
return to_array(ret, 'd')
def getWidth(self):
return 80
def getHeight(self):
return 40
ws1 = WritableScalar()
rs1 = ReadableScalar()
rw1 = ReadableWaveform()
ri1 = ReadableImage()
####################################################################################################
# Imaging
####################################################################################################
configured = os.path.exists(Device.getConfigFileName("src1"))
add_device(RegisterMatrixSource("src1", im1), True)
add_device(RegisterMatrixSource("src2", ri1), True)
#Some configuration for so the imaging will work out of the box
if not configured:
import ch.psi.pshell.imaging.Colormap
src1.config.dataPolling = 100
src1.config.colormapAutomatic = True
src1.config.colormap = ch.psi.pshell.imaging.Colormap.Temperature
src1.config.save()
src2.config.dataPolling = 100
src2.config.colormapAutomatic = True
src2.config.save()