diff --git a/plugins/Scienta.java b/plugins/Scienta.java new file mode 100644 index 00000000..2b2154c5 --- /dev/null +++ b/plugins/Scienta.java @@ -0,0 +1,547 @@ +//package ch.psi.pshell.epics; +import ch.psi.pshell.epics.*; +import ch.psi.pshell.device.AccessType; +import ch.psi.pshell.device.ArrayCalibration; +import ch.psi.pshell.device.CameraImageDescriptor; +import ch.psi.pshell.device.MatrixCalibration; +import ch.psi.pshell.device.Device; +import ch.psi.pshell.device.DeviceAdapter; +import ch.psi.pshell.device.Readable.ReadableCalibratedArray; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +/** + * Implementation of Scienta spectrometer analyser. + */ +public class Scienta extends AreaDetector { + + final ChannelInteger slices, frames; + final ChannelDouble lowEnergy, centerEnergy, highEnergy, stepSize, stepTime, energyWidth; + final ChannelDoubleArray spectrum, image, extio; + final ChannelDouble currentChannel, totalPoints; + final ChannelDouble channelBegin, channelEnd, sliceBegin, sliceEnd; + final ChannelString lensMode, acquisitionMode, passEnergy; + final ChannelInteger numChannels, numSlices; + final ChannelDouble acquisitionTime; + final Stats[] stats; + + final String channelCtrl, channelData; + + public Scienta(final String name, final String channelPrefix) { + this(name, channelPrefix + ":cam1", channelPrefix + ":image1"); + } + + public Scienta(String name, String channelCtrl, String channelData) { + super(name, channelCtrl, channelData); + this.channelCtrl = channelCtrl; + this.channelData = channelData; + slices = new ChannelInteger(name + " slices", channelCtrl + ":SLICES", false); + frames = new ChannelInteger(name + " frames", channelCtrl + ":FRAMES", false); + + lowEnergy = new ChannelDouble(name + " low energy", channelCtrl + ":LOW_ENERGY", 3, false); + centerEnergy = new ChannelDouble(name + " center energy", channelCtrl + ":CENTRE_ENERGY", 3, false); + highEnergy = new ChannelDouble(name + " high energy", channelCtrl + ":HIGH_ENERGY", 3, false); + stepSize = new ChannelDouble(name + " step size", channelCtrl + ":STEP_SIZE", 3, false); + stepTime = new ChannelDouble(name + " step time", channelCtrl + ":STEP_TIME", 3, false); + + energyWidth = new ChannelDouble(name + " energy width", channelCtrl + ":ENERGY_WIDTH_RBV", 3, false); + energyWidth.setAccessType(AccessType.Read); + passEnergy = new ChannelString(name + " pass energy", channelCtrl + ":PASS_ENERGY", false); + lensMode = new ChannelString(name + " lens mode", channelCtrl + ":LENS_MODE", false); + acquisitionMode = new ChannelString(name + " lens mode", channelCtrl + ":ACQ_MODE", false); + channelBegin = new ChannelDouble(name + " channel begin", channelCtrl + ":CHANNEL_BEGIN_RBV", 3, false); + channelBegin.setAccessType(AccessType.Read); + channelEnd = new ChannelDouble(name + " channel end", channelCtrl + ":CHANNEL_END_RBV", 3, false); + channelEnd.setAccessType(AccessType.Read); + sliceBegin = new ChannelDouble(name + " slice begin", channelCtrl + ":SLICE_BEGIN_RBV", 3, false); + sliceBegin.setAccessType(AccessType.Read); + sliceEnd = new ChannelDouble(name + " slice end", channelCtrl + ":SLICE_END_RBV", 3, false); + sliceEnd.setAccessType(AccessType.Read); + numChannels = new ChannelInteger(name + " num channels", channelCtrl + ":NUM_CHANNELS_RBV", false); + numChannels.setAccessType(AccessType.Read); + numSlices = new ChannelInteger(name + " num slices", channelCtrl + ":SLICES_RBV", false); + numSlices.setAccessType(AccessType.Read); + numChannels.addListener(new DeviceAdapter() { + @Override + public void onValueChanged(Device device, Object value, Object former) { + try { + if (value != null) { + spectrum.setSize((Integer) value); + } + } catch (IOException ex) { + getLogger().log(Level.WARNING, null, ex); + } + } + }); + acquisitionTime = new ChannelDouble(name + " acquire time", channelCtrl + ":TOTAL_ACQ_TIME_RBV", 3, false); + acquisitionTime.setAccessType(AccessType.Read); + + + /* + spectrum = new ChannelDoubleArray(name + " spectrum", channelCtrl + ":INT_SPECTRUM", 8, 200, false) { + @Override + protected double[] doRead() throws IOException, InterruptedException { + numChannels.getValue(); + return super.doRead(); + } + }; + spectrum.setAccessType(AccessType.Read); + */ + spectrum = new ScientaSpectrum(); + image = new ChannelDoubleArray(name + " image", channelCtrl + ":IMAGE", 8, EpicsRegister.SIZE_MAX, false); + image.setAccessType(AccessType.Read); + extio = new ChannelDoubleArray(name + " extio", channelCtrl + ":EXTIO", 8, EpicsRegister.SIZE_MAX, false); + extio.setAccessType(AccessType.Read); + + currentChannel = new ChannelDouble(name + " current channel", channelCtrl + ":CURRENT_CHANNEL_RBV", 0, false); + currentChannel.setAccessType(AccessType.Read); + totalPoints = new ChannelDouble(name + " total points", channelCtrl + ":TOTAL_POINTS_RBV", 0, false); + totalPoints.setAccessType(AccessType.Read); + + addChildren(new Device[]{slices, frames, + lowEnergy, centerEnergy, highEnergy, stepSize, stepTime, energyWidth, + spectrum, image, extio, + currentChannel, totalPoints, + channelBegin, channelEnd, sliceBegin, sliceEnd, + passEnergy, lensMode, acquisitionMode, + numChannels, numSlices, acquisitionTime + }); + + stats = new Stats[5]; + stats[0] = new Stats("CountsR1", 1); + stats[1] = new Stats("CountsR2", 2); + stats[2] = new Stats("CountsR3", 3); + stats[3] = new Stats("CountsR4", 4); + stats[4] = new Stats("Counts", 5); + addChildren(stats); + } + + public class ScientaSpectrum extends ChannelDoubleArray implements ReadableCalibratedArray { + + ScientaSpectrum() { + super(Scienta.this.getName() + " spectrum", channelCtrl + ":INT_SPECTRUM", 8, 200, false); + setAccessType(AccessType.Read); + } + + @Override + protected double[] doRead() throws IOException, InterruptedException { + numChannels.getValue(); + return super.doRead(); + } + + @Override + public ArrayCalibration getCalibration() { + double scale = 1.0; + double offset = 0.0; + try { + List channelRange = getChannelRange(); + Double cb = channelRange.get(0); + Double ce = channelRange.get(1); + scale = (ce - cb) / Math.max(numChannels.getValue() - 1, 1); + offset = cb; + } catch (Exception ex) { + } + return new ArrayCalibration(scale, offset); + } + } + + @Override + protected void doSetSimulated() { + super.doSetSimulated(); + setCache(channelBegin, 50.0); + setCache(channelEnd, 150.0); + setCache(sliceBegin, -20.0); + setCache(sliceEnd, 20.0); + setCache(numChannels, 100); + setCache(numSlices, 10); + setCache(spectrum, new double[100]); + setCache(currentChannel, 0.0); + setCache(totalPoints, 100.0); + setCache(passEnergy, String.valueOf(PASS_ENERGY_VALUES[0])); + setCache(lensMode, LensMode.Angular45.toString()); + setCache(acquisitionMode, AcquisitionMode.Fixed.toString()); + setCache(acquisitionTime, 10.0); + + setSimulatedValue("ENERGY_MODE", EnergyMode.Binding.toString()); + setSimulatedValue("DETECTOR_MODE", DetectorMode.Pulse_Counting.toString()); + setSimulatedValue("ELEMENT_SET", ElementSet.High_Pass.toString()); + //setSimulatedValue("ACQ_MODE", AcquisitionMode.Fixed.toString()); + //setSimulatedValue("LENS_MODE", LensMode.Angular45.toString()); + //setSimulatedValue("PASS_ENERGY", String.valueOf(PASS_ENERGY_VALUES[0])); + + } + + @Override + protected void doUpdate() throws IOException, InterruptedException { + super.doUpdate(); + numChannels.update(); + currentChannel.update(); + slices.update(); + lowEnergy.update(); + centerEnergy.update(); + highEnergy.update(); + energyWidth.update(); + stepSize.update(); + stepTime.update(); + totalPoints.update(); + passEnergy.update(); + lensMode.update(); + acquisitionMode.update(); + acquisitionTime.update(); + //channelBegin.update(); + //channelEnd.update(); + ///sliceBegin.update(); + //sliceEnd.update(); + } + + @Override + protected void doSetMonitored(boolean value) { + super.doSetMonitored(value); + numChannels.setMonitored(value); + currentChannel.setMonitored(value); + slices.setMonitored(value); + lowEnergy.setMonitored(value); + centerEnergy.setMonitored(value); + highEnergy.setMonitored(value); + energyWidth.setMonitored(value); + stepSize.setMonitored(value); + stepTime.setMonitored(value); + totalPoints.setMonitored(value); + passEnergy.setMonitored(value); + lensMode.setMonitored(value); + acquisitionMode.setMonitored(value); + acquisitionTime.setMonitored(value); + //channelBegin.setMonitored(value); + //channelEnd.setMonitored(value); + //sliceBegin.setMonitored(value); + //sliceEnd.setMonitored(value); + } + + @Override + protected CameraImageDescriptor doReadImageDescriptor() throws IOException, InterruptedException { + CameraImageDescriptor ret = super.doReadImageDescriptor(); + List channelRange = getChannelRange(); + List sliceRange = getSliceRange(); + + Double cb = channelRange.get(0); + Double ce = channelRange.get(1); + Double sb = sliceRange.get(0); + Double se = sliceRange.get(1); + + if ((cb == null) || (ce == null) || (sb == null) || (se == null) || (ret.width == 0) || (ret.height == 0)) { + ret.calibration = null; + } else { + double scaleX = (ce - cb) / Math.max(ret.width - 1, 1); + double offsetX = cb; + double scaleY = (se - sb) / Math.max(ret.height - 1, 1); + double offsetY = sb; + + ret.calibration = new MatrixCalibration(scaleX, scaleY, offsetX, offsetY); + } + return ret; + } + + //Modes + public enum AcquisitionMode { + + Swept, + Fixed + } + + public void setAcquisitionMode(AcquisitionMode mode) throws IOException, InterruptedException { + //writeCtrlEnum("ACQ_MODE", String.valueOf(mode)); + acquisitionMode.write(String.valueOf(mode)); + } + + public AcquisitionMode getAcquisitionMode() throws IOException, InterruptedException { + //return (AcquisitionMode) readCtrlEnum("ACQ_MODE", AcquisitionMode.class); + String val = acquisitionMode.getValue(); + return (AcquisitionMode) convertCtrlEnum(val, AcquisitionMode.class); + } + + public enum EnergyMode { + + Binding, + Kinetic + } + + public void setEnergyMode(EnergyMode mode) throws IOException, InterruptedException { + writeCtrlEnum("ENERGY_MODE", String.valueOf(mode)); + } + + public EnergyMode getEnergyMode() throws IOException, InterruptedException { + return (EnergyMode) readCtrlEnum("ENERGY_MODE", EnergyMode.class); + } + + public enum LensMode { + + Transmission, + Angular45, + Angular60 + } + + public void setLensMode(LensMode mode) throws IOException, InterruptedException { + //writeCtrlEnum("LENS_MODE", String.valueOf(mode)); + lensMode.write(String.valueOf(mode)); + } + + public LensMode getLensMode() throws IOException, InterruptedException { + //return (LensMode) readCtrlEnum("LENS_MODE", LensMode.class); + String val = lensMode.getValue(); + return (LensMode) convertCtrlEnum(val, LensMode.class); + } + + public enum DetectorMode { + + Pulse_Counting, + ADC + } + + public void setDetectorMode(DetectorMode mode) throws IOException, InterruptedException { + writeCtrlEnum("DETECTOR_MODE", String.valueOf(mode)); + } + + public DetectorMode getDetectorMode() throws IOException, InterruptedException { + return (DetectorMode) readCtrlEnum("DETECTOR_MODE", DetectorMode.class); + } + + public enum ElementSet { + + Low_Pass, + High_Pass + } + + public void setElementSet(ElementSet mode) throws IOException, InterruptedException { + writeCtrlEnum("ELEMENT_SET", String.valueOf(mode)); + } + + public ElementSet getElementSet() throws IOException, InterruptedException { + return (ElementSet) readCtrlEnum("ELEMENT_SET", ElementSet.class); + } + + public static final int[] PASS_ENERGY_VALUES = new int[]{2, 5, 10, 20, 50, 100, 200}; + + public void setPassEnergy(int energy) throws IOException, InterruptedException { + //writeCtrl("PASS_ENERGY", String.valueOf(energy)); + passEnergy.write(String.valueOf(energy)); + + } + + public int getPassEnergy() throws IOException, InterruptedException { + //String ret = (String) readCtrl("PASS_ENERGY", String.class); + String ret = passEnergy.getValue(); + try { + return Integer.valueOf(ret); + } catch (Exception ex) { + throw new DeviceInvalidParameterException("Pass Energy", ret); + } + } + + public void zeroSupplies() throws IOException, InterruptedException { + writeCtrl("ZERO_SUPPLIES", 1); + } + + //Progress + //Disconnected operations + public double getProgress() { + Double cur = currentChannel.take(); + Double total = totalPoints.take(); + if ((cur == null) || (total == null) || (total == 0)) { + return 0.0; + } + return cur / total; + } + + public double[] getSpectrumX() throws IOException, InterruptedException { + List range = getChannelRange(); + Double begin = range.get(0); + Double end = range.get(1); + double[] spectrum = getSpectrum().take(); + if ((begin == null) || (end == null) || (spectrum == null)) { + return null; + } + int spectrumSize = spectrum.length; + double step = (end - begin) / (spectrumSize - 1); + double[] x = new double[spectrumSize]; + for (int i = 0; i < spectrumSize; i++) { + x[i] = begin + step * i; + } + return x; + } + + //Direct register access + public ChannelInteger getSlices() { + return slices; + } + + public ChannelInteger getFrames() { + return frames; + } + + public ChannelDouble getLowEnergy() { + return lowEnergy; + } + + public ChannelDouble getCenterEnergy() { + return centerEnergy; + } + + public ChannelDouble getHighEnergy() { + return highEnergy; + } + + public ChannelDouble getStepSize() { + return stepSize; + } + + public ChannelDouble getStepTime() { + return stepTime; + } + + public ChannelDouble getEnergyWidth() { + return energyWidth; + } + + public ChannelDoubleArray getSpectrum() { + return spectrum; + } + + public ChannelDoubleArray getImage() { + return image; + } + + public ChannelDoubleArray getExtio() { + return extio; + } + + public ChannelDouble getChannelBegin() { + return channelBegin; + } + + public ChannelDouble getChannelEnd() { + return channelEnd; + } + + public ChannelDouble getSliceBegin() { + return sliceBegin; + } + + public ChannelDouble getSliceEnd() { + return sliceEnd; + } + + public ChannelDouble getAcquisitionTime() { + return acquisitionTime; + } + + public List getChannelRange() throws IOException, InterruptedException { + ArrayList ret = new ArrayList<>(); + //ret.add(getChannelBegin().getValue()); + //ret.add(getChannelEnd().getValue()); + switch (getAcquisitionMode()) { + case Swept: + ret.add(lowEnergy.getValue()); + ret.add(highEnergy.getValue()); + break; + case Fixed: + default: + double eCenter = centerEnergy.getValue(); + int ePass = getPassEnergy(); + double xe = 0.04464; + double xn = 0.04464; + ret.add(eCenter - xe * ePass); + ret.add(eCenter + xn * ePass); + break; + + } + return ret; + } + + public List getSliceRange() throws IOException, InterruptedException { + ArrayList ret = new ArrayList<>(); + //ret.add(sliceBegin.getValue()); + //ret.add(sliceEnd.getValue()); + try{ + switch (getLensMode()) { + case Angular45: + ret.add(-28.148); + ret.add(27.649); + break; + case Angular60: + ret.add(-34.736); + ret.add(34.119); + break; + case Transmission: + default: + ret.add(-2.332); + ret.add(2.291); + break; + + } + } catch (Exception ex){ + ret.add(Double.NaN); + ret.add(Double.NaN); + } + return ret; + } + + public ChannelInteger getNumChannels() { + return numChannels; + } + + public ChannelInteger getNumSlices() { + return numSlices; + } + + public ChannelDouble getTotalChannels() { + return totalPoints; + } + + public ChannelDouble getCurrentChannel() { + return currentChannel; + } + + public Stats[] getStats() { + return stats; + } + + public class Stats extends ChannelDouble { + + final int index; + final ChannelInteger uid; + + Stats(String name, int index) { + super(name, channelCtrl.split(":")[0] + ":Stats" + index + ":Total_RBV", 3, false); + this.index = index; + uid = new ChannelInteger(name + " uid", channelCtrl.split(":")[0] + ":Stats" + index + ":UniqueId_RBV", false); + //setParent(Scienta.this); + addChild(uid); + } + + @Override + public boolean isReady() throws IOException, InterruptedException { + Integer imageCounter = getImageCounter().getValue(); + if (imageCounter == null) { + return false; + } + Integer id = uid.take(); + if ((id == null) || (!imageCounter.equals(id))) { + uid.update(); + } + return imageCounter.equals(uid.take()); + } + + @Override + public Double read() throws IOException, InterruptedException { + assertInitialized(); + waitReady(10000); + return super.read(); + } + + public int getUID() throws IOException, InterruptedException { + return uid.getValue(); + } + } +}