This commit is contained in:
x07maop
2015-08-24 08:28:12 +02:00
parent 92a7e39edf
commit bf5427bd04
28 changed files with 768 additions and 435 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_data_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,40), 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,59 @@
"""
Function fitting and peak search with mathutils.py
!!! Require commons-math3-*.jar in the extensions folder!!!
"""
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,38 @@
"""
Multiple Gaussians peak search
!!! Require commons-math3-*.jar in the extensions folder!!!
"""
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,69 @@
"""
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):
save_data_item(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:
save_data_item(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]
dims = [0,0,0,0]
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, dims)
for row in table:
save_table_item(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,36 @@
"""
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()
#In order to use in a scan we need to implement Readable/Writable interfaces.
#A wripper device can be created as:
channel = create_channel_device(channel_name, 'd')
lscan(channel, ai2, 0, 10, 0.1)
#Or else we can use ch.psi.pshell.epics
import ch.psi.pshell.epics.ChannelDouble as ChannelDouble
channel = ChannelDouble("My Channel", channel_name)
channel.initialize()
lscan(channel, ai2, 0, 10, 0.1)

View File

@@ -0,0 +1,21 @@
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_image_source(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)

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

@@ -0,0 +1,175 @@
import random
import ch.psi.pshell.device.DummyMotor as DummyMotor
import ch.psi.pshell.device.DummyRegister as DummyRegister
import ch.psi.pshell.device.RegisterBase as RegisterBase
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)
#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_image_source(RegisterMatrixSource("src1", im1), True)
add_image_source(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()