This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#Thu Aug 02 10:45:31 CEST 2018
|
||||
#Tue Nov 06 14:31:07 CET 2018
|
||||
autoSaveScanData=true
|
||||
commandExecutionEvents=false
|
||||
createSessionFiles=true
|
||||
dataLayout=default
|
||||
dataPath={data}/{year}_{month}/{date}/{date}_{time}_{name}
|
||||
@@ -9,6 +10,7 @@ dataScanPreserveTypes=false
|
||||
dataScanReleaseRecords=false
|
||||
dataServerPort=5573
|
||||
depthDimension=0
|
||||
generateCommandExecutionEvents=true
|
||||
hostName=
|
||||
instanceName=Dev
|
||||
logDaysToLive=50
|
||||
@@ -17,7 +19,7 @@ logLevelConsole=Off
|
||||
logPath={logs}/{date}_{time}
|
||||
notificationLevel=Off
|
||||
scanStreamerPort=5563
|
||||
serverEnabled=true
|
||||
serverEnabled=false
|
||||
serverPort=8080
|
||||
simulation=false
|
||||
terminalEnabled=false
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
dispatcher=ch.psi.pshell.bs.Provider|tcp://localhost:9999|||
|
||||
cam_server_local=ch.psi.pshell.bs.PipelineServer|localhost:8889|||true
|
||||
cam_server=ch.psi.pshell.bs.PipelineServer|sf-daqsync-01:8889|||true
|
||||
#ts1=ch.psi.pshell.epics.GenericArray|TESTIOC:TESTWF2:MyWF|||
|
||||
#monit_cam=ch.psi.pshell.imaging.MjpegSource|http://axis-accc8e9cc87b.psi.ch/axis-cgi/mjpg/video.cgi?camera=2 reopen||-200|false
|
||||
#tststr=ch.psi.pshell.epics.ChannelString|TESTIOC:TESTSINUS:SinCalc|||
|
||||
#stream=ch.psi.pshell.bs.Stream|<dispatcher> #false:boolean|||
|
||||
#scalar=ch.psi.pshell.bs.Scalar|stream Int8Scalar|||
|
||||
#cam=ch.psi.pshell.bs.StreamCamera|"tcp://gfa-lc6-64:10100" 1|||true
|
||||
@@ -9,7 +11,8 @@ cam_server=ch.psi.pshell.bs.PipelineServer|sf-daqsync-01:8889|||true
|
||||
#rr=RotationReadback|TESTIOC:TESTCALCOUT:Output|||true
|
||||
#rp=ch.psi.pshell.epics.Positioner|TESTIOC:TESTCALCOUT:Input null rr|||
|
||||
#webcam=ch.psi.pshell.webcam.Webcam|:2|||true
|
||||
#det=ch.psi.pshell.epics.AreaDetector|13SIM1|||false
|
||||
$det=ch.psi.pshell.epics.AreaDetector|13SIM1|||true
|
||||
deting=ch.psi.pshell.imaging.CameraSource|det||-500|
|
||||
#PiezoRoll1=ch.psi.pshell.epics.ControlledVariable|"TESTIOC:TESTCALCOUT:Input" TESTIOC:TESTCALCOUT:Output false|||true
|
||||
#$ser=ch.psi.pshell.serial.TcpDevice||||
|
||||
#$beam_ok=ch.psi.pshell.epics.ChannelString|CHANNEL|||
|
||||
@@ -57,6 +60,11 @@ sin=ch.psi.pshell.epics.ChannelDouble|TESTIOC:TESTSINUS:SinCalc 3 true|||true
|
||||
#average=ch.psi.pshell.device.Averager|sin||2000|
|
||||
#isin=ch.psi.pshell.epics.ChannelInteger|TESTIOC:TESTSINUS:SinCalc|||true
|
||||
arr=ch.psi.pshell.epics.ChannelDoubleArray|TESTIOC:TESTWF2:MyWF 6 -1 false|||true
|
||||
arr1=ch.psi.pshell.epics.ChannelIntegerArray|TESTIOC:TESTWF2:MyWF -1 false|||
|
||||
mt=ch.psi.pshell.epics.GenericMatrix|TESTIOC:TESTWF2:MyWF 2 3 false|||
|
||||
mt1=ch.psi.pshell.epics.GenericMatrix|TESTIOC:TESTWF2:MyWF 3 2 False None [i|||
|
||||
as1=ch.psi.pshell.imaging.RegisterArraySource|arr1|||
|
||||
cm1=ch.psi.pshell.epics.ChannelIntegerMatrix|TESTIOC:TESTWF2:MyWF 3 3 false|||
|
||||
pv=ch.psi.pshell.epics.ProcessVariable|TESTIOC:TESTCALCOUT:Input|||true
|
||||
shutter=ch.psi.pshell.epics.BinaryPositioner|TESTIOC:TESTBO:MyBO TESTIOC:TESTBO:MyBO|||true
|
||||
$motor=ch.psi.pshell.epics.Motor|MTEST-GOBBO:MOT1|||true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#Fri Aug 24 11:15:23 CEST 2018
|
||||
#Fri Nov 16 23:08:57 CET 2018
|
||||
ch.psi.jcae.ContextFactory.addressList=127.0.0.1\:54321
|
||||
ch.psi.jcae.ContextFactory.maxArrayBytes=10000000
|
||||
ch.psi.jcae.ChannelFactory.timeout=100
|
||||
ch.psi.jcae.ChannelFactory.timeout=200
|
||||
ch.psi.jcae.ChannelFactory.retries=1
|
||||
ch.psi.jcae.ContextFactory.serverPort=
|
||||
ch.psi.jcae.impl.DefaultChannelService.retries=1
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
PID.java=enabled
|
||||
SpinnerLayoutTest.java=disabled
|
||||
MXSC-1.10.0.jar=disabled
|
||||
ScreenPanel3.java=disabled
|
||||
Tomaz.java=disabled
|
||||
|
||||
27
devices/as.properties
Normal file
27
devices/as.properties
Normal file
@@ -0,0 +1,27 @@
|
||||
#Tue Oct 30 09:21:40 CET 2018
|
||||
colormap=Grayscale
|
||||
colormapAutomatic=false
|
||||
colormapLogarithmic=false
|
||||
colormapMax=NaN
|
||||
colormapMin=NaN
|
||||
flipHorizontally=false
|
||||
flipVertically=false
|
||||
grayscale=false
|
||||
imageHeight=3
|
||||
imageWidth=3
|
||||
invert=false
|
||||
rescaleFactor=1.0
|
||||
rescaleOffset=0.0
|
||||
roiHeight=-1
|
||||
roiWidth=-1
|
||||
roiX=0
|
||||
roiY=0
|
||||
rotation=0.0
|
||||
rotationCrop=false
|
||||
scale=1.0
|
||||
spatialCalOffsetX=NaN
|
||||
spatialCalOffsetY=NaN
|
||||
spatialCalScaleX=NaN
|
||||
spatialCalScaleY=NaN
|
||||
spatialCalUnits=mm
|
||||
transpose=false
|
||||
27
devices/as1.properties
Normal file
27
devices/as1.properties
Normal file
@@ -0,0 +1,27 @@
|
||||
#Tue Oct 30 09:23:19 CET 2018
|
||||
colormap=Grayscale
|
||||
colormapAutomatic=false
|
||||
colormapLogarithmic=false
|
||||
colormapMax=NaN
|
||||
colormapMin=NaN
|
||||
flipHorizontally=false
|
||||
flipVertically=false
|
||||
grayscale=false
|
||||
imageHeight=3
|
||||
imageWidth=3
|
||||
invert=false
|
||||
rescaleFactor=1.0
|
||||
rescaleOffset=0.0
|
||||
roiHeight=-1
|
||||
roiWidth=-1
|
||||
roiX=0
|
||||
roiY=0
|
||||
rotation=0.0
|
||||
rotationCrop=false
|
||||
scale=1.0
|
||||
spatialCalOffsetX=NaN
|
||||
spatialCalOffsetY=NaN
|
||||
spatialCalScaleX=NaN
|
||||
spatialCalScaleY=NaN
|
||||
spatialCalUnits=mm
|
||||
transpose=false
|
||||
9
devices/cm1.properties
Normal file
9
devices/cm1.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
#Tue Oct 30 09:41:22 CET 2018
|
||||
mirror_x=false
|
||||
mirror_y=false
|
||||
precision=-1
|
||||
roi_height=-1
|
||||
roi_width=-1
|
||||
roi_x=0
|
||||
roi_y=0
|
||||
transpose=false
|
||||
25
devices/detector_roi_img.properties
Normal file
25
devices/detector_roi_img.properties
Normal file
@@ -0,0 +1,25 @@
|
||||
#Fri Aug 31 16:53:08 CEST 2018
|
||||
colormap=Temperature
|
||||
colormapAutomatic=true
|
||||
colormapLogarithmic=false
|
||||
colormapMax=1.0
|
||||
colormapMin=0.0
|
||||
flipHorizontally=false
|
||||
flipVertically=false
|
||||
grayscale=false
|
||||
invert=false
|
||||
rescaleFactor=1.0
|
||||
rescaleOffset=0.0
|
||||
roiHeight=-1
|
||||
roiWidth=-1
|
||||
roiX=0
|
||||
roiY=0
|
||||
rotation=0.0
|
||||
rotationCrop=false
|
||||
scale=1.0
|
||||
spatialCalOffsetX=NaN
|
||||
spatialCalOffsetY=NaN
|
||||
spatialCalScaleX=NaN
|
||||
spatialCalScaleY=NaN
|
||||
spatialCalUnits=mm
|
||||
transpose=false
|
||||
25
devices/deting.properties
Normal file
25
devices/deting.properties
Normal file
@@ -0,0 +1,25 @@
|
||||
#Thu Sep 13 17:21:24 CEST 2018
|
||||
colormap=Grayscale
|
||||
colormapAutomatic=false
|
||||
colormapLogarithmic=false
|
||||
colormapMax=NaN
|
||||
colormapMin=NaN
|
||||
flipHorizontally=false
|
||||
flipVertically=false
|
||||
grayscale=false
|
||||
invert=false
|
||||
rescaleFactor=1.0
|
||||
rescaleOffset=0.0
|
||||
roiHeight=-1
|
||||
roiWidth=-1
|
||||
roiX=0
|
||||
roiY=0
|
||||
rotation=0.0
|
||||
rotationCrop=false
|
||||
scale=1.0
|
||||
spatialCalOffsetX=NaN
|
||||
spatialCalOffsetY=NaN
|
||||
spatialCalScaleX=NaN
|
||||
spatialCalScaleY=NaN
|
||||
spatialCalUnits=mm
|
||||
transpose=false
|
||||
@@ -1,4 +1,4 @@
|
||||
#Tue Jun 19 17:19:36 CEST 2018
|
||||
#Tue Oct 16 15:21:38 CEST 2018
|
||||
accessType=ReadWrite
|
||||
channel=MTEST-GOBBO\:MOT2
|
||||
defaultSpeed=0.2
|
||||
@@ -7,9 +7,9 @@ hasEnable=true
|
||||
homingDirection=Backward
|
||||
homingType=Backward
|
||||
maxSpeed=100.0
|
||||
maxValue=2.0
|
||||
maxValue=100.0
|
||||
minSpeed=0.001
|
||||
minValue=-2.0
|
||||
minValue=-100.0
|
||||
offset=10.0
|
||||
precision=3
|
||||
resolution=NaN
|
||||
|
||||
9
devices/mt.properties
Normal file
9
devices/mt.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
#Tue Oct 30 08:49:48 CET 2018
|
||||
mirror_x=false
|
||||
mirror_y=false
|
||||
precision=-1
|
||||
roi_height=-1
|
||||
roi_width=-1
|
||||
roi_x=0
|
||||
roi_y=0
|
||||
transpose=false
|
||||
9
devices/mt1.properties
Normal file
9
devices/mt1.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
#Tue Oct 30 08:49:48 CET 2018
|
||||
mirror_x=false
|
||||
mirror_y=false
|
||||
precision=-1
|
||||
roi_height=-1
|
||||
roi_width=-1
|
||||
roi_x=0
|
||||
roi_y=0
|
||||
transpose=false
|
||||
468
plugins/PID.java
Normal file
468
plugins/PID.java
Normal file
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* Small, easy to use PID implementation with advanced controller capability.<br>
|
||||
* Minimal usage:<br>
|
||||
* MiniPID pid = new MiniPID(p,i,d); <br>
|
||||
* ...looping code...{ <br>
|
||||
* output= pid.getOutput(sensorvalue,target); <br>
|
||||
* }
|
||||
*
|
||||
* @see http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-direction/improving-the-beginners-pid-introduction
|
||||
*/
|
||||
public class PID{
|
||||
//**********************************
|
||||
// Class private variables
|
||||
//**********************************
|
||||
|
||||
private double P=0;
|
||||
private double I=0;
|
||||
private double D=0;
|
||||
private double F=0;
|
||||
|
||||
private double maxIOutput=0;
|
||||
private double maxError=0;
|
||||
private double errorSum=0;
|
||||
|
||||
private double maxOutput=0;
|
||||
private double minOutput=0;
|
||||
|
||||
private double setpoint=0;
|
||||
|
||||
private double lastActual=0;
|
||||
|
||||
private boolean firstRun=true;
|
||||
private boolean reversed=false;
|
||||
|
||||
private double outputRampRate=0;
|
||||
private double lastOutput=0;
|
||||
|
||||
private double outputFilter=0;
|
||||
|
||||
private double setpointRange=0;
|
||||
|
||||
//**********************************
|
||||
// Constructor functions
|
||||
//**********************************
|
||||
|
||||
/**
|
||||
* Create a MiniPID class object.
|
||||
* See setP, setI, setD methods for more detailed parameters.
|
||||
* @param p Proportional gain. Large if large difference between setpoint and target.
|
||||
* @param i Integral gain. Becomes large if setpoint cannot reach target quickly.
|
||||
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
|
||||
*/
|
||||
public PID(double p, double i, double d){
|
||||
P=p; I=i; D=d;
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MiniPID class object.
|
||||
* See setP, setI, setD, setF methods for more detailed parameters.
|
||||
* @param p Proportional gain. Large if large difference between setpoint and target.
|
||||
* @param i Integral gain. Becomes large if setpoint cannot reach target quickly.
|
||||
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
|
||||
* @param f Feed-forward gain. Open loop "best guess" for the output should be. Only useful if setpoint represents a rate.
|
||||
*/
|
||||
public PID(double p, double i, double d, double f){
|
||||
P=p; I=i; D=d; F=f;
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
//**********************************
|
||||
// Configuration functions
|
||||
//**********************************
|
||||
/**
|
||||
* Configure the Proportional gain parameter. <br>
|
||||
* This responds quickly to changes in setpoint, and provides most of the initial driving force
|
||||
* to make corrections. <br>
|
||||
* Some systems can be used with only a P gain, and many can be operated with only PI.<br>
|
||||
* For position based controllers, this is the first parameter to tune, with I second. <br>
|
||||
* For rate controlled systems, this is often the second after F.
|
||||
*
|
||||
* @param p Proportional gain. Affects output according to <b>output+=P*(setpoint-current_value)</b>
|
||||
*/
|
||||
public void setP(double p){
|
||||
P=p;
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the I parameter <br>
|
||||
* This is used for overcoming disturbances, and ensuring that the controller always gets to the control mode.
|
||||
* Typically tuned second for "Position" based modes, and third for "Rate" or continuous based modes. <br>
|
||||
* Affects output through <b>output+=previous_errors*Igain ;previous_errors+=current_error</b>
|
||||
*
|
||||
* @see {@link #setMaxIOutput(double) setMaxIOutput} for how to restrict
|
||||
*
|
||||
* @param i New gain value for the Integral term
|
||||
*/
|
||||
public void setI(double i){
|
||||
if(I!=0){
|
||||
errorSum=errorSum*I/i;
|
||||
}
|
||||
if(maxIOutput!=0){
|
||||
maxError=maxIOutput/i;
|
||||
}
|
||||
I=i;
|
||||
checkSigns();
|
||||
// Implementation note:
|
||||
// This Scales the accumulated error to avoid output errors.
|
||||
// As an example doubling the I term cuts the accumulated error in half, which results in the
|
||||
// output change due to the I term constant during the transition.
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the D parameter <br>
|
||||
* This has two primary effects:
|
||||
* <list>
|
||||
* <li> Adds a "startup kick" and speeds up system response during setpoint changes
|
||||
* <li> Adds "drag" and slows the system when moving toward the target
|
||||
* </list>
|
||||
* A small D value can be useful for both improving response times, and preventing overshoot.
|
||||
* However, in many systems a large D value will cause significant instability, particularly
|
||||
* for large setpoint changes.
|
||||
* <br>
|
||||
* Affects output through <b>output += -D*(current_input_value - last_input_value)</b>
|
||||
*
|
||||
* @param d New gain value for the Derivative term
|
||||
*/
|
||||
public void setD(double d){
|
||||
D=d;
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the FeedForward parameter. <br>
|
||||
* This is excellent for velocity, rate, and other continuous control modes where you can
|
||||
* expect a rough output value based solely on the setpoint.<br>
|
||||
* Should not be used in "position" based control modes.<br>
|
||||
* Affects output according to <b>output+=F*Setpoint</b>. Note, that a F-only system is actually open loop.
|
||||
*
|
||||
* @param f Feed forward gain.
|
||||
*/
|
||||
public void setF(double f){
|
||||
F=f;
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the PID object.
|
||||
* See setP, setI, setD methods for more detailed parameters.
|
||||
* @param p Proportional gain. Large if large difference between setpoint and target.
|
||||
* @param i Integral gain. Becomes large if setpoint cannot reach target quickly.
|
||||
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
|
||||
*/
|
||||
public void setPID(double p, double i, double d){
|
||||
P=p;D=d;
|
||||
//Note: the I term has additional calculations, so we need to use it's
|
||||
//specific method for setting it.
|
||||
setI(i);
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the PID object.
|
||||
* See setP, setI, setD, setF methods for more detailed parameters.
|
||||
* @param p Proportional gain. Large if large difference between setpoint and target.
|
||||
* @param i Integral gain. Becomes large if setpoint cannot reach target quickly.
|
||||
* @param d Derivative gain. Responds quickly to large changes in error. Small values prevents P and I terms from causing overshoot.
|
||||
* @param f Feed-forward gain. Open loop "best guess" for the output should be. Only useful if setpoint represents a rate.
|
||||
*/
|
||||
public void setPID(double p, double i, double d,double f){
|
||||
P=p;D=d;F=f;
|
||||
//Note: the I term has additional calculations, so we need to use it's
|
||||
//specific method for setting it.
|
||||
setI(i);
|
||||
checkSigns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum output value contributed by the I component of the system
|
||||
* This can be used to prevent large windup issues and make tuning simpler
|
||||
* @param maximum. Units are the same as the expected output value
|
||||
*/
|
||||
public void setMaxIOutput(double maximum){
|
||||
// Internally maxError and Izone are similar, but scaled for different purposes.
|
||||
// The maxError is generated for simplifying math, since calculations against
|
||||
// the max error are far more common than changing the I term or Izone.
|
||||
maxIOutput=maximum;
|
||||
if(I!=0){
|
||||
maxError=maxIOutput/I;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a maximum output range. <br>
|
||||
* When one input is specified, output range is configured to
|
||||
* <b>[-output, output]</b>
|
||||
* @param output
|
||||
*/
|
||||
public void setOutputLimits(double output){
|
||||
setOutputLimits(-output,output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a maximum output.
|
||||
* When two inputs specified, output range is configured to
|
||||
* <b>[minimum, maximum]</b>
|
||||
* @param minimum possible output value
|
||||
* @param maximum possible output value
|
||||
*/
|
||||
public void setOutputLimits(double minimum,double maximum){
|
||||
if(maximum<minimum)return;
|
||||
maxOutput=maximum;
|
||||
minOutput=minimum;
|
||||
|
||||
// Ensure the bounds of the I term are within the bounds of the allowable output swing
|
||||
if(maxIOutput==0 || maxIOutput>(maximum-minimum) ){
|
||||
setMaxIOutput(maximum-minimum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the operating direction of the PID controller
|
||||
* @param reversed Set true to reverse PID output
|
||||
*/
|
||||
public void setDirection(boolean reversed){
|
||||
this.reversed=reversed;
|
||||
}
|
||||
|
||||
//**********************************
|
||||
// Primary operating functions
|
||||
//**********************************
|
||||
|
||||
/**
|
||||
* Configure setpoint for the PID calculations<br>
|
||||
* This represents the target for the PID system's, such as a
|
||||
* position, velocity, or angle. <br>
|
||||
* @see PID#getOutput(actual) <br>
|
||||
* @param setpoint
|
||||
*/
|
||||
public void setSetpoint(double setpoint){
|
||||
this.setpoint=setpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the output value for the current PID cycle.<br>
|
||||
* @param actual The monitored value, typically as a sensor input.
|
||||
* @param setpoint The target value for the system
|
||||
* @return calculated output value for driving the system
|
||||
*/
|
||||
public double getOutput(double actual, double setpoint){
|
||||
double output;
|
||||
double Poutput;
|
||||
double Ioutput;
|
||||
double Doutput;
|
||||
double Foutput;
|
||||
|
||||
this.setpoint=setpoint;
|
||||
|
||||
// Ramp the setpoint used for calculations if user has opted to do so
|
||||
if(setpointRange!=0){
|
||||
setpoint=constrain(setpoint,actual-setpointRange,actual+setpointRange);
|
||||
}
|
||||
|
||||
// Do the simple parts of the calculations
|
||||
double error=setpoint-actual;
|
||||
|
||||
// Calculate F output. Notice, this depends only on the setpoint, and not the error.
|
||||
Foutput=F*setpoint;
|
||||
|
||||
// Calculate P term
|
||||
Poutput=P*error;
|
||||
|
||||
// If this is our first time running this, we don't actually _have_ a previous input or output.
|
||||
// For sensor, sanely assume it was exactly where it is now.
|
||||
// For last output, we can assume it's the current time-independent outputs.
|
||||
if(firstRun){
|
||||
lastActual=actual;
|
||||
lastOutput=Poutput+Foutput;
|
||||
firstRun=false;
|
||||
}
|
||||
|
||||
// Calculate D Term
|
||||
// Note, this is negative. This actually "slows" the system if it's doing
|
||||
// the correct thing, and small values helps prevent output spikes and overshoot
|
||||
Doutput= -D*(actual-lastActual);
|
||||
lastActual=actual;
|
||||
|
||||
// The Iterm is more complex. There's several things to factor in to make it easier to deal with.
|
||||
// 1. maxIoutput restricts the amount of output contributed by the Iterm.
|
||||
// 2. prevent windup by not increasing errorSum if we're already running against our max Ioutput
|
||||
// 3. prevent windup by not increasing errorSum if output is output=maxOutput
|
||||
Ioutput=I*errorSum;
|
||||
if(maxIOutput!=0){
|
||||
Ioutput=constrain(Ioutput,-maxIOutput,maxIOutput);
|
||||
}
|
||||
|
||||
// And, finally, we can just add the terms up
|
||||
output=Foutput + Poutput + Ioutput + Doutput;
|
||||
|
||||
// Figure out what we're doing with the error.
|
||||
if(minOutput!=maxOutput && !bounded(output, minOutput,maxOutput) ){
|
||||
errorSum=error;
|
||||
// reset the error sum to a sane level
|
||||
// Setting to current error ensures a smooth transition when the P term
|
||||
// decreases enough for the I term to start acting upon the controller
|
||||
// From that point the I term will build up as would be expected
|
||||
}
|
||||
else if(outputRampRate!=0 && !bounded(output, lastOutput-outputRampRate,lastOutput+outputRampRate) ){
|
||||
errorSum=error;
|
||||
}
|
||||
else if(maxIOutput!=0){
|
||||
errorSum=constrain(errorSum+error,-maxError,maxError);
|
||||
// In addition to output limiting directly, we also want to prevent I term
|
||||
// buildup, so restrict the error directly
|
||||
}
|
||||
else{
|
||||
errorSum+=error;
|
||||
}
|
||||
|
||||
// Restrict output to our specified output and ramp limits
|
||||
if(outputRampRate!=0){
|
||||
output=constrain(output, lastOutput-outputRampRate,lastOutput+outputRampRate);
|
||||
}
|
||||
if(minOutput!=maxOutput){
|
||||
output=constrain(output, minOutput,maxOutput);
|
||||
}
|
||||
if(outputFilter!=0){
|
||||
output=lastOutput*outputFilter+output*(1-outputFilter);
|
||||
}
|
||||
|
||||
// Get a test printline with lots of details about the internal
|
||||
// calculations. This can be useful for debugging.
|
||||
// System.out.printf("Final output %5.2f [ %5.2f, %5.2f , %5.2f ], eSum %.2f\n",output,Poutput, Ioutput, Doutput,errorSum );
|
||||
// System.out.printf("%5.2f\t%5.2f\t%5.2f\t%5.2f\n",output,Poutput, Ioutput, Doutput );
|
||||
|
||||
lastOutput=output;
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the output value for the current PID cycle.<br>
|
||||
* In no-parameter mode, this uses the last sensor value,
|
||||
* and last setpoint value. <br>
|
||||
* Not typically useful, and use of parameter modes is suggested. <br>
|
||||
* @return calculated output value for driving the system
|
||||
*/
|
||||
public double getOutput(){
|
||||
return getOutput(lastActual,setpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the output value for the current PID cycle.<br>
|
||||
* In one parameter mode, the last configured setpoint will be used.<br>
|
||||
* @see PID#setSetpoint()
|
||||
* @param actual The monitored value, typically as a sensor input.
|
||||
* @param setpoint The target value for the system
|
||||
* @return calculated output value for driving the system
|
||||
*/
|
||||
public double getOutput(double actual){
|
||||
return getOutput(actual,setpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the controller. This erases the I term buildup, and removes
|
||||
* D gain on the next loop.<br>
|
||||
* This should be used any time the PID is disabled or inactive for extended
|
||||
* duration, and the controlled portion of the system may have changed due to
|
||||
* external forces.
|
||||
*/
|
||||
public void reset(){
|
||||
firstRun=true;
|
||||
errorSum=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum rate the output can increase per cycle.<br>
|
||||
* This can prevent sharp jumps in output when changing setpoints or
|
||||
* enabling a PID system, which might cause stress on physical or electrical
|
||||
* systems. <br>
|
||||
* Can be very useful for fast-reacting control loops, such as ones
|
||||
* with large P or D values and feed-forward systems.
|
||||
*
|
||||
* @param rate, with units being the same as the output
|
||||
*/
|
||||
public void setOutputRampRate(double rate){
|
||||
outputRampRate=rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a limit on how far the setpoint can be from the current position
|
||||
* <br>Can simplify tuning by helping tuning over a small range applies to a much larger range.
|
||||
* <br>This limits the reactivity of P term, and restricts impact of large D term
|
||||
* during large setpoint adjustments. Increases lag and I term if range is too small.
|
||||
* @param range, with units being the same as the expected sensor range.
|
||||
*/
|
||||
public void setSetpointRange(double range){
|
||||
setpointRange=range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a filter on the output to reduce sharp oscillations. <br>
|
||||
* 0.1 is likely a sane starting value. Larger values use historical data
|
||||
* more heavily, with low values weigh newer data. 0 will disable, filtering, and use
|
||||
* only the most recent value. <br>
|
||||
* Increasing the filter strength will P and D oscillations, but force larger I
|
||||
* values and increase I term overshoot.<br>
|
||||
* Uses an exponential wieghted rolling sum filter, according to a simple <br>
|
||||
* <pre>output*(1-strength)*sum(0..n){output*strength^n}</pre> algorithm.
|
||||
* @param output valid between [0..1), meaning [current output only.. historical output only)
|
||||
*/
|
||||
public void setOutputFilter(double strength){
|
||||
if(strength==0 || bounded(strength,0,1)){
|
||||
outputFilter=strength;
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************
|
||||
// Helper functions
|
||||
//**************************************
|
||||
|
||||
/**
|
||||
* Forces a value into a specific range
|
||||
* @param value input value
|
||||
* @param min maximum returned value
|
||||
* @param max minimum value in range
|
||||
* @return Value if it's within provided range, min or max otherwise
|
||||
*/
|
||||
private double constrain(double value, double min, double max){
|
||||
if(value > max){ return max;}
|
||||
if(value < min){ return min;}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the value is within the min and max, inclusive
|
||||
* @param value to test
|
||||
* @param min Minimum value of range
|
||||
* @param max Maximum value of range
|
||||
* @return true if value is within range, false otherwise
|
||||
*/
|
||||
private boolean bounded(double value, double min, double max){
|
||||
// Note, this is an inclusive range. This is so tests like
|
||||
// `bounded(constrain(0,0,1),0,1)` will return false.
|
||||
// This is more helpful for determining edge-case behaviour
|
||||
// than <= is.
|
||||
return (min<value) && (value<max);
|
||||
}
|
||||
|
||||
/**
|
||||
* To operate correctly, all PID parameters require the same sign
|
||||
* This should align with the {@literal}reversed value
|
||||
*/
|
||||
private void checkSigns(){
|
||||
if(reversed){ // all values should be below zero
|
||||
if(P>0) P*=-1;
|
||||
if(I>0) I*=-1;
|
||||
if(D>0) D*=-1;
|
||||
if(F>0) F*=-1;
|
||||
}
|
||||
else{ // all values should be above zero
|
||||
if(P<0) P*=-1;
|
||||
if(I<0) I*=-1;
|
||||
if(D<0) D*=-1;
|
||||
if(F<0) F*=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
script/AreaDetectorROI.py
Normal file
27
script/AreaDetectorROI.py
Normal file
@@ -0,0 +1,27 @@
|
||||
device = det
|
||||
det.imageCounter.polling = 500
|
||||
|
||||
class AreaDetectorROI(ReadonlyRegisterBase, ReadonlyRegisterMatrix):
|
||||
def __init__(self, name, detector, x, y, w, h):
|
||||
ReadonlyRegisterBase.__init__(self, name)
|
||||
self.detector = detector
|
||||
self.x, self.y, self.w, self.h = x, y, w, h
|
||||
|
||||
def doRead(self):
|
||||
data = self.detector.dataMatrix.take()
|
||||
ret = Convert.matrixRoi(data, self.x, self.y, self.w, self.h)
|
||||
return ret
|
||||
|
||||
def getWidth(self):
|
||||
return self.w
|
||||
|
||||
def getHeight(self):
|
||||
return self.h
|
||||
|
||||
|
||||
add_device(AreaDetectorROI("roi1", device, 10,10,10,5), True)
|
||||
add_device(AreaDetectorROI("roi2", device,100,30,7,3), True)
|
||||
|
||||
#tscan((roi1, roi2), 10, 0.1)
|
||||
|
||||
mscan(device.imageCounter, (roi1, roi2), 10, async=False)
|
||||
0
script/TestPID.py
Normal file
0
script/TestPID.py
Normal file
@@ -1,18 +1,21 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Deployment specific global definitions - executed after startup.groovy
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
import ch.psi.pshell.scan.ScanRecord
|
||||
import ch.psi.pshell.scan.ScanRecord
|
||||
|
||||
System.out.println("OK")
|
||||
|
||||
before_readout = { double[] pos-> println (pos) }
|
||||
|
||||
after_readout = { ScanRecord rec->println (rec) }
|
||||
after_readout = { ch.psi.pshell.scan.ScanRecord rec->println (rec) }
|
||||
|
||||
def onBeforeReadout(double[]pos){
|
||||
println (pos)
|
||||
}
|
||||
|
||||
def onAfterReadout(ScanRecord rec){
|
||||
def onAfterReadout(ch.psi.pshell.scan.ScanRecord rec){
|
||||
println (rec)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
@@ -10,4 +10,5 @@ function onAfterReadout(rec){
|
||||
|
||||
|
||||
|
||||
run("SimulatedDEvices")
|
||||
run("SimulatedDEvices")
|
||||
|
||||
|
||||
@@ -8,17 +8,21 @@ import ch.psi.pshell.crlogic.CrlogicPositioner as CrlogicPositioner
|
||||
import ch.psi.pshell.crlogic.CrlogicSensor as CrlogicSensor
|
||||
|
||||
|
||||
def on_command_started(info):
|
||||
print "Started: " + str(info.script) + str(info.error)
|
||||
|
||||
def on_command_finished(info):
|
||||
print "Finished: " + str(info.script) + str(info.error)
|
||||
###################################################################################################
|
||||
# Layout setup
|
||||
###################################################################################################
|
||||
import ch.psi.pshell.data.LayoutSF as LayoutSF
|
||||
|
||||
LayoutSF.setExperimentArguments([pv, motor, pe, cv, en, sin])
|
||||
LayoutSF.setExperimentArguments([pv, motor, pe, cv, energy, sin])
|
||||
|
||||
|
||||
|
||||
#Libraries
|
||||
#Librariesenergy
|
||||
#import Jama.Matrix
|
||||
#sys.path.append('/Users/gobbo_a/dev/pshell/config/home/script/Lib/diffcalc')
|
||||
|
||||
@@ -136,14 +140,14 @@ get_context().addListener(clistener)
|
||||
def trig_scienta():
|
||||
time.sleep(1.0)
|
||||
|
||||
energy = None
|
||||
en_val = None
|
||||
class SimulatedEnergy(Writable):
|
||||
def write(self, value):
|
||||
self.put(value)
|
||||
|
||||
def put(self, value, timeout = None):
|
||||
global energy
|
||||
energy = value
|
||||
global en_val
|
||||
en_val = value
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
@@ -155,8 +159,8 @@ class SimulatedEnergy(Writable):
|
||||
|
||||
class SimulatedEnergyReadback(Readable):
|
||||
def read(self):
|
||||
global energy
|
||||
return energy;
|
||||
global en_val
|
||||
return en_val;
|
||||
|
||||
def get(self):
|
||||
return self.read()
|
||||
|
||||
14
script/test/CustomPlot.py
Normal file
14
script/test/CustomPlot.py
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
p = plot(None, title="Custom Plot")[0]
|
||||
p.addSeries(LinePlotSeries("My Series"))
|
||||
proc_data = "/proc/calc"
|
||||
|
||||
create_dataset(proc_data , 'd')
|
||||
|
||||
def AfterReadout(record, scan):
|
||||
val = record.values[1] - record.values[0]
|
||||
p.getSeries(0).appendData(record.positions[0], val)
|
||||
append_dataset(proc_data , val)
|
||||
|
||||
lscan(inp, (out, sin), 0.0, 10.0, 1.0, latency = 0.1, after_read=AfterReadout)
|
||||
22
script/test/TestChanel.py
Normal file
22
script/test/TestChanel.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import ch.psi.pshell.epics.CAS as CAS
|
||||
import random
|
||||
class Waveform(ReadonlyRegisterBase, ReadonlyRegisterArray):
|
||||
def doRead(self):
|
||||
ret = []
|
||||
for i in range(self.getSize()):
|
||||
ret.append(random.random())
|
||||
ret = to_array(ret, 'd')
|
||||
return ret
|
||||
|
||||
def getSize(self):
|
||||
return 1000
|
||||
|
||||
|
||||
wf = Waveform("wf")
|
||||
wf.initialize()
|
||||
print wf.read()[0]
|
||||
|
||||
casN = CAS("TESTCAS:VAL", wf, 'double')
|
||||
print casN
|
||||
|
||||
print caget("TESTCAS:VAL")
|
||||
78
script/test/TestData.py
Normal file
78
script/test/TestData.py
Normal file
@@ -0,0 +1,78 @@
|
||||
###################################################################################################
|
||||
#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
|
||||
|
||||
|
||||
#Writing scalars (datasets with rank 0)
|
||||
save_dataset("group/val1", 1)
|
||||
save_dataset("group/val2", 3.14)
|
||||
save_dataset("group/val3", "test")
|
||||
print load_data("group/val1")
|
||||
print load_data("group/val2")
|
||||
print load_data("group/val3")
|
||||
|
||||
|
||||
22
script/test/TestDetectorROI.py
Normal file
22
script/test/TestDetectorROI.py
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DetectorRoi(ReadonlyRegisterBase, ReadonlyRegisterMatrix):
|
||||
def doRead(self):
|
||||
#data = caget(“PINK:GEYES:image2:ArrayData")
|
||||
data = caget("TESTCAS:VAL", '[d', self.getHeight() * self.getWidth())
|
||||
return Convert.reshape(data,self.getHeight(), self.getWidth()) #
|
||||
|
||||
def getWidth(self):
|
||||
return 50
|
||||
|
||||
def getHeight(self):
|
||||
return 20
|
||||
|
||||
add_device (DetectorRoi("detector_roi"), True)
|
||||
|
||||
|
||||
add_device(RegisterMatrixSource("detector_roi_img", detector_roi), True)
|
||||
detector_roi_img.polling = -500
|
||||
557
script/test/TestDiff.py
Normal file
557
script/test/TestDiff.py
Normal file
@@ -0,0 +1,557 @@
|
||||
###################################################################################################\
|
||||
# Diffcalc utilities
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################\
|
||||
# Installaling
|
||||
###################################################################################################
|
||||
|
||||
#1- Download from: https://github.com/DiamondLightSource/diffcalc/archive/master.zip
|
||||
#2- Extract the contents to {script}/Lib/diffcalc
|
||||
#3- Download http://central.maven.org/maven2/gov/nist/math/jama/1.0.3/jama-1.0.3.jar
|
||||
# to the extensions folder.
|
||||
|
||||
###################################################################################################\
|
||||
# Library loading and Hardware setup
|
||||
###################################################################################################
|
||||
|
||||
#1- Create a MotorGroup with the diffractometer motors
|
||||
# e.g. 'sixc', containing mu, delta, gam, eta, chi, phi motors (gam = nu)
|
||||
# or 'fivec', containing delta, gam, eta, chi, phi motors
|
||||
# or 'fourc', containing delta, eta, chi, phi motors
|
||||
#2- Create positioner to read/set the energy in kEv, e.g. named 'en'
|
||||
#3- Execute: run("diffutils")
|
||||
#4- Execute: setup_diff(sixc, en)
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
import traceback
|
||||
|
||||
|
||||
import Jama.Matrix
|
||||
diffcalc_path = os.path.abspath(get_context().setup.expandPath("{script}/Lib/diffcalc"))
|
||||
if not diffcalc_path in sys.path:
|
||||
sys.path.append(diffcalc_path)
|
||||
|
||||
import diffcalc
|
||||
from diffcalc import settings
|
||||
from diffcalc.hkl.you.geometry import YouGeometry,SixCircle, FiveCircle, FourCircle, YouPosition
|
||||
from diffcalc.hardware import HardwareAdapter
|
||||
from diffcalc.ub.persistence import UbCalculationNonPersister
|
||||
from diffcalc.gdasupport.minigda.scannable import ScannableBase, ScannableGroup
|
||||
#from diffcalc.gdasupport.minigda import command
|
||||
from diffcalc.hardware import HardwareAdapter
|
||||
|
||||
|
||||
import ch.psi.pshell.device.PositionerConfig as PositionerConfig
|
||||
import ch.psi.pshell.device.RegisterConfig as RegisterConfig
|
||||
import ch.psi.pshell.device.Register as Register
|
||||
|
||||
_difcalc_names = {}
|
||||
|
||||
#
|
||||
# Disable error handling designed for interactive use
|
||||
#diffcalc.util.DEBUG = True
|
||||
|
||||
###################################################################################################
|
||||
# Device mapping to difcalc
|
||||
###################################################################################################
|
||||
class PositionerScannable(ScannableBase):
|
||||
def __init__(self, positioner, name = None):
|
||||
self.positioner = positioner
|
||||
self.name = positioner.name if name is None else name
|
||||
self.inputNames = [self.name]
|
||||
self.outputFormat = ['% 6.4f']
|
||||
self.level = 3
|
||||
|
||||
def isBusy(self):
|
||||
return self.positioner.state == State.Busy
|
||||
|
||||
def waitWhileBusy(self):
|
||||
self.positioner.waitReady(-1)
|
||||
|
||||
def asynchronousMoveTo(self, new_position):
|
||||
#print "Moving " , self.name, " to: ", new_position
|
||||
self.positioner.moveAsync(float(new_position), -1)
|
||||
|
||||
def getPosition(self):
|
||||
return self.positioner.getPosition()
|
||||
|
||||
def _get_diffcalc_axis_names():
|
||||
nu_name=diffcalc.hkl.you.constraints.NUNAME
|
||||
return ("mu", "delta", nu_name, "eta", "chi", "phi")
|
||||
|
||||
class PositionerScannableGroup(ScannableGroup):
|
||||
def __init__(self, name, motors, diffcalc_axis_names=None):
|
||||
self.name = name
|
||||
global _difcalc_names
|
||||
_difcalc_names = {}
|
||||
positioners = []
|
||||
if diffcalc_axis_names is None:
|
||||
if len(motors) == 6: diffcalc_axis_names = _get_diffcalc_axis_names()
|
||||
elif len(motors) == 5: diffcalc_axis_names = ("delta", "gam", "eta", "chi", " phi")
|
||||
elif len(motors) == 4: diffcalc_axis_names = ("delta", "eta", "chi", " phi")
|
||||
for i in range(len(motors)):
|
||||
_difcalc_names[motors[i]] = diffcalc_axis_names[i]
|
||||
exec('self.' + diffcalc_axis_names[i] + ' = PositionerScannable(' + motors[i].name + ', "' +diffcalc_axis_names[i] + '")')
|
||||
exec('positioners.append(self.' + diffcalc_axis_names[i] + ')' )
|
||||
#for m in motors:
|
||||
# exec('self.' + m.name + ' = PositionerScannable(' + m.name + ', "' + m.name + '")')
|
||||
# exec('positioners.append(self.' + m.name + ')' )
|
||||
ScannableGroup.__init__(self, self.name, positioners)
|
||||
|
||||
class MotorGroupScannable(PositionerScannableGroup):
|
||||
def __init__(self, motor_group, diffcalc_axis_names=None):
|
||||
self.motor_group = motor_group
|
||||
PositionerScannableGroup.__init__(self, motor_group.name, motor_group.motors, diffcalc_axis_names)
|
||||
|
||||
|
||||
class ScannableAdapter(HardwareAdapter):
|
||||
def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1):
|
||||
self.diffractometer = diffractometer
|
||||
self.energy = energy
|
||||
self.energy_multiplier_to_kev = energy_multiplier_to_kev
|
||||
input_names = diffractometer.getInputNames()
|
||||
HardwareAdapter.__init__(self, input_names)
|
||||
|
||||
#Returns the current physical POSITIONS
|
||||
def get_position(self):
|
||||
"""
|
||||
pos = getDiffractometerPosition() -- returns the current physical
|
||||
diffractometer position as a list in degrees
|
||||
"""
|
||||
return self.diffractometer.getPosition()
|
||||
|
||||
#returns energy in kEv
|
||||
def get_energy(self):
|
||||
"""energy = get_energy() -- returns energy in kEv (NOT eV!) """
|
||||
multiplier = self.energy_multiplier_to_kev
|
||||
energy = self.energy.getPosition() * multiplier
|
||||
if energy is None:
|
||||
raise DiffcalcException("Energy has not been set")
|
||||
return energy
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.diffractometer.getName()
|
||||
|
||||
class MotorGroupAdapter(ScannableAdapter):
|
||||
def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1, diffcalc_axis_names=None):
|
||||
self.diffractometer = MotorGroupScannable(diffractometer, diffcalc_axis_names)
|
||||
self.energy = PositionerScannable(energy)
|
||||
self.energy.level = 3
|
||||
ScannableAdapter.__init__(self, self.diffractometer, self.energy, energy_multiplier_to_kev)
|
||||
|
||||
class Wavelength(RegisterBase):
|
||||
def doRead(self):
|
||||
try:
|
||||
return get_wavelength().getPosition()
|
||||
except:
|
||||
return None
|
||||
|
||||
def doWrite(self, val):
|
||||
get_wavelength().asynchronousMoveTo(val)
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# HKL Pseudo-devices
|
||||
###################################################################################################
|
||||
class HklPositoner (PositionerBase):
|
||||
def __init__(self, name, index, hkl_group):
|
||||
PositionerBase.__init__(self, name, PositionerConfig())
|
||||
self.setParent(hkl_group)
|
||||
self.index = index
|
||||
|
||||
def isReady(self):
|
||||
return PositionerBase.isReady(self) and self.getParent().isReady()
|
||||
|
||||
def doRead(self):
|
||||
return self.getParent()._setpoint[self.index]
|
||||
|
||||
def doWrite(self, value):
|
||||
#print "Setting " , self.getName(), "to: ", value
|
||||
pos = [None, None, None]
|
||||
pos[self.index] = value
|
||||
self.getParent().write(pos)
|
||||
|
||||
def doReadReadback(self):
|
||||
if java.lang.Thread.currentThread() != self.getParent()._updating_thread:
|
||||
self.getParent().update()
|
||||
return self.getParent()._readback[self.index]
|
||||
|
||||
class HklGroup(RegisterBase, Register.RegisterArray):
|
||||
def __init__(self, name):
|
||||
RegisterBase.__init__(self, name, RegisterConfig())
|
||||
self.hkl=get_hkl()
|
||||
self.h, self.k, self.l = HklPositoner("h", 0, self), HklPositoner("k", 1, self), HklPositoner("l", 2, self)
|
||||
add_device(self.h, True)
|
||||
add_device(self.k, True)
|
||||
add_device(self.l, True)
|
||||
self._setpoint = self.doRead()
|
||||
self._updating = False
|
||||
|
||||
def getSize(self):
|
||||
return 3
|
||||
|
||||
def doRead(self):
|
||||
try:
|
||||
self._readback = self.hkl.getPosition()
|
||||
self._updating_thread = java.lang.Thread.currentThread()
|
||||
self.h.update()
|
||||
self.k.update()
|
||||
self.l.update()
|
||||
except:
|
||||
#traceback.print_exc()
|
||||
self._readback = (None, None, None)
|
||||
finally:
|
||||
self._updating_thread = None
|
||||
return self._readback
|
||||
|
||||
def doWrite(self, pos):
|
||||
self._setpoint = pos
|
||||
#print "Moving to: " + str(pos)
|
||||
self.hkl.asynchronousMoveTo(pos)
|
||||
|
||||
def sim(self, pos):
|
||||
return self.hkl.simulateMoveTo(pos)
|
||||
|
||||
###################################################################################################
|
||||
# System setup
|
||||
###################################################################################################
|
||||
you = None
|
||||
dc, ub, hardware, hkl = None, None, None, None
|
||||
_motor_group = None
|
||||
def setup_diff(diffractometer, energy, diffcalc_axis_names = None, geometry=None):
|
||||
"""
|
||||
diffractometer: Diffraction motor group
|
||||
energy: Positioner having energy in kev
|
||||
geometry: YouGeometry extension. If none, uses default
|
||||
diffcalc_axis_names: if None use defaults:
|
||||
- mu, delta, gam, eta, chi, phi (six circle)
|
||||
- delta, gam, eta, chi, phi (ficve circle)
|
||||
- delta, eta, chi, phi (four circle)
|
||||
"""
|
||||
global you, dc, ub, hardware, hkl, _motor_group
|
||||
_motor_group = diffractometer
|
||||
you = None
|
||||
if geometry is not None:
|
||||
settings.geometry = geometry
|
||||
elif diffcalc_axis_names is not None:
|
||||
class CustomGeometry(YouGeometry):
|
||||
def __init__(self):
|
||||
self.all_axis_names = _get_diffcalc_axis_names()
|
||||
self.my_axis_names = diffcalc_axis_names
|
||||
fixed_constraints = {}
|
||||
for axis in self.all_axis_names:
|
||||
if not axis in self.my_axis_names:
|
||||
fixed_constraints[axis] = 0
|
||||
YouGeometry.__init__(self, diffractometer.name, fixed_constraints)
|
||||
def physical_angles_to_internal_position(self, physical_angle_tuple):
|
||||
pos=[]
|
||||
index = 0
|
||||
for axis in self.all_axis_names:
|
||||
pos.append(physical_angle_tuple[index] if (axis in self.my_axis_names) else 0)
|
||||
index = index+1
|
||||
return YouPosition(*pos)
|
||||
def internal_position_to_physical_angles(self, internal_position):
|
||||
pos = internal_position.totuple()
|
||||
ret = []
|
||||
for i in range (len(self.all_axis_names)):
|
||||
if self.all_axis_names[i] in self.my_axis_names:
|
||||
ret.append(pos[i])
|
||||
return tuple(ret)
|
||||
settings.geometry = CustomGeometry()
|
||||
elif len(diffractometer.motors) == 6:
|
||||
settings.geometry = SixCircle()
|
||||
elif len(diffractometer.motors) == 5:
|
||||
settings.geometry = FiveCircle()
|
||||
elif len(diffractometer.motors) == 4:
|
||||
settings.geometry = FourCircle()
|
||||
else:
|
||||
raise Exception("Invalid motor group")
|
||||
settings.hardware = MotorGroupAdapter(diffractometer, energy, diffcalc_axis_names = diffcalc_axis_names)
|
||||
settings.ubcalc_persister = UbCalculationNonPersister()
|
||||
settings.axes_scannable_group = settings.hardware.diffractometer
|
||||
settings.energy_scannable = settings.hardware.energy
|
||||
settings.ubcalc_strategy = diffcalc.hkl.you.calc.YouUbCalcStrategy()
|
||||
settings.angles_to_hkl_function = diffcalc.hkl.you.calc.youAnglesToHkl
|
||||
from diffcalc.gdasupport import you
|
||||
reload(you)
|
||||
|
||||
# These must be imported AFTER the settings have been configured
|
||||
from diffcalc.dc import dcyou as dc
|
||||
from diffcalc.ub import ub
|
||||
from diffcalc import hardware
|
||||
from diffcalc.hkl.you import hkl
|
||||
|
||||
add_device(HklGroup("hkl_group"), True)
|
||||
add_device(Wavelength("wavelength", 6), True)
|
||||
hkl_group.polling = 250
|
||||
wavelength.polling = 250
|
||||
|
||||
def setup_axis(motor, min=None, max=None, cut=None):
|
||||
name = _difcalc_names[motor]
|
||||
if min is not None: hardware.setmin(name, min)
|
||||
if max is not None: hardware.setmax(name, max)
|
||||
if cut is not None: hardware.setcut(name, cut)
|
||||
|
||||
def print_axis_setup():
|
||||
print "Diffcalc names:"
|
||||
for m in _difcalc_names.keys():
|
||||
print " \t" + m.name + " = " + _difcalc_names[m]
|
||||
print "------------------------------------------------------"
|
||||
hardware.hardware()
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# Acceess functions
|
||||
###################################################################################################
|
||||
def get_diff():
|
||||
return settings.hardware.diffractometer
|
||||
|
||||
def get_en():
|
||||
return settings.hardware.energy
|
||||
|
||||
def get_motor_group():
|
||||
return _motor_group
|
||||
|
||||
def get_wavelength():
|
||||
return you.wl
|
||||
|
||||
def get_hkl():
|
||||
return you.hkl
|
||||
|
||||
def hkl_to_angles(h, k, l, energy=None):
|
||||
return dc.hkl_to_angles(h, k, l, energy)
|
||||
|
||||
def angles_to_hkl(positions, energy=None):
|
||||
return dc.angles_to_hkl(positions, energy)
|
||||
|
||||
def hkl_read():
|
||||
return hkl_group.read()
|
||||
|
||||
def hkl_write(h, k, l):
|
||||
hkl_group.write([h,k,l])
|
||||
|
||||
def hkl_simulate(h, k, l):
|
||||
return hkl_group.sim([h,k,l])
|
||||
|
||||
def con(*args):
|
||||
hkl.con(*args)
|
||||
|
||||
def uncon(name):
|
||||
hkl.uncon(name)
|
||||
|
||||
def print_con():
|
||||
hkl.con()
|
||||
###################################################################################################
|
||||
# HKL Combined Scan
|
||||
###################################################################################################
|
||||
def hklscan(vector, readables,latency = 0.0, passes = 1, **pars):
|
||||
"""
|
||||
HKL Scan:
|
||||
|
||||
Args:
|
||||
vector(list of lists): HKL values to be scanned
|
||||
readables(list of Readable): Sensors to be sampled on each step.
|
||||
latency(float, optional): settling time for each step before readout, defaults to 0.0.
|
||||
passes(int, optional): number of passes
|
||||
pars(keyworded variable length arguments, optional): scan optional named arguments:
|
||||
- title(str, optional): plotting window name.
|
||||
- hidden(bool, optional): if true generates no effects on user interface.
|
||||
- before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
|
||||
- after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
|
||||
- before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
|
||||
- after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
|
||||
- Aditional arguments defined by set_exec_pars.
|
||||
Returns:
|
||||
ScanResult object.
|
||||
|
||||
"""
|
||||
readables=to_list(string_to_obj(readables))
|
||||
pars["initial_move"] = False
|
||||
scan = ManualScan([h,k,l], readables ,vector[0], vector[-1], [len(vector)-1] * 3, dimensions = 1)
|
||||
if not "domain_axis" in pars.keys():
|
||||
pars["domain_axis"] = "Index"
|
||||
processScanPars(scan, pars)
|
||||
scan.start()
|
||||
try:
|
||||
for pos in vector:
|
||||
#print "Writing ", pos
|
||||
hkl_group.write(pos)
|
||||
time.sleep(0.1) #Make sure is busy
|
||||
get_motor_group().update()
|
||||
get_motor_group().waitReady(-1)
|
||||
time.sleep(latency)
|
||||
hkl_group.update()
|
||||
scan.append ([h.take(), k.take(), l.take()], [h.getPosition(), k.getPosition(), l.getPosition()], [readable.read() for readable in readables ])
|
||||
finally:
|
||||
scan.end()
|
||||
return scan.result
|
||||
|
||||
|
||||
def test_diffcalc():
|
||||
print "Start test"
|
||||
en.move(20.0)
|
||||
delta.config.maxSpeed = 50.0
|
||||
delta.speed = 50.0
|
||||
delta.move(1.0)
|
||||
|
||||
#Setup
|
||||
setup_diff(sixc, en)
|
||||
setup_axis('gam', 0, 179)
|
||||
setup_axis('delta', 0, 179)
|
||||
setup_axis('delta', min=0)
|
||||
setup_axis('phi', cut=-180.0)
|
||||
print_axis_setup()
|
||||
|
||||
#Orientation
|
||||
help(ub.ub)
|
||||
ub.listub()
|
||||
# Create a new ub calculation and set lattice parameters
|
||||
ub.newub('test')
|
||||
ub.setlat('cubic', 1, 1, 1, 90, 90, 90)
|
||||
# Add 1st reflection (demonstrating the hardware adapter)
|
||||
settings.hardware.wavelength = 1
|
||||
ub.c2th([1, 0, 0]) # energy from hardware
|
||||
settings.hardware.position = 0, 60, 0, 30, 0, 0
|
||||
ub.addref([1, 0, 0])# energy and position from hardware
|
||||
# Add 2nd reflection (this time without the harware adapter)
|
||||
ub.c2th([0, 1, 0], 12.39842)
|
||||
ub.addref([0, 1, 0], [0, 60, 0, 30, 0, 90], 12.39842)
|
||||
# check the state
|
||||
ub.ub()
|
||||
ub.checkub()
|
||||
|
||||
#Constraints
|
||||
help(hkl.con)
|
||||
hkl.con('qaz', 90)
|
||||
hkl.con('a_eq_b')
|
||||
hkl.con('mu', 0)
|
||||
hkl.con()
|
||||
|
||||
#Motion
|
||||
print angles_to_hkl((0., 60., 0., 30., 0., 0.))
|
||||
print hkl_to_angles(1, 0, 0)
|
||||
sixc.write([0, 60, 0, 30, 90, 0])
|
||||
print "sixc=" , sixc.position
|
||||
wavelength.write(1.0)
|
||||
print "wavelength = ", wavelength.read()
|
||||
ub.lastub()
|
||||
ub.setu ([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
ub.showref()
|
||||
ub.swapref(1,2)
|
||||
#print you.hkl
|
||||
#pos(get_hkl())
|
||||
hkl_group.read()
|
||||
#you.hkl.simulateMoveTo([0,1,1])
|
||||
#sim(get_hkl(), [0,1,1])
|
||||
hkl_group.sim([0.0,1.0,1.0])
|
||||
#pos(get_hkl(), [0,1,1])
|
||||
hkl_group.write([0.0,1.0,1.0])
|
||||
|
||||
#Scans
|
||||
lscan(l, [sin], 1.0, 1.5, 0.1)
|
||||
ascan([k,l], [sin], [1.0, 1.0], [1.2, 1.3], [0.1, 0.1], zigzag=True, parallel_positioning = False)
|
||||
vector = [[1.0,1.0,1.0], [1.0,1.0,1.1], [1.0,1.0,1.2], [1.0,1.0,1.4]]
|
||||
hklscan(vector, [sin, arr], 0.9)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
#run("diffutils")
|
||||
|
||||
###################################################################################################\
|
||||
#Setup
|
||||
###################################################################################################
|
||||
|
||||
#alpha, delta, gamma, omegaV
|
||||
setup_diff(fourcv, energy, ("mu", "delta", "gam", "eta"))
|
||||
print_axis_setup()
|
||||
|
||||
|
||||
setup_axis(mu, mu.getMinValue(), mu.getMaxValue())
|
||||
setup_axis(delta, delta.getMinValue(), 90) #delta.getMaxValue())
|
||||
setup_axis(gamma, 0, gamma.getMaxValue())
|
||||
setup_axis(eta, eta.getMinValue(), eta.getMaxValue())
|
||||
|
||||
|
||||
wavelength.write(1.0)
|
||||
|
||||
|
||||
###################################################################################################\
|
||||
#Orientation
|
||||
###################################################################################################
|
||||
help(ub.ub)
|
||||
ub.listub()
|
||||
|
||||
#alpha delta gamma omegaV
|
||||
|
||||
# Create a new ub calculation and set lattice parameters
|
||||
ub.newub('test')
|
||||
|
||||
"""
|
||||
ub.setlat('cubic', 5.114, 5.8361, 11.058, 90, 90, 90)
|
||||
en = 8
|
||||
#ub.c2th([0, 0, 4], en)
|
||||
#ub.addref([0, 0, 4]) #From current position and ekergy
|
||||
ub.addref([0, 0, 4], [16.2785, 0.0, 32.5568, 0.0], en)
|
||||
#ub.c2th([2, 0, 12], en)
|
||||
ub.addref([2, 0, 12], [71.8285, 37.3082, 138.7440, 0.0], en)
|
||||
#ub.c2th([1, -4, 10], en)
|
||||
ub.addref([1, -4, 10], [27.7185, 17.6409 , 128.4220, 0.0], en)
|
||||
"""
|
||||
|
||||
ub.setlat('cubic', 1.0, 1.0, 1.0, 90, 90, 90)
|
||||
|
||||
en = 12.4
|
||||
#ub.c2th([0, 0, 4], en)
|
||||
#ub.addref([0, 0, 4]) #From current position and ekergy
|
||||
ub.addref([0, 0, 1], [30.0, 0.0, 60.0, 0.0], en)
|
||||
#ub.c2th([2, 0, 12], en)
|
||||
ub.addref([1, 0, 1], [20.0, 45.5564,90.000, 44.4437], en)
|
||||
#ub.c2th([1, -4, 10], en)
|
||||
ub.addref([0, 1, 1], [20.0, 45.5564,90.000, 134.4437], en)
|
||||
|
||||
ub.ub()
|
||||
|
||||
#ub.setub([[1.22862,0.00000,0.00000], [-0.00000,1.07663,0.00000], [-0.00000,-0.00000,0.56820]])
|
||||
|
||||
|
||||
# check the state
|
||||
|
||||
ub.checkub()
|
||||
|
||||
|
||||
|
||||
###################################################################################################\
|
||||
#Constraints
|
||||
###################################################################################################
|
||||
help(hkl.con)
|
||||
hkl.con('a_eq_b')
|
||||
#hkl.con('eta:0')
|
||||
|
||||
|
||||
#hkl.con( 'eta', 0) #OmegaV constant
|
||||
#hkl.con( 'mu', 20) #Alpha constant
|
||||
|
||||
|
||||
###################################################################################################\
|
||||
#Motion
|
||||
###################################################################################################
|
||||
|
||||
#print angles_to_hkl((16.278, 0.0000, 32.5568, 0.0))
|
||||
#print angles_to_hkl((44.3400, 0.0000, 123.7322 , 0.0))
|
||||
#print hkl_to_angles(2, -2, 10)
|
||||
|
||||
|
||||
154
script/test/TestNilson.py
Normal file
154
script/test/TestNilson.py
Normal file
@@ -0,0 +1,154 @@
|
||||
# Greateyes scan
|
||||
|
||||
#******** SCAN Settings ********
|
||||
Note="DESY - "
|
||||
|
||||
Exp_Time= 1
|
||||
|
||||
X0=4200
|
||||
Xstep=450
|
||||
XNum_points=3
|
||||
|
||||
Y0=5960
|
||||
Ystep=-40
|
||||
YNum_points=10
|
||||
|
||||
GE_ROI_Line = 3
|
||||
|
||||
GE_AreaDet = det
|
||||
|
||||
GE_ROI_Image= det.getDataMatrix()
|
||||
GE_Raw_Image= det.getDataMatrix()
|
||||
GE_BG_Image = det.getDataMatrix()
|
||||
GE_Spectrum_Conv = det.getDataArray()
|
||||
GE_Spectrum = det.getDataArray()
|
||||
GE_BG_Line = det.getArraySize2()
|
||||
GE_BG_SizeX = det.getArraySize0()
|
||||
GE_BG_SizeY = det.getArraySize1()
|
||||
GE_ROI_Line = det.getArraySize2()
|
||||
GE_ROI_SizeX = det.getArraySize0()
|
||||
GE_ROI_SizeY = det.getArraySize1()
|
||||
IZero_Profile = det.getDataArray()
|
||||
TFY_Profile = det.getDataArray()
|
||||
IZero=sin
|
||||
TFY = sin
|
||||
GE_Sensor_Temp = sin
|
||||
GE_FrameID = sin
|
||||
Press_Diag_PV= sin
|
||||
Press_Spec_PV= sin
|
||||
Press_Spec_HV =sin
|
||||
Press_Sample_Ch=sin
|
||||
Press_Diag_HV = sin
|
||||
GE_Spectrum_Sum = det.getDataArray()
|
||||
|
||||
#******** Functions Definitions ********
|
||||
def Save_Pre_Scan_Data():
|
||||
save_dataset("RAW/GE_BG_Image", GE_BG_Image.read())
|
||||
save_dataset("Processed/GE_Spectrum_Convertion", GE_Spectrum_Conv.read())
|
||||
save_dataset("Detector/GE_ROI_Line", GE_ROI_Line.read())
|
||||
save_dataset("Detector/GE_ROI_SizeX", GE_ROI_SizeX.read())
|
||||
save_dataset("Detector/GE_ROI_SizeY", GE_ROI_SizeY.read())
|
||||
save_dataset("Detector/Exposure_Time", GE_AreaDet.getExposure())
|
||||
save_dataset("Detector/GE_Open_Delay", 1)
|
||||
save_dataset("Detector/GE_Close_Delay", 1)
|
||||
save_dataset("Detector/GE_Num_Images", GE_AreaDet.getNumImages())
|
||||
save_dataset("Detector/Gain_Type", 1)
|
||||
save_dataset("Scan/Scan_Start_Time", time.ctime())
|
||||
save_dataset("Scan/Note", Note)
|
||||
#save_dataset("Scan/Note", Note)
|
||||
|
||||
def Create_Scan_Dataset():
|
||||
create_dataset("RAW/GE_Raw_Image", 'd', False, (Num_Images, int(GE_BG_SizeY.take()), int(GE_BG_SizeX.take())))
|
||||
create_dataset("RAW/IZero_Profile", 'd', False, (Num_Images, 100))
|
||||
create_dataset("RAW/TFY_Profile", 'd', False, (Num_Images, 100))
|
||||
create_dataset("Processed/GE_ROI_Image", 'd', False, (Num_Images, int(GE_ROI_SizeY.take()), int(GE_ROI_SizeX.take())))
|
||||
create_dataset("Processed/GE_Spectrum", 'd', False, (Num_Images, int(GE_BG_SizeX.take())))
|
||||
create_dataset("Processed/Izero", 'd', False)
|
||||
create_dataset("Processed/TFY", 'd', False)
|
||||
create_dataset("Detector/GE_Sensor_Temp", 'd', False)
|
||||
create_dataset("Scan/GE_FrameID", 'i', False)
|
||||
create_dataset("Scan/Timestamps", 'l', False)
|
||||
create_dataset("Pressure/Diagnostic_PV", 'd', False)
|
||||
create_dataset("Pressure/Diagnostic_HV", 'd', False)
|
||||
create_dataset("Pressure/Spectrometer_PV", 'd', False)
|
||||
create_dataset("Pressure/Spectrometer_HV", 'd', False)
|
||||
create_dataset("Pressure/Sample_Chamber", 'd', False)
|
||||
create_dataset("RAW/Xposition", 'd', False)
|
||||
create_dataset("RAW/Yposition", 'd', False)
|
||||
|
||||
|
||||
|
||||
def Save_Scan_Data():
|
||||
#sleep(0.2)
|
||||
append_dataset("RAW/GE_Raw_Image", GE_Raw_Image.read())
|
||||
append_dataset("RAW/IZero_Profile", IZero_Profile.take())
|
||||
append_dataset("RAW/TFY_Profile", TFY_Profile.take())
|
||||
append_dataset("Processed/GE_ROI_Image", GE_ROI_Image.read())
|
||||
append_dataset("Processed/GE_Spectrum", GE_Spectrum.take())
|
||||
append_dataset("Processed/Izero", IZero.take())
|
||||
append_dataset("Processed/TFY", TFY.take())
|
||||
append_dataset("Detector/GE_Sensor_Temp", GE_Sensor_Temp.take())
|
||||
append_dataset("Scan/GE_FrameID", GE_FrameID.take())
|
||||
append_dataset("Scan/Timestamps", GE_FrameID.getTimestampNanos())
|
||||
append_dataset("Pressure/Diagnostic_PV", Press_Diag_PV.take())
|
||||
append_dataset("Pressure/Diagnostic_HV", Press_Diag_HV.take())
|
||||
append_dataset("Pressure/Spectrometer_PV", Press_Spec_PV.take())
|
||||
append_dataset("Pressure/Spectrometer_HV", Press_Spec_HV.take())
|
||||
append_dataset("Pressure/Sample_Chamber", Press_Sample_Ch.take())
|
||||
#Scan_Progress = (100*calcprog(GE_FrameID.take(),GE_start_frame,Num_Images))
|
||||
|
||||
def Save_Pos_Scan_Data():
|
||||
sleep(1)
|
||||
save_dataset("Processed/GE_Spectrum_Sum", GE_Spectrum_Sum.read())
|
||||
save_dataset("Scan/Scan_Finish_Time", time.ctime())
|
||||
|
||||
#******** Test limits on sample vertical and horizontal motor ********
|
||||
X0=float(X0)
|
||||
Xstep=float(Xstep)
|
||||
Y0=float(Y0)
|
||||
Ystep=float(Ystep)
|
||||
|
||||
X1=X0+(XNum_points*Xstep)
|
||||
Y1=Y0+(YNum_points*Ystep)
|
||||
Num_Images= XNum_points*YNum_points
|
||||
|
||||
#******** Setting up Caenels ********
|
||||
values_p_reading=1000*Exp_Time
|
||||
Scan_Progress = 1
|
||||
|
||||
############### Scan ###############
|
||||
|
||||
|
||||
#******** Scan Script Begins here ********
|
||||
|
||||
# Set Nr of images to NofImages
|
||||
#******** Saving Pre Scan data ********
|
||||
Save_Pre_Scan_Data()
|
||||
|
||||
#******** Pre Scan Setup ********
|
||||
print("Scan starting: " + time.ctime())
|
||||
GE_start_frame =1
|
||||
Create_Scan_Dataset()
|
||||
#Sample_Horiz.move(X0)
|
||||
|
||||
|
||||
#******** Main Scan Function ********
|
||||
|
||||
for j in range(XNum_points):
|
||||
#Sample_Horiz.move(X0+(j*Xstep))
|
||||
for i in range(YNum_points):
|
||||
#Sample_Vert.move(Y0+(i*Ystep))
|
||||
print GE_start_frame , " / ", (XNum_points*YNum_points)
|
||||
time.sleep(0.001)
|
||||
Save_Scan_Data()
|
||||
GE_start_frame = GE_start_frame+1
|
||||
|
||||
#Save_Scan_Data()
|
||||
#Save_Scan_Data()
|
||||
#******** Saving Pos Scan data ********
|
||||
Save_Pos_Scan_Data()
|
||||
|
||||
#******** Post Scan ********
|
||||
print("Scan Finished: " + time.ctime())
|
||||
|
||||
|
||||
172
script/test/TestNilson2.py
Normal file
172
script/test/TestNilson2.py
Normal file
@@ -0,0 +1,172 @@
|
||||
#features = {"compression" : "true", "shuffle": "false", "chunk":[1, 100, 200]}
|
||||
features = {"compression" : True, "shuffle": False}
|
||||
#features =None
|
||||
FIXED = False
|
||||
# Greateyes scan
|
||||
|
||||
|
||||
|
||||
#******** SCAN Settings ********
|
||||
Note="DESY - "
|
||||
|
||||
Exp_Time= 1
|
||||
|
||||
X0=4200
|
||||
Xstep=450
|
||||
XNum_points=10
|
||||
|
||||
Y0=5960
|
||||
Ystep=-40
|
||||
YNum_points=10
|
||||
|
||||
GE_ROI_Line = 3
|
||||
|
||||
GE_AreaDet = det
|
||||
|
||||
GE_ROI_Image= det.getDataMatrix()
|
||||
GE_Raw_Image= det.getDataMatrix()
|
||||
GE_BG_Image = det.getDataMatrix()
|
||||
GE_Spectrum_Conv = arr #det.getDataArray()
|
||||
GE_Spectrum = arr #det.getDataArray()
|
||||
GE_BG_Line = det.getArraySize2()
|
||||
GE_BG_SizeX = det.getArraySize0()
|
||||
GE_BG_SizeY = det.getArraySize1()
|
||||
GE_ROI_Line = det.getArraySize2()
|
||||
GE_ROI_SizeX = det.getArraySize0()
|
||||
GE_ROI_SizeY = det.getArraySize1()
|
||||
IZero_Profile = arr #det.getDataArray()
|
||||
TFY_Profile = arr #det.getDataArray()
|
||||
IZero=sin
|
||||
TFY = sin
|
||||
GE_Sensor_Temp = sin
|
||||
GE_FrameID = sin
|
||||
Press_Diag_PV= sin
|
||||
Press_Spec_PV= sin
|
||||
Press_Spec_HV =sin
|
||||
Press_Sample_Ch=sin
|
||||
Press_Diag_HV = sin
|
||||
GE_Spectrum_Sum = det.getDataArray()
|
||||
Sample_Horiz=motor
|
||||
Sample_Vert=motor2
|
||||
|
||||
#******** Functions Definitions ********
|
||||
def Save_Pre_Scan_Data():
|
||||
save_dataset("RAW/GE_BG_Image", GE_BG_Image.read())
|
||||
save_dataset("Processed/GE_Spectrum_Convertion", GE_Spectrum_Conv.read())
|
||||
save_dataset("Detector/GE_ROI_Line", GE_ROI_Line.read())
|
||||
save_dataset("Detector/GE_ROI_SizeX", GE_ROI_SizeX.read())
|
||||
save_dataset("Detector/GE_ROI_SizeY", GE_ROI_SizeY.read())
|
||||
save_dataset("Detector/Exposure_Time", GE_AreaDet.getExposure())
|
||||
save_dataset("Detector/GE_Open_Delay", 1)
|
||||
save_dataset("Detector/GE_Close_Delay", 1)
|
||||
save_dataset("Detector/GE_Num_Images", GE_AreaDet.getNumImages())
|
||||
save_dataset("Detector/Gain_Type", 1)
|
||||
save_dataset("Scan/Scan_Start_Time", time.ctime())
|
||||
save_dataset("Scan/Note", Note)
|
||||
#save_dataset("Scan/Note", Note)
|
||||
|
||||
def Create_Scan_Dataset():
|
||||
create_dataset("RAW/GE_Raw_Image", 'd', False, (Num_Images if FIXED else 0, int(GE_BG_SizeY.take()), int(GE_BG_SizeX.take())), features)
|
||||
create_dataset("RAW/IZero_Profile", 'd', False, (Num_Images if FIXED else 0, 100))
|
||||
create_dataset("RAW/TFY_Profile", 'd', False, (Num_Images if FIXED else 0, 100))
|
||||
#create_dataset("Processed/GE_ROI_Image", 'd', False, (Num_Images if FIXED else 0Images, int(GE_ROI_SizeY.take()), int(GE_ROI_SizeX.take())))
|
||||
create_dataset("Processed/GE_Spectrum", 'd', False, (Num_Images if FIXED else 0, int(GE_BG_SizeX.take())))
|
||||
create_dataset("Processed/Izero", 'd', False)
|
||||
create_dataset("Processed/TFY", 'd', False)
|
||||
create_dataset("Detector/GE_Sensor_Temp", 'd', False)
|
||||
create_dataset("Scan/GE_FrameID", 'i', False)
|
||||
create_dataset("Scan/Timestamps", 'l', False)
|
||||
create_dataset("Pressure/Diagnostic_PV", 'd', False)
|
||||
create_dataset("Pressure/Diagnostic_HV", 'd', False)
|
||||
create_dataset("Pressure/Spectrometer_PV", 'd', False)
|
||||
create_dataset("Pressure/Spectrometer_HV", 'd', False)
|
||||
create_dataset("Pressure/Sample_Chamber", 'd', False)
|
||||
create_dataset("RAW/Xposition", 'd', False)
|
||||
create_dataset("RAW/Yposition", 'd', False)
|
||||
|
||||
|
||||
|
||||
def Save_Scan_Data():
|
||||
#sleep(0.2)
|
||||
append_dataset("RAW/GE_Raw_Image", GE_Raw_Image.read())
|
||||
append_dataset("RAW/IZero_Profile", IZero_Profile.take())
|
||||
append_dataset("RAW/TFY_Profile", TFY_Profile.take())
|
||||
#append_dataset("Processed/GE_ROI_Image", GE_ROI_Image.read())
|
||||
append_dataset("Processed/GE_Spectrum", GE_Spectrum.take())
|
||||
append_dataset("Processed/Izero", IZero.take())
|
||||
append_dataset("Processed/TFY", TFY.take())
|
||||
append_dataset("Detector/GE_Sensor_Temp", GE_Sensor_Temp.take())
|
||||
append_dataset("Scan/GE_FrameID", GE_FrameID.take())
|
||||
append_dataset("Scan/Timestamps", GE_FrameID.getTimestampNanos())
|
||||
append_dataset("Pressure/Diagnostic_PV", Press_Diag_PV.take())
|
||||
append_dataset("Pressure/Diagnostic_HV", Press_Diag_HV.take())
|
||||
append_dataset("Pressure/Spectrometer_PV", Press_Spec_PV.take())
|
||||
append_dataset("Pressure/Spectrometer_HV", Press_Spec_HV.take())
|
||||
append_dataset("Pressure/Sample_Chamber", Press_Sample_Ch.take())
|
||||
#Scan_Progress = (100*calcprog(GE_FrameID.take(),GE_start_frame,Num_Images))
|
||||
|
||||
def Save_Pos_Scan_Data():
|
||||
sleep(1)
|
||||
save_dataset("Processed/GE_Spectrum_Sum", GE_Spectrum_Sum.read())
|
||||
save_dataset("Scan/Scan_Finish_Time", time.ctime())
|
||||
|
||||
#******** Test limits on sample vertical and horizontal motor ********
|
||||
X0=float(X0)
|
||||
Xstep=float(Xstep)
|
||||
Y0=float(Y0)
|
||||
Ystep=float(Ystep)
|
||||
|
||||
X1=X0+(XNum_points*Xstep)
|
||||
Y1=Y0+(YNum_points*Ystep)
|
||||
Num_Images= XNum_points*YNum_points
|
||||
|
||||
#******** Setting up Caenels ********
|
||||
values_p_reading=1000*Exp_Time
|
||||
Scan_Progress = 1
|
||||
|
||||
############### Scan ###############
|
||||
|
||||
create_dataset("RAW/GE_Raw_Image2", 'd', False, (Num_Images if FIXED else 0, int(GE_BG_SizeY.take()), int(GE_BG_SizeX.take())), features)
|
||||
|
||||
|
||||
|
||||
|
||||
Save_Pre_Scan_Data()
|
||||
|
||||
#******** Pre Scan Setup ********
|
||||
print("Scan starting: " + time.ctime())
|
||||
GE_start_frame =1
|
||||
Create_Scan_Dataset()
|
||||
|
||||
#Sample_Horiz.move(X0)
|
||||
|
||||
|
||||
#******** Main Scan Function ********
|
||||
#XNum_points, YNum_points 3, 4
|
||||
X0, Y0, Xstep, Ystep = 0, 0, 0.1, 0.1
|
||||
|
||||
for j in range(XNum_points):
|
||||
#Sample_Horiz.move(X0+(j*Xstep))
|
||||
for i in range(YNum_points):
|
||||
#Sample_Vert.move(Y0+(i*Ystep))
|
||||
print GE_start_frame , " / ", (XNum_points*YNum_points)
|
||||
time.sleep(0.001)
|
||||
Save_Scan_Data()
|
||||
GE_start_frame = GE_start_frame+1
|
||||
|
||||
|
||||
"""
|
||||
def after(record, scan):
|
||||
Save_Scan_Data()
|
||||
|
||||
ascan((Sample_Horiz, Sample_Vert), (GE_Raw_Image), (X0, Y0), (X1, Y1), (Xstep, Ystep), after_read = after)
|
||||
"""
|
||||
#Save_Scan_Data()
|
||||
#Save_Scan_Data()
|
||||
#******** Saving Pos Scan data ********
|
||||
Save_Pos_Scan_Data()
|
||||
|
||||
#******** Post Scan ********
|
||||
print("Scan Finished: " + time.ctime())
|
||||
|
||||
|
||||
23
script/test/TestNilson3.py
Normal file
23
script/test/TestNilson3.py
Normal file
@@ -0,0 +1,23 @@
|
||||
features = None
|
||||
size = 5
|
||||
typ = 'd'
|
||||
features = {"compression" : True, "shuffle": True}
|
||||
|
||||
#features = {"layout" : "chunked", "chunk_size":[1, 500, 1000]}
|
||||
|
||||
|
||||
create_dataset("group/contiguous", typ, False, (size, 1000, 1000), features)
|
||||
create_dataset("group/chunked", typ, False, (0, 1000, 1000), features)
|
||||
|
||||
|
||||
|
||||
|
||||
for i in range(size):
|
||||
data = [ [int(i)] * 1000, ] * 1000
|
||||
#data = Convert.flatten(to_array(data, typ))
|
||||
append_dataset("group/chunked", data )
|
||||
append_dataset("group/contiguous", data)
|
||||
|
||||
|
||||
|
||||
|
||||
17
script/test/TestPID.py
Normal file
17
script/test/TestPID.py
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
PID = get_context().getClassByName("PID")
|
||||
|
||||
|
||||
|
||||
pid=PID(1,1,1);
|
||||
|
||||
|
||||
while True:
|
||||
sensor = sin.read()
|
||||
target = 30.0
|
||||
#set some sort of target value
|
||||
output=pid.getOutput(sensor,target);
|
||||
print output
|
||||
#do something with the output
|
||||
inp.write(output)
|
||||
time.sleep(1.0)
|
||||
5
script/test/TestPlotLayout.py
Normal file
5
script/test/TestPlotLayout.py
Normal file
@@ -0,0 +1,5 @@
|
||||
tscan([out, inp, sin, arr], 10, 0.1)
|
||||
set_exec_pars(plot_layout = "Grid")
|
||||
tscan([out, inp, sin, arr], 10, 0.1, plot_layout = "Grid")
|
||||
set_exec_pars(defaults=True)
|
||||
tscan([out, inp, sin, arr], 10, 0.1)
|
||||
2
script/test/TestThen.py
Normal file
2
script/test/TestThen.py
Normal file
@@ -0,0 +1,2 @@
|
||||
lscan(inp, sin, 0, 10, 0.1)
|
||||
set_exec_pars(then="lscan(inp, sin, 0, 10, 0.2)")
|
||||
143
script/test/TestTiff.py
Normal file
143
script/test/TestTiff.py
Normal file
@@ -0,0 +1,143 @@
|
||||
from ijutils import *
|
||||
|
||||
|
||||
#Image Loading
|
||||
ip = load_image("/Users/gobbo_a/dev/pshell/config/home/images/img.tiff", title="Image")
|
||||
|
||||
#Basic image manipulation: creation, copying, padding, saving
|
||||
resized = resize(ip, 300,300)
|
||||
save_image(resized, get_context().setup.expandPath("{images}/resized.tiff") ,"tiff")
|
||||
crop=sub_image(ip,10,20,50,30)
|
||||
bin_im = binning(ip,2)
|
||||
new_im = new_image(256, 256, "color")
|
||||
copy_image_to(bin_im, new_im, 20, 20)
|
||||
pad_im = pad_image(ip, 1, 2, 3, 4, Color.RED)
|
||||
stack=create_stack([ip,resized,crop, bin_im, new_im, pad_im], title = "Basic Functions")
|
||||
save_image(stack, get_context().setup.expandPath("{images}/stack.tiff") ,"tiff")
|
||||
stack.show()
|
||||
|
||||
|
||||
#Decomposing color channels
|
||||
|
||||
create_stack([ get_channel(ip, "red"),
|
||||
get_channel(ip, "green"),
|
||||
get_channel(ip, "blue"),
|
||||
get_channel(ip, "alpha"),
|
||||
grayscale(get_channel(ip, "brightness"), False)], title = "Color Decomposition").show()
|
||||
|
||||
|
||||
#Basic functions (in_place)
|
||||
aux = ip.duplicate()
|
||||
aux.show()
|
||||
grayscale(aux)
|
||||
gaussian_blur(aux); aux.repaintWindow()
|
||||
invert(aux); aux.repaintWindow()
|
||||
smooth(aux); aux.repaintWindow()
|
||||
sharpen(aux); aux.repaintWindow()
|
||||
noise(aux, 100); aux.repaintWindow()
|
||||
|
||||
|
||||
#Changing LUT
|
||||
aux = ip.duplicate()
|
||||
aux = grayscale(aux, in_place=False)
|
||||
r,g,b = [],[],[]
|
||||
for i in range(256):
|
||||
r.append(0)
|
||||
g.append(0)
|
||||
b.append(i)
|
||||
set_lut(aux, r, g, b)
|
||||
aux.show()
|
||||
|
||||
|
||||
#Histogram
|
||||
plot(get_histogram(ip))
|
||||
|
||||
|
||||
aux = grayscale(ip, in_place = False)
|
||||
bin = ip.duplicate()
|
||||
ip_bin = auto_threshold(aux, in_place=False)
|
||||
create_stack([ ip_bin,
|
||||
binary_fill_holes(ip_bin, in_place=False),
|
||||
binary_outline(ip_bin, in_place=False),
|
||||
binary_outline(binary_fill_holes(ip_bin, in_place=False)),
|
||||
binary_dilate(ip_bin, in_place=False),
|
||||
binary_erode(ip_bin, in_place=False),
|
||||
binary_open(ip_bin, in_place=False),
|
||||
binary_close(ip_bin, in_place=False),
|
||||
binary_skeletonize(ip_bin, in_place=False)], title = "Binarization").show()
|
||||
|
||||
|
||||
#EDM, const & image operations
|
||||
aux = grayscale(ip, in_place = False)
|
||||
ip_bin = auto_threshold(aux, in_place=False)
|
||||
binary_fill_holes(ip_bin)
|
||||
|
||||
edm = edm(ip_bin, in_place=False)
|
||||
ws = watershed(ip_bin, in_place=False)
|
||||
up = ultimate_points(ip_bin, in_place=False)
|
||||
vr = veronoi(ip_bin, in_place=False)
|
||||
edm_disp = remap(edm, in_place=False)
|
||||
ws_disp = grayscale(ws, False)
|
||||
up_disp = enhance_contrast(up, in_place=False)
|
||||
vr_disp = enhance_contrast(vr, in_place=False)
|
||||
create_stack([edm_disp, aux, ip_bin, ws_disp, up_disp, vr_disp], title = "EDM Operations").show()
|
||||
final = grayscale(ip_bin, in_place = False)
|
||||
op_const(final,"add", -200)
|
||||
op_image(final, vr_disp, 'or')
|
||||
op_image(final, up_disp, 'or')
|
||||
final.show()
|
||||
|
||||
aux = grayscale(ip, in_place = False)
|
||||
|
||||
create_stack([ aux,
|
||||
subtract_background(aux, in_place=False),
|
||||
smooth(aux, False),
|
||||
sharpen(aux, False),
|
||||
edges(aux, False),
|
||||
bandpass_filter(aux,0, 5, in_place=False),
|
||||
bandpass_filter(aux,5, 100, in_place=False),
|
||||
op_const(aux,"and", 127, False),
|
||||
convolve(aux, KERNEL_BLUR, False),
|
||||
convolve(aux, KERNEL_SHARPEN, False),
|
||||
convolve(aux, KERNEL_SHARPEN_2, False),
|
||||
convolve(aux, KERNEL_LIGHT, False),
|
||||
convolve(aux, KERNEL_DARK, False),
|
||||
convolve(aux, KERNEL_EDGE_DETECT, False),
|
||||
convolve(aux, KERNEL_EDGE_DETECT_2, False),
|
||||
convolve(aux, KERNEL_DIFFERENTIAL_EDGE_DETECT, False),
|
||||
convolve(aux, KERNEL_PREWITT, False),
|
||||
convolve(aux, KERNEL_SOBEL, False)
|
||||
], title = "General Operations").show()
|
||||
|
||||
|
||||
#Rank operators
|
||||
rank_opers = []
|
||||
for op in "mean", "min", "max", "variance", "median", "close_maxima", "open_maxima", "remove_outliers", "remove_nan", "despeckle":
|
||||
rank_opers.append(op_rank(aux,op, in_place=False, kernel_radius=1))
|
||||
create_stack(rank_opers, title = "Rank Operations").show()
|
||||
|
||||
|
||||
#Reslicing
|
||||
#orig = load_image("{data}/img/img2.png")
|
||||
orig = resize(ip, 300,200)
|
||||
grayscale(orig)
|
||||
images=[]
|
||||
for i in range (20):
|
||||
images.append(orig.duplicate())
|
||||
op_const(orig, "multiply", 0.9)
|
||||
stack=create_stack(images, title = "Original Stack")
|
||||
#stack.show()
|
||||
r1 = reslice(stack, start_at="Left", title="Reslice Horizontally")
|
||||
r2 = reslice(stack, start_at="Top", title="Reslice Vertically")
|
||||
r1.show()
|
||||
r2.show()
|
||||
|
||||
|
||||
#Particle Analysis
|
||||
aux = grayscale(ip, in_place = False)
|
||||
auto_threshold(aux)
|
||||
#binary_fill_holes(aux)
|
||||
#aux.show()
|
||||
(results,output_img)=analyse_particles(aux, 100,1000, print_table=True)
|
||||
output_img.show()
|
||||
|
||||
31
script/test/TestTypes.py
Normal file
31
script/test/TestTypes.py
Normal file
@@ -0,0 +1,31 @@
|
||||
class StringDev(ReadonlyRegisterBase, Readable.StringType):
|
||||
def doRead(self):
|
||||
time.sleep(0.001)
|
||||
return str(time.time())
|
||||
|
||||
class BoolDevS(ReadonlyRegisterBase):
|
||||
def doRead(self):
|
||||
return (int(time.time()) %2) == 0
|
||||
|
||||
class BoolDev(ReadonlyRegisterBase, Readable.BooleanType):
|
||||
def doRead(self):
|
||||
return (int(time.time()) %2) == 0
|
||||
|
||||
class ImageDev(ReadonlyRegisterBase, ReadonlyRegisterMatrix, Readable.IntegerType):
|
||||
def doRead(self):
|
||||
return mt1.read()
|
||||
|
||||
def getWidth(self):
|
||||
return mt1.width
|
||||
|
||||
def getHeight(self):
|
||||
return mt1.height
|
||||
|
||||
|
||||
|
||||
add_device(StringDev(), True)
|
||||
add_device(BoolDev(), True)
|
||||
add_device(BoolDevS(), True)
|
||||
add_device(ImageDev(), True)
|
||||
|
||||
tscan((StringDev, BoolDev, BoolDevS, sin, arr, arr1, mt, mt1, cm1, ImageDev), 10, 0.1, compression =ImageDev)
|
||||
55
script/test/test_multipass.py
Normal file
55
script/test/test_multipass.py
Normal file
@@ -0,0 +1,55 @@
|
||||
def set_energy(v):
|
||||
motor.move(v)
|
||||
energy_setpoint = motor
|
||||
energy = motor.getReadback()
|
||||
set_device_alias(energy, "energy")
|
||||
|
||||
|
||||
START, STOP, STEP = 0, 40, 2
|
||||
ENERGIES = [0.0, 0.5, 0.25]
|
||||
sensors = (sin,out, energy)
|
||||
|
||||
|
||||
|
||||
#def before_pass(pass_num):
|
||||
# set_energy(ENERGIES[pass_num-1])
|
||||
#ret= lscan(inp, sensors, START, STOP, STEP, 0.2, passes = len(ENERGIES), before_pass = before_pass, latency = 0.5)
|
||||
|
||||
|
||||
#positions = frange(START, STOP, STEP , True)
|
||||
#vector = [[pos, en] for en in ENERGIES for pos in positions]
|
||||
#ret = vscan((inp,energy_setpoint), sensors, vector, line = True, latency = 0.5)
|
||||
|
||||
|
||||
"""
|
||||
plots = plot([None]*len(sensors), name = [d.name for d in sensors])
|
||||
for p in plots: p.clear()
|
||||
def AfterReadout(record, scan):
|
||||
if record.setpoints[1] == scan.getStart()[1]:
|
||||
for p in plots: p.addSeries(LinePlotSeries(str(record.positions[0])))
|
||||
for i in range(len(plots)):
|
||||
plots[i].getSeries(plots[i].numberOfSeries-1).appendData(record.positions[1], record.values[i])
|
||||
|
||||
class EnergyIndex("Writable):
|
||||
def write(self, value):
|
||||
if not hasattr(self, 'setpoint') or self.setpoint != value:
|
||||
self.setpoint = value
|
||||
set_energy(ENERGIES[int(value)])
|
||||
|
||||
positions = frange(START, STOP, STEP , True)
|
||||
ret = ascan([EnergyIndex(), inp], sensors, (0,START), (len(ENERGIES)-1,STOP), (1.0,STEP), latency = 0.1, after_read=AfterReadout, plot_disabled=True)
|
||||
"""
|
||||
|
||||
plots = plot([None]*len(sensors), name = [d.name for d in sensors])
|
||||
for p in plots:
|
||||
p.clear()
|
||||
p.legendVisible = True
|
||||
def after_readout(record, scan):
|
||||
for i in range(len(plots)):
|
||||
plots[i].getSeries(plots[i].numberOfSeries-1).appendData(record.positions[0], record.values[i])
|
||||
def before_pass(pass_num):
|
||||
en = ENERGIES[pass_num-1]
|
||||
set_energy(en)
|
||||
for p in plots: p.addSeries(LinePlotSeries(str(en)))
|
||||
|
||||
ret= lscan(inp, sensors, START, STOP, STEP, latency = 0.2, passes = len(ENERGIES), before_pass = before_pass, after_read=after_readout, plot_disabled=True)
|
||||
Reference in New Issue
Block a user