From 2b0e6e08a42c5653be4dc8920cf79057997e951d Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Thu, 13 Apr 2023 17:32:35 -0500 Subject: [PATCH 01/11] Slightly better debugging --- python/HSDIO.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/HSDIO.py b/python/HSDIO.py index 34eb63c1..d3782d0c 100644 --- a/python/HSDIO.py +++ b/python/HSDIO.py @@ -394,12 +394,11 @@ def add_repeat_waveform(self, transition_list, waveformsInUse): logger.info("cycles per repeated in add_repeat_waveform: {}".format( cycles_per_repeat)) - first_cycle_err = "An HSDIO state change was detected during the first" - " repeat cycle." + first_cycle_err = "An HSDIO state change was detected during the first repeat cycle." for t in transition_list: if self.repeats[t['index']] == -1: if repeat_sample_clock_cycles < cycles_per_repeat: - logger.error(first_cycle_err) + logger.error(first_cycle_err + "index == -1") raise PauseError if not repeats_done or sample_clock_cycles_to_next_ot < 0: # mark the total time to first other transition, use from before but cant overwrite From c3834b53eb0bcf9d037afaa6681d8101fc0f9e7f Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Thu, 13 Apr 2023 17:33:06 -0500 Subject: [PATCH 02/11] End infinite loop --- python/NewportStageController.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/NewportStageController.py b/python/NewportStageController.py index 63acd28e..57427b45 100644 --- a/python/NewportStageController.py +++ b/python/NewportStageController.py @@ -111,8 +111,11 @@ def status(self): return self.WriteThenStore(self.axis+'STAT')[0].rstrip()[-2:] def whereAmI(self): output = self.WriteThenStore(self.axis+'R')[1].rstrip()[2:] - while output == '': + c = 0 + while output == '' and c < 20: output = self.WriteThenStore(self.axis+'R')[1].rstrip()[2:] + c += 1 + logger.info("finding location, attempt {}/20".format(c)) return float(output) def findCenter(self,side=-1): From d8d1a8f8a22f68ade8e0e0fa6499eb55cf234a8a Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Thu, 13 Apr 2023 17:33:27 -0500 Subject: [PATCH 03/11] Add counter graph and analysis to Hybrid experiment --- python/hybrid.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/hybrid.py b/python/hybrid.py index 08f4399d..0783fcca 100644 --- a/python/hybrid.py +++ b/python/hybrid.py @@ -220,7 +220,9 @@ def __init__(self, 'Functional Waveforms Graph': 'FunctionalWaveformsGraph(graph = main.experiment.functional_waveforms_graph, creator=main, name="Functional Waveforms Graph")', 'Origin Interface': 'Origin(origin = main.experiment.origin, creator=main, name="Origin Interface")', 'HP Signal Generators': 'HPGenerators(hps = main.experiment.HPGenerators, creator=main, name="HP Signal Generators")', - 'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")' + 'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")', + 'CounterGraph': 'CounterGraph(analysis = main.experiment.counter_graph, creator=main, name="CounterGraph")', + 'CounterHistAnalysis': 'CounterHistAnalysis(analysis = main.experiment.counter_hist, creator=main, name="CounterHistAnalysis")' } try: From a5ae21a47727a792443eed4aa42cc38c9215564b Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Thu, 13 Apr 2023 17:47:13 -0500 Subject: [PATCH 04/11] Initial skelleton of class --- python/hybrid_auto_aligner.py | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 python/hybrid_auto_aligner.py diff --git a/python/hybrid_auto_aligner.py b/python/hybrid_auto_aligner.py new file mode 100644 index 00000000..a9b726dc --- /dev/null +++ b/python/hybrid_auto_aligner.py @@ -0,0 +1,73 @@ +""" +Communicates with the Hybrid-Auto-Alignment system + +hybrid_auto_aligner.py +author = Juan C. Bohorquez +created = 2023-04-13 + +This largely works to communicate with the auto-aligner. The auto-aligner will send cs.py messages when the beam is +being moved, so data is not stored during movement. This will also serve to set the auto-aligner settings. +""" + +import logging +from cs_errors import PauseError +from atom.api import Typed, Member, Int, Float, Bool, Str +from cs_instruments import Instrument +from TCP import CsClientSock + +logger = logging.getLogger(__name__) + +__author__ = "Juan C. Bohorquez" + + +class AutoAligner(Instrument): + + # Comm settings + port = Member() + IP = Str() + connected = Member() + sock = Member() + error = Bool() + + # messages + message = Str("") + received = Str("") + + def __init__(self, name, experiment, description): + """ + + """ + + + super(AutoAligner, self).__init__(name, experiment, description) + self.properties += ["message", "received"] + + def send(self): + """ + Sends self.message to the aligner + """ + + def receive(self): + """ + Receives a messag from the aligner + """ + + def update(self): + """ + Run at the start of each new iteration + """ + pass + + def start(self): + """ + Run at start of each measurement. Check for messages here + """ + pass + + def initialize(self): + """ + Initializes connection to device, sets exposed settings + """ + pass + + def \ No newline at end of file From ad8a770fcf38bb0aa377f285bbd6e475ee2d5053 Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Fri, 14 Apr 2023 19:18:25 -0500 Subject: [PATCH 05/11] Import AutoAligner Class --- python/hybrid.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/hybrid.py b/python/hybrid.py index 0783fcca..f37cede0 100644 --- a/python/hybrid.py +++ b/python/hybrid.py @@ -9,7 +9,7 @@ import DDS import andor, AnalogInput -import Counter, unlock_pause, newportstage, nidaq_ai, HPSignalGenerator, HVcontroller +import Counter, unlock_pause, newportstage, nidaq_ai, HPSignalGenerator, HVcontroller, hybrid_auto_aligner logger = logging.getLogger(__name__) import origin_interface import FakeInstrument # for testing @@ -123,11 +123,13 @@ def __init__(self, self.Embezzletron = FakeInstrument.Embezzletron('Embezzletron', self, 'Fake instrument that generates random data for testing') self.HPGenerators = HPSignalGenerator.HPGenerators('HPGenerators', self, 'controls HP8648B signal generator') self.HVcontrol = HVcontroller.HighVoltageController('HVcontrol', self, 'Controls Hybrid HV DACs') + self.AutoAlinger = hybrid_auto_aligner.AutoAligner("AutoAligner", self, "Maintains the 595nm alignment") + # do not include functional_waveforms in self.instruments because it # need not start/stop self.instruments += [ self.Andors, self.DDS, self.unlock_pause, - self.Embezzletron, self.NewportStage, self.HPGenerators, self.HVcontrol + self.Embezzletron, self.NewportStage, self.HPGenerators, self.HVcontrol, self.AutoAlinger ] # Labview must be last at least until someone fixes the start command self.instruments += [self.LabView] From 9c16e3cc353d9ddd62528eba0060662a969bbe17 Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Fri, 14 Apr 2023 19:18:41 -0500 Subject: [PATCH 06/11] Implement barebones GUI --- python/cs_GUI.enaml | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/python/cs_GUI.enaml b/python/cs_GUI.enaml index 0dd7d037..c3272669 100644 --- a/python/cs_GUI.enaml +++ b/python/cs_GUI.enaml @@ -1880,6 +1880,45 @@ enamldef HVcontrol(Window): text = 'Delete' clicked :: ctrl.delete() +enamldef AutoAligner(Window): + attr item + attr creator + + closing :: creator.open_windows.pop(name) + title = "Hybrid Auto Aligner" + + style_class << 'valid' if experiment.valid else 'invalid' + Container: + padding = 0 + style_class << 'valid' if experiment.valid else 'invalid' + hug_width = 'strong' + hug_height = 'strong' + Form: + Label: + text = 'enable' + CheckBox: + checked := item.enable + + EvalProp: + prop << item.IP + EvalProp: + prop << item.port + EvalProp: + prop << item.to_send + PushButton: + text = 'Send' + clicked :: item.test_send() + PushButton: + text = "Connect" + clicked :: item.open() + PushButton: + text = "Initialize" + clicked :: item.initialize() + PushButton: + text = "Disconnect" + clicked :: item.close() + + enamldef HPGenerators(Window): attr hps attr creator @@ -5197,7 +5236,8 @@ window_dictionary = { 'Origin Interface': 'Origin(origin = main.experiment.origin, creator=main, name="Origin Interface")', 'National Instruments Scopes': 'NIScopes(niscopes = main.experiment.NIScopes, creator=main, name="National Instruments Scopes")', 'HP Signal Generators': 'HPGenerators(hps = main.experiment.HPGenerators, creator=main, name="HP Signal Generators")', -'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")' +'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")', +'Hybrid Auto Aligner': 'AutoAligner(item= main.experiment.AutoAligner, creator=main, name="Hybrid Auto Aligner")' } window_keys = window_dictionary.keys() From b51f62414a4fe73c7f1e6df195d0961eccff85ad Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Fri, 14 Apr 2023 19:20:03 -0500 Subject: [PATCH 07/11] Add small testing functionality --- python/hybrid_auto_aligner.py | 37 +++++++++++------------------------ 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/python/hybrid_auto_aligner.py b/python/hybrid_auto_aligner.py index a9b726dc..35b869cc 100644 --- a/python/hybrid_auto_aligner.py +++ b/python/hybrid_auto_aligner.py @@ -12,7 +12,8 @@ import logging from cs_errors import PauseError from atom.api import Typed, Member, Int, Float, Bool, Str -from cs_instruments import Instrument +from cs_instruments import TCP_Instrument +from instrument_property import StrProp from TCP import CsClientSock logger = logging.getLogger(__name__) @@ -20,32 +21,13 @@ __author__ = "Juan C. Bohorquez" -class AutoAligner(Instrument): +class AutoAligner(TCP_Instrument): - # Comm settings - port = Member() - IP = Str() - connected = Member() - sock = Member() - error = Bool() - - # messages - message = Str("") - received = Str("") + to_send = Member() def __init__(self, name, experiment, description): - """ - - """ - - super(AutoAligner, self).__init__(name, experiment, description) - self.properties += ["message", "received"] - - def send(self): - """ - Sends self.message to the aligner - """ + self.to_send = StrProp("to_send", experiment, "Message to send to aligner", '') def receive(self): """ @@ -54,7 +36,7 @@ def receive(self): def update(self): """ - Run at the start of each new iteration + Run at the start of each new iteration, sets exposed settings """ pass @@ -66,8 +48,11 @@ def start(self): def initialize(self): """ - Initializes connection to device, sets exposed settings + Initializes connection to device, initializes internal device properties """ + super(AutoAligner, self).initialize() + # run code to initialize instrument properly pass - def \ No newline at end of file + def test_send(self): + self.send(self.to_send.value) \ No newline at end of file From d9a9faa2204f77f404a7c9f3ca02caabb7881f22 Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Mon, 15 May 2023 19:27:12 -0500 Subject: [PATCH 08/11] Fix Typo --- python/analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/analysis.py b/python/analysis.py index 8af8ccd4..af5b454c 100644 --- a/python/analysis.py +++ b/python/analysis.py @@ -63,7 +63,7 @@ class Analysis(Prop): postExperiment(). You can enable multi-threading of analyses using queueAfterMeasurement and queueAfterIteration, but only if those results are not needed for other things (filtering, other analyses, optimization). If multi-threading, you can also chose to dropMeasurementIfSlow or dropIterationIfSlow, which will not delete the data - but will just not process it. An analysis can return a success code after analyzeMesurement, which can be used to + but will just not process it. An analysis can return a success code after analyzeMeasurement, which can be used to filter results. The highest returned code dominates others: 0 or None: good measurement, increment measurement total 1: soft fail, continue with other analyses, but do not increment measurement total From da9d17c53f1a71d456516f6c2e643fa20ed7cc8f Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Mon, 15 May 2023 19:27:46 -0500 Subject: [PATCH 09/11] Fix "waiting for Origin" bug locally --- python/origin_interface.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/origin_interface.py b/python/origin_interface.py index de5c819c..9e50f105 100644 --- a/python/origin_interface.py +++ b/python/origin_interface.py @@ -412,6 +412,11 @@ def preExperiment(self, experimentResults): def preIteration(self, iterationResults, experimentResults): # initialize any logged parameters that are on a per iteration basis return 0 + + def postIteration(self, iterationResults, experimentResults): + if not self.enable: + return + super(Origin, self).postIteration(iterationResults, experimentResults) # ========================================================================== def analyzeMeasurement(self, measurementResults, iterationResults, experimentResults): From 1c1805b23669a11d74e33ea1b6fd0d8325992d7c Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Mon, 15 May 2023 19:28:14 -0500 Subject: [PATCH 10/11] Implement auto_aligner TCP interface, beam alignment filter --- python/hybrid_auto_aligner.py | 255 ++++++++++++++++++++++++++++++++-- 1 file changed, 245 insertions(+), 10 deletions(-) diff --git a/python/hybrid_auto_aligner.py b/python/hybrid_auto_aligner.py index 35b869cc..720e6ebb 100644 --- a/python/hybrid_auto_aligner.py +++ b/python/hybrid_auto_aligner.py @@ -10,10 +10,14 @@ """ import logging +import threading +import numpy as np + from cs_errors import PauseError from atom.api import Typed, Member, Int, Float, Bool, Str from cs_instruments import TCP_Instrument -from instrument_property import StrProp +from instrument_property import StrProp, FloatProp, IntProp, BoolProp, Prop +from analysis import Analysis from TCP import CsClientSock logger = logging.getLogger(__name__) @@ -21,38 +25,269 @@ __author__ = "Juan C. Bohorquez" +class BeamAlignmentFilter(Analysis): + """ + This analysis allows the user to drop measurements in an iteration where beam alignment is outside a desired region. + Must be used in conjunction with the hybrid_auto_aligner.AutoAligner class. + """ + version = '2023.05.09' + enable = Bool() + filter_level = Int() + max_error = Float() + beam_avg_x = Float(0) + beam_avg_y = Float(0) + avg_time = Int(10) + + def __init__(self, name, experiment, description=''): + super(BeamAlignmentFilter, self).__init__(name, experiment, description) + self.properties += ['version', 'enable', 'filter_level', 'max_error', 'avg_time'] + + def analyzeMeasurement(self, measurementResults, iterationResults, experimentResults): + if not self.enable: + return + try: + aligner_enable = self.experiment.AutoAligner.enable + except AttributeError: + aligner_enable = False + if not aligner_enable: + logger.warning("cannot filter on beam data, AutoAligner is not enabled") + return + + beam_x = float(measurementResults["data/auto_aligner/last_image/x"][()]) + beam_y = float(measurementResults["data/auto_aligner/last_image/y"][()]) + + if self.avg_time > 0: + r = np.exp(-1/self.avg_time) # implement a running average as an exponential decay of running average + else: + r = 0 + + measurement = int(measurementResults.attrs['measurement']) + self.beam_avg_x = (beam_x+r*self.beam_avg_x*(measurement > 0))/(1+r) + # zero-out running average at start of iterations + self.beam_avg_y = (beam_y+r*self.beam_avg_y*(measurement > 0))/(1+r) + + error_x = self.experiment.AutoAligner.control.set_point_x.value-self.beam_avg_x + error_y = self.experiment.AutoAligner.control.set_point_y.value-self.beam_avg_y + + logger.debug("beam position measured = {}, {}".format(self.beam_avg_x, self.beam_avg_y)) + logger.debug("error signal = {},{}, |e| = {}".format(error_x,error_y,np.sqrt(error_x**2+error_y**2))) + + # compute pythagorean mean beam displacement + if np.sqrt(error_x**2+error_y**2) > self.max_error: + logger.warning("beam out of positon, dropping this measurement.\n beam position = {}, {}".format(beam_x, beam_y)) + return max(0, self.filter_level) + + class AutoAligner(TCP_Instrument): - to_send = Member() + enable_aligner = Member() + timeout = Member() + + images = Member() + control = Member() + motors = Member() + camera = Member() def __init__(self, name, experiment, description): super(AutoAligner, self).__init__(name, experiment, description) - self.to_send = StrProp("to_send", experiment, "Message to send to aligner", '') + + self.enable_aligner = BoolProp("enable_aligner", experiment, "Enable Auto Aligner servo?") + self.timeout = IntProp("timeout", experiment, "") + + self.images = Images(experiment) + self.control = Control(experiment) + self.motors = Motors(experiment) + self.camera = Camera(experiment) + + self.properties += ["enable_aligner", "timeout", "images", "control", "motors", "camera"] + + self.doNotSendToHardware += ["timeout"] def receive(self): """ - Receives a messag from the aligner + Receives a message from the aligner + """ + + def openThread(self): + """ + Opens the connection thread """ + thread = threading.Thread(target=self.initialize) + thread.daemon = True + thread.start() def update(self): """ Run at the start of each new iteration, sets exposed settings """ - pass + self.send(self.toHardware()) def start(self): """ - Run at start of each measurement. Check for messages here + Run at start of each measurement """ - pass + self.send('<{}><{}/>'.format(self.name, self.name)) + self.parse_results() def initialize(self): """ Initializes connection to device, initializes internal device properties """ + logger.info("Initializing Device (not a Labview instrument)") super(AutoAligner, self).initialize() - # run code to initialize instrument properly + + def parse_results(self): + logger.debug("Query Results = {}".format(self.results)) pass - def test_send(self): - self.send(self.to_send.value) \ No newline at end of file + +# wrapper classes to hold nested data, for nested XML dicts +class Images(Prop): + """ + Settings related to image saving, analysis + """ + cutoff = Member() + path = Member() + + def __init__(self, experiment): + super(Images, self).__init__("images", experiment, "holds image processing parameters") + + self.cutoff = FloatProp( + 'cutoff', + experiment, + 'Minimum value not set to zero before computing centroid (camera counts)', + "0" + ) + self.path = StrProp('path', experiment, 'Filepath to which beam images will be saved', "") + self.properties += ["cutoff", "path"] + + +class Control(Prop): + """ + Settings related to control loop + """ + set_point_x = Member() + set_point_y = Member() + stability = Member() + transform = Member() + + def __init__(self, experiment): + super(Control, self).__init__("control", experiment, "holds control loop parameters") + self.set_point_x = FloatProp("set_point_x", experiment, "spot set point x-coordinate. In pixels.", "0") + self.set_point_y = FloatProp("set_point_y", experiment, "spot set point y-coordinate. In pixels.", "0") + self.stability = FloatProp("stability", experiment, "size of stability region. In pixels.", "1.0") + self.transform = Transform("transform", experiment) + + self.properties += ["set_point_x", "set_point_y", "stability", "transform"] + + +class Transform(Prop): + """ + Transformation Matrix elements for one axis, between motor moves and beam displacement. That is: + + d = M.s + + where s is a vector of motor moves, and d is a vector of the beam displacements those motor moves will cause. M is + the transfer matrix + + Both horizontal and vertical axes have significant hysteresis. They also show a small coupling to the transverse + direction (the off-diagonal matrix elements). To solve the hysteresis issue the aligner will split the camera into + quadrants around the set-point, these quadrants will determine if the beam needs to be moved positively/negatively + by the motors. The correct matrix elements from the motor-beam transfer matrix will be chosen, that matrix is + then inverted to compute the motor moves. + """ + mxx_p = Member() + mxy_p = Member() + myx_p = Member() + myy_p = Member() + + mxx_n = Member() + mxy_n = Member() + myx_n = Member() + myy_n = Member() + + def __init__(self, name, experiment): + super(Transform, self).__init__(name, experiment, "Matrix mapping beam displacement to motor moves") + self.mxx_p = FloatProp("mxx_p", experiment, "xx element for positive x motion (pixels/step)", "0") + self.myx_p = FloatProp("myx_p", experiment, "yx element for positive x motion (pixels/step)", "0") + self.mxy_p = FloatProp("mxy_p", experiment, "xy element for positive y motion (pixels/step)", "0") + self.myy_p = FloatProp("myy_p", experiment, "yy element for positive y motion (pixels/step)", "0") + + self.mxx_n = FloatProp("mxx_n", experiment, "xx element for negative x motion (pixels/step)", "0") + self.myx_n = FloatProp("myx_n", experiment, "yx element for negative x motion (pixels/step)", "0") + self.mxy_n = FloatProp("mxy_n", experiment, "xy element for negative y motion (pixels/step)", "0") + self.myy_n = FloatProp("myy_n", experiment, "yy element for negative y motion (pixels/step)", "0") + + self.properties += [ + "mxx_p", + "mxy_p", + "myx_p", + "myy_p", + "mxx_n", + "mxy_n", + "myx_n", + "myy_n" + ] + + +class Motors(Prop): + """ + Settings related to the PicoMotors + """ + serial_number = Member() + x_axis = Member() + y_axis = Member() + + def __init__(self, experiment): + super(Motors, self).__init__("motors", experiment, "holds motor parameters") + self.serial_number = StrProp("serial_number", experiment, "Serial Number of the PicoMotor driver", "") + self.x_axis = IntProp("x_axis", experiment, "motion axis of the horizontal mirror motor (1-4)", "0") + self.y_axis = IntProp("y_axis", experiment, "motion axis of the vertical mirror motor (1-4)", "0") + + self.properties += ["serial_number", "x_axis", "y_axis"] + + +class Camera(Prop): + """ + Settings related to the Blackfly Camera + """ + serial_number = Member() + trigger_delay = Member() + roi = Member() + exposure_time = Member() + gain = Member() + + def __init__(self, experiment): + super(Camera, self).__init__("camera", experiment, "holds camera parameters") + self.serial_number = StrProp("serial_number", experiment, "Serial Number of the BlackFly camera", "") + self.trigger_delay = IntProp( + "trigger_delay", + experiment, + "amount of time between trigger signal and start of exposure (us)", + "0.0" + ) + self.roi = ROI(experiment) + self.exposure_time = IntProp("exposure_time", experiment, "Length of image exposure (us)", "0") + self.gain = IntProp("gain", experiment, "Camera gain (dB)", "0") + + self.properties += ["serial_number", "trigger_delay", "roi", "exposure_time", "gain"] + + +class ROI(Prop): + """ + Holds info on the camera ROI + """ + width = Member() + height = Member() + offset_x = Member() + offset_y = Member() + + def __init__(self, experiment): + + super(ROI, self).__init__("roi", experiment, "holds ROI info") + self.width = IntProp("width", experiment, "width of camera ROI", "50") + self.height = IntProp("height", experiment, "height of camera ROI", "50") + self.offset_x = IntProp("offset_x", experiment, "x-offset of the camera ROI", "0") + self.offset_y = IntProp("offset_y", experiment, "y-offset of the camera ROI", "0") + + self.properties += ["width", "height", "offset_x", "offset_y"] From 2adb1b5cf232ce881298e163f236a0f082db656a Mon Sep 17 00:00:00 2001 From: JuanBohorquez3 Date: Mon, 15 May 2023 19:28:35 -0500 Subject: [PATCH 11/11] Add auto-aligner window, import auto-aligner to experiment file --- python/cs_GUI.enaml | 204 ++++++++++++++++++++++++++++++++++++++------ python/hybrid.py | 23 +++-- 2 files changed, 192 insertions(+), 35 deletions(-) diff --git a/python/cs_GUI.enaml b/python/cs_GUI.enaml index c3272669..1a2615b8 100644 --- a/python/cs_GUI.enaml +++ b/python/cs_GUI.enaml @@ -1881,43 +1881,159 @@ enamldef HVcontrol(Window): clicked :: ctrl.delete() enamldef AutoAligner(Window): - attr item + attr ctrl attr creator closing :: creator.open_windows.pop(name) - title = "Hybrid Auto Aligner" + title = 'Hybrid Auto Aligner' style_class << 'valid' if experiment.valid else 'invalid' Container: padding = 0 style_class << 'valid' if experiment.valid else 'invalid' - hug_width = 'strong' - hug_height = 'strong' - Form: - Label: - text = 'enable' - CheckBox: - checked := item.enable + ScrollArea: + style_class << 'valid' if experiment.valid else 'invalid' + Container: + style_class << 'valid' if experiment.valid else 'invalid' + VGroup: + HGroup: + align_widths = False + hug_width = 'strong' - EvalProp: - prop << item.IP - EvalProp: - prop << item.port - EvalProp: - prop << item.to_send - PushButton: - text = 'Send' - clicked :: item.test_send() - PushButton: - text = "Connect" - clicked :: item.open() - PushButton: - text = "Initialize" - clicked :: item.initialize() - PushButton: - text = "Disconnect" - clicked :: item.close() + PushButton: b1: + text = 'open connection' + clicked:: + ctrl.openThread() + PushButton: b2: + text='update settings' + clicked:: + ctrl.update() + PushButton: b3: + text='close connection' + clicked:: + ctrl.close() + Label: + text = 'enable communication with Auto Alignment Server?' + CheckBox: + checked := ctrl.enable + Label: + text = 'Auto Aligner IP address' + Field: + text:= ctrl.IP + Label: + text = 'Auto Aligner port' + IntField: + value := ctrl.port + EvalProp: + prop << ctrl.timeout + HGroup: + align_widths = False + hug_width = 'strong' + EvalProp: + prop << ctrl.enable_aligner + PushButton: + text = 'Query Aligner Status' + clicked:: + ctrl.start() + ScrollArea: + style_class << 'valid' if experiment.valid else 'invalid' + Container: + style_class << 'valid' if experiment.valid else 'invalid' + + padding = 0 + hug_height='strong' + hug_width='strong' + GroupBox: + title = 'Image Settings' + EvalProp: + prop << ctrl.images.cutoff + EvalProp: + prop << ctrl.images.path + GroupBox: + title = 'Control Loop Settings' + GroupBox: + title = 'set point' + HGroup: + align_widths = True + EvalProp: + prop << ctrl.control.set_point_x + EvalProp: + prop << ctrl.control.set_point_y + EvalProp: + prop << ctrl.control.stability + GroupBox: + title = 'transformation matrix elements' + VGroup: + HGroup: + align_widths = False + GroupBox: + title = 'positive x motion' + VGroup: + EvalProp: + prop << ctrl.control.transform.mxx_p + EvalProp: + prop << ctrl.control.transform.myx_p + GroupBox: + title = 'positive y motion' + VGroup: + EvalProp: + prop << ctrl.control.transform.mxy_p + EvalProp: + prop << ctrl.control.transform.myy_p + HGroup: + align_widths = False + GroupBox: + title = 'negative x motion' + VGroup: + EvalProp: + prop << ctrl.control.transform.mxx_n + EvalProp: + prop << ctrl.control.transform.myx_n + + GroupBox: + title = 'negative y motion' + VGroup: + EvalProp: + prop << ctrl.control.transform.mxy_n + EvalProp: + prop << ctrl.control.transform.myy_n + + GroupBox: + title = 'Motor Settings' + EvalProp: + prop << ctrl.motors.serial_number + EvalProp: + prop << ctrl.motors.x_axis + EvalProp: + prop << ctrl.motors.y_axis + GroupBox: + title= 'Camera Settings' + EvalProp: + prop << ctrl.camera.serial_number + EvalProp: + prop << ctrl.camera.trigger_delay + EvalProp: + prop << ctrl.camera.exposure_time + EvalProp: + prop << ctrl.camera.gain + GroupBox: + title = 'ROI' + VGroup: + HGroup: + align_widths = False + hug_width = 'strong' + EvalProp: + prop << ctrl.camera.roi.offset_x + EvalProp: + prop << ctrl.camera.roi.offset_y + HGroup: + align_widths = False + hug_width = 'strong' + EvalProp: + prop << ctrl.camera.roi.width + EvalProp: + prop << ctrl.camera.roi.height enamldef HPGenerators(Window): attr hps @@ -3581,6 +3697,36 @@ enamldef FirstMeasurementsFilter(GroupBox): 'hard: do not continue with other analyses, do not increment measurement total, delete measurement data'] index := filters.filter_level +enamldef BeamAlignmentFilter(GroupBox): + attr filters + title = 'Drop Measurements with a misaligned beam' + + Form: + Label: + text='enable' + CheckBox: + checked := filters.enable + + Label: + text='Averaging Time' + SpinBox: + minimum=0 + value := filters.avg_time + + Label: + text='Max beam positioning error before data is dropped (pixels)' + FloatField: + value := filters.max_error + + Label: + text='filter level' + ComboBox: + items = ['None: increment measurement total', + 'soft: continue with other analyses, but do not increment measurement total', + 'med: continue with other analyses, do not increment measurement total, and delete measurement data after all analyses', + 'hard: do not continue with other analyses, do not increment measurement total, delete measurement data'] + index := filters.filter_level + enamldef Histogram(Window): attr experiment attr analysis @@ -4998,6 +5144,8 @@ enamldef Filters(Window): filters = experiment.loading_filters FirstMeasurementsFilter: filters = experiment.first_measurements_filter + BeamAlignmentFilter: + filters = experiment.beam_alignment_filter enamldef BoxTemperature(Window): attr box_temperature @@ -5237,7 +5385,7 @@ window_dictionary = { 'National Instruments Scopes': 'NIScopes(niscopes = main.experiment.NIScopes, creator=main, name="National Instruments Scopes")', 'HP Signal Generators': 'HPGenerators(hps = main.experiment.HPGenerators, creator=main, name="HP Signal Generators")', 'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")', -'Hybrid Auto Aligner': 'AutoAligner(item= main.experiment.AutoAligner, creator=main, name="Hybrid Auto Aligner")' +'Hybrid Auto Aligner': 'AutoAligner(ctrl = main.experiment.AutoAligner, creator=main, name="Hybrid Auto Aligner")' } window_keys = window_dictionary.keys() diff --git a/python/hybrid.py b/python/hybrid.py index f37cede0..e8d63116 100644 --- a/python/hybrid.py +++ b/python/hybrid.py @@ -9,7 +9,7 @@ import DDS import andor, AnalogInput -import Counter, unlock_pause, newportstage, nidaq_ai, HPSignalGenerator, HVcontroller, hybrid_auto_aligner +import Counter, unlock_pause, newportstage, nidaq_ai, HPSignalGenerator, HVcontroller, hybrid_auto_aligner, AWG logger = logging.getLogger(__name__) import origin_interface import FakeInstrument # for testing @@ -45,6 +45,8 @@ class Hybrid(Experiment): Embezzletron = Member() HPGenerators = Member() HVcontrol = Member() + AutoAligner = Member() + AWG = Member() thresholdROIAnalysis = Member() @@ -75,6 +77,7 @@ class Hybrid(Experiment): Ramsey = Member() squareROIAnalysis = Member() window_dict = Member() + beam_alignment_filter = Member() def __init__(self, config_instrument=None, @@ -123,13 +126,13 @@ def __init__(self, self.Embezzletron = FakeInstrument.Embezzletron('Embezzletron', self, 'Fake instrument that generates random data for testing') self.HPGenerators = HPSignalGenerator.HPGenerators('HPGenerators', self, 'controls HP8648B signal generator') self.HVcontrol = HVcontroller.HighVoltageController('HVcontrol', self, 'Controls Hybrid HV DACs') - self.AutoAlinger = hybrid_auto_aligner.AutoAligner("AutoAligner", self, "Maintains the 595nm alignment") - + self.AutoAligner = hybrid_auto_aligner.AutoAligner('AutoAligner', self, 'Maintains the 595nm alignment') + self.AWG = AWG.AWG(name='AWG', experiment=self, description="testing 1 2") # do not include functional_waveforms in self.instruments because it # need not start/stop self.instruments += [ self.Andors, self.DDS, self.unlock_pause, - self.Embezzletron, self.NewportStage, self.HPGenerators, self.HVcontrol, self.AutoAlinger + self.Embezzletron, self.NewportStage, self.HPGenerators, self.HVcontrol, self.AutoAligner, self.AWG ] # Labview must be last at least until someone fixes the start command self.instruments += [self.LabView] @@ -159,6 +162,8 @@ def __init__(self, self.counter_hist = Counter.CounterHistogramAnalysis('counter_hist', self, 'Fits histograms of counter data and plots hist and fits.') self.save_notes = save2013style.SaveNotes('save_notes', self, 'save a separate notes.txt') self.origin = origin_interface.Origin('origin', self, 'saves selected data to the origin data server') + self.beam_alignment_filter = hybrid_auto_aligner.BeamAlignmentFilter('beam_alignment_filter', self, 'drop measurements where beam isn\'t aligned') + # do not include functional_waveforms_graph in self.analyses because it # need not update on iterations, etc. @@ -176,7 +181,8 @@ def __init__(self, self.iterations_graph, self.Andors, self.Ramsey, self.DAQmxAI, self.unlock_pause, self.retention_analysis, self.retention_graph, - self.save_notes, self.counter_hist, self.origin + self.save_notes, self.counter_hist, self.origin, + self.beam_alignment_filter ] self.properties += [ @@ -190,7 +196,8 @@ def __init__(self, 'retention_graph', 'Ramsey', 'counter_graph', 'counter_hist', 'unlock_pause', 'ROI_rows', 'ROI_columns', 'ROI_bg_rows', 'ROI_bg_columns', - 'origin', 'HPGenerators', 'thresholdROIAnalysis', 'squareROIAnalysis', 'HVcontrol' + 'origin', 'HPGenerators', 'thresholdROIAnalysis', 'squareROIAnalysis', 'HVcontrol', 'AutoAligner', + 'AWG', 'beam_alignment_filter' ] self.window_dict = { @@ -224,7 +231,9 @@ def __init__(self, 'HP Signal Generators': 'HPGenerators(hps = main.experiment.HPGenerators, creator=main, name="HP Signal Generators")', 'High Voltage Controller': 'HVcontrol(ctrl = main.experiment.HVcontrol, creator=main, name="High Voltage Controller")', 'CounterGraph': 'CounterGraph(analysis = main.experiment.counter_graph, creator=main, name="CounterGraph")', - 'CounterHistAnalysis': 'CounterHistAnalysis(analysis = main.experiment.counter_hist, creator=main, name="CounterHistAnalysis")' + 'CounterHistAnalysis': 'CounterHistAnalysis(analysis = main.experiment.counter_hist, creator=main, name="CounterHistAnalysis")', + 'Hybrid Auto Aligner': 'AutoAligner(ctrl = main.experiment.AutoAligner, creator=main, name="Hybrid Auto Aligner")', + 'AWG': 'AWG_Page(AWG = main.experiment.AWG, creator=main, name="AWG")' } try: