class CameraReadoutDock(QtWidgets.QDockWidget): def __init__(self, acxn): QtWidgets.QDockWidget.__init__(self, "Camera Readout") self.acxn = acxn self.setObjectName("CameraReadoutHistogram") self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetFloatable) self.image = None self.image_region = None self.run_time = None self.main_widget = QtWidgets.QWidget() self.setWidget(self.main_widget) self.make_GUI() self.connect_GUI() self.connect_asyncio_server() def connect_asyncio_server(self): self.loop = asyncio.get_event_loop() self.asyncio_server = Server( {"camera_reference_image": self.RemotePlotting(self)}, None, True) self.task = self.loop.create_task( self.asyncio_server.start("::1", 3288)) class RemotePlotting: def __init__(self, plt): self.plt = plt def plot(self, image, image_region, run_time=None): self.plt.image = image self.plt.image_region = image_region if run_time is None: self.run_time = dt.now().strftime("%Y%m%d_%H%M.%S") try: cxn = labrad.connect() p = cxn.parametervault except: logger.error("Couldn't connect to parametervault", exc_info=True) N = int(p.get_parameter("IonsOnCamera", "ion_number")) x_axis = np.arange(image_region[2], image_region[3] + 1, image_region[0]) y_axis = np.arange(image_region[4], image_region[5] + 1, image_region[1]) xx, yy = np.meshgrid(x_axis, y_axis) fitter = ion_state_detector(N) result, params = fitter.guess_parameters_and_fit(xx, yy, image) p.set_parameter("IonsOnCamera", "fit_background_level", params["background_level"].value) p.set_parameter("IonsOnCamera", "fit_amplitude", params["amplitude"].value) p.set_parameter("IonsOnCamera", "fit_rotation_angle", params["rotation_angle"].value) p.set_parameter("IonsOnCamera", "fit_center_horizontal", params["center_x"].value) p.set_parameter("IonsOnCamera", "fit_center_vertical", params["center_y"].value) p.set_parameter("IonsOnCamera", "fit_spacing", params["spacing"].value) p.set_parameter("IonsOnCamera", "fit_sigma", params["sigma"].value) self.plt.ax.clear() with suppress(Exception): self.plt.cb.remove() I = self.plt.ax.imshow(image, cmap="cividis", interpolation="spline16", extent=[ x_axis.min(), x_axis.max(), y_axis.max(), y_axis.min() ]) self.plt.cb = self.plt.fig.colorbar(I, fraction=0.046, pad=0.04) x_axis_fit = np.linspace(x_axis.min(), x_axis.max(), x_axis.size * 10) y_axis_fit = np.linspace(y_axis.min(), y_axis.max(), y_axis.size * 10) xx, yy = np.meshgrid(x_axis_fit, y_axis_fit) fit = fitter.ion_model(params, xx, yy) self.plt.ax.contour(x_axis_fit, y_axis_fit, fit, 3, colors=[(1., .49, 0., .75)]) if result is not None: # print(lmfit.fit_report(result, show_correl=False)) results_text = lmfit.fit_report(result, show_correl=False) param_results = results_text.split("\n")[-7:] for i, param_result in enumerate(param_results): param_result = param_result.split("(")[0] param_result = param_result.replace(" +/- ", "(")[:-1] param_result = param_result.split(".") param_result1 = param_result[1].split("(") param_result[1] = param_result1[0][:3] + "(" try: param_result[2] = param_result[2][:3] except IndexError: pass param_result = ".".join(param_result) param_result += ")" param_results[i] = param_result results_text = "\n".join(param_results) results_text += "\n chi_red = {:.2f}".format(result.redchi) results_text += "\n runtime = " + str(self.run_time) self.plt.ax.annotate(results_text, (0.5, 0.75), xycoords="axes fraction", color=(1., .49, 0., 1.)) self.plt.canvas.draw() self.plt.ax.relim() self.plt.ax.autoscale(enable=True, axis="both") cxn.disconnect() def enable_button(self): self.plt.reference_image_button.setDisabled(False) def closeEvent(self, event): self.task.cancel() self.loop.create_task(self.asyncio_server.stop()) super(CameraReadoutDock, self).closeEvent(event) def make_GUI(self): layout = QtWidgets.QGridLayout() self.fig = Figure(figsize=(10, 10), tight_layout=True) self.fig.patch.set_facecolor((.97, .96, .96)) self.canvas = FigureCanvasQTAgg(self.fig) self.canvas.setParent(self) # self.canvas.setMinimumSize(800, 800) self.ax = self.fig.add_subplot(111) self.ax.set_facecolor((.97, .96, .96)) self.ax.tick_params(top=False, bottom=False, left=False, right=False, labeltop=True, labelbottom=True, labelleft=True, labelright=False) self.mpl_toolbar = NavigationToolbar2QT(self.canvas, self) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.ax.tick_params(axis="both", direction="in") self.reference_image_button = QtWidgets.QPushButton("reference image") try: cxn = labrad.connect() p = cxn.parametervault except: pass accessed_params = set() parameters = p.get_parameter_names("IonsOnCamera") for parameter in parameters: accessed_params.update({"IonsOnCamera." + parameter}) d_accessed_parameter_editor = ParameterEditorDock( acxn=None, name="Camera Options", accessed_params=accessed_params) d_accessed_parameter_editor.setFeatures( QtGui.QDockWidget.NoDockWidgetFeatures) d_accessed_parameter_editor.setTitleBarWidget(QtGui.QWidget()) d_accessed_parameter_editor.table.setMaximumWidth(390) layout.addWidget(self.mpl_toolbar, 0, 0, 1, 1) layout.addWidget(self.reference_image_button, 0, 2, 1, 1) layout.addWidget(d_accessed_parameter_editor, 1, 0, 1, 1) layout.addWidget(self.canvas, 1, 1, 1, 2) self.main_widget.setLayout(layout) def connect_GUI(self): self.scheduler = Client("::1", 3251, "master_schedule") self.reference_image_button.clicked.connect(self.get_reference_image) def get_reference_image(self): self.reference_image_button.setDisabled(True) expid = { "arguments": {}, "class_name": "ReferenceImage", "file": "misc/reference_image.py", "log_level": 30, "repo_rev": None, "priority": 2 } self.scheduler.submit("main", expid, 2) def save_state(self): return { "image": self.image, "image_region": self.image_region, "run_time": self.run_time } def restore_state(self, state): if state["image"] is not None and state["image_region"] is not None: r = self.RemotePlotting(self) r.plot(state["image"], state["image_region"], run_time=state["run_time"])
class PMTControlDock(QtWidgets.QDockWidget): def __init__(self, acxn): QtWidgets.QDockWidget.__init__(self, "Manual Controls") self.setObjectName("pmt_control") self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetFloatable) self.pv = None self.pm = None self.bb = None self.acxn = acxn self.setup_listeners() self.dset_ctl = Client("::1", 3251, "master_dataset_db") self.scheduler = Client("::1", 3251, "master_schedule") self.dataset_db = Client("::1", 3251, "master_dataset_db") self.rid = None self.pulsed = False self.expid_continuous = { "arguments": {}, "class_name": "pmt_collect_continuously", "file": "run_continuously/run_pmt_continuously.py", "log_level": 30, "repo_rev": None, "priority": 0 } self.expid_pulsed = { "arguments": {}, "class_name": "pmt_collect_pulsed", "file": "run_continuously/run_pmt_pulsed.py", "log_level": 30, "repo_rev": None, "priority": 0 } self.expid_ttl = { "class_name": "change_ttl", "file": "misc/manual_ttl_control.py", "log_level": 30, "repo_rev": None, "priority": 1 } self.expid_dds = { "arguments": {}, "class_name": "change_cw", "file": "misc/manual_dds_control.py", "log_level": 30, "repo_rev": None, "priority": 1 } self.expid_dc = { "arguments": {}, "class_name": "set_dopplercooling_and_statereadout", "file": "misc/set_dopplercooling_and_statereadout.py", "log_level": 30, "repo_rev": None, "priority": 2 } frame = QtWidgets.QFrame() layout = QtWidgets.QVBoxLayout() pmt_frame = self.create_pmt_frame() linetrigger_frame = self.create_linetrigger_frame() dds_frame = self.create_dds_frame() picomotor_frame = self.create_picomotor_frame() layout.addWidget(pmt_frame) layout.addWidget(dds_frame) layout.addWidget(linetrigger_frame) layout.addWidget(picomotor_frame) layout.setSpacing(50) layout.setContentsMargins(0, 50, 0, 50) frame.setLayout(layout) scroll = QtWidgets.QScrollArea() scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) scroll.setWidgetResizable(False) scroll.setWidget(frame) scroll.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) scroll.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) self.setWidget(scroll) self.connect_servers() def create_pmt_frame(self): pmtLabel = boldLabel("PMT") self.onButton = QtWidgets.QPushButton("On") self.onButton.setCheckable(True) self.onButton.clicked[bool].connect(self.set_state) self.setDCButton = QtWidgets.QPushButton("set") self.setDCButton.clicked.connect(self.set_dc_and_state_readout) self.clearPMTPlotButton = QtWidgets.QPushButton("clear") self.clearPMTPlotButton.clicked.connect(self.clear_pmt_plot) self.autoLoadButton = QtWidgets.QPushButton("On") self.autoLoadButton.setCheckable(True) self.autoLoadButton.clicked[bool].connect(self.toggle_autoload) self.autoLoadSpin = customIntSpinBox(0, (0, 1000000)) self.autoLoadCurrentSpin = customSpinBox(0, (0, 10), " A") self.countDisplay = QtWidgets.QLCDNumber() self.countDisplay.setSegmentStyle(2) self.countDisplay.display(0) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.updateLCD) self.timer.start(250) self.countDisplay.setStyleSheet("background-color: lightGray;" "color: green;") self.unitsLabel = QtWidgets.QLabel("kcounts / s") self.durationLabel = QtWidgets.QLabel("Duration (ms): ") self.duration = QtWidgets.QLineEdit("100") try: with labrad.connection() as cxn: p = cxn.parametervault p.set_parameter(["PmtReadout", "duration", U(100, "ms")]) except: logger.error("Failed to initially connect to labrad.") self.duration.setDisabled(True) validator = QtGui.QDoubleValidator() self.duration.setValidator(validator) self.duration.returnPressed.connect(self.duration_changed) self.duration.setStyleSheet("QLineEdit { background-color: #c4df9b}" ) self.modeLabel = QtWidgets.QLabel("Mode: ") self.setMode = customComboBox(["continuous", "pulsed"]) self.setMode.currentIndexChanged.connect(self.set_mode) layout = QtWidgets.QGridLayout() frame = QtWidgets.QFrame() frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) frame.setLineWidth(2) frame.setMidLineWidth(3) layout.addWidget(pmtLabel, 0, 0, 1, 3) layout.addWidget(self.onButton, 1, 0) layout.addWidget(self.countDisplay, 1, 1) layout.addWidget(self.unitsLabel, 1, 2) layout.addWidget(self.durationLabel, 2, 0) layout.addWidget(self.duration, 2, 1, 1, 2) layout.addWidget(self.modeLabel, 3, 0) layout.addWidget(self.setMode, 3, 1, 1, 2) layout.addWidget(QtWidgets.QLabel("Autoload: "), 4, 0) layout.addWidget(self.autoLoadButton, 4, 1) layout.addWidget(self.autoLoadSpin, 4, 2) layout.addWidget(QtWidgets.QLabel("Current: "), 5, 0) layout.addWidget(self.autoLoadCurrentSpin, 5, 1) dcLabel = QtWidgets.QLabel("Set Doppler cooling and state readout: ") layout.addWidget(dcLabel, 6, 0, 1, 2) layout.addWidget(self.setDCButton, 6, 2) # clearLabel = QtWidgets.QLabel("Reset PMT plot: ") # layout.addWidget(clearLabel, 7, 0) # layout.addWidget(self.clearPMTPlotButton, 7, 1, 1, 2) frame.setLayout(layout) return frame def create_linetrigger_frame(self): linetriggerLabel = boldLabel("LINETRIGGER") self.linetriggerButton = QtWidgets.QPushButton("Off") self.linetriggerButton.setCheckable(True) self.linetriggerButton.setChecked(True) self.linetriggerButton.clicked[bool].connect(self.toggle_linetrigger) self.linetriggerLineEdit = QtWidgets.QLineEdit("0") self.linetriggerLineEdit.returnPressed.connect(self.linetrigger_duration_changed) layout = QtWidgets.QGridLayout() frame = QtWidgets.QFrame() frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) frame.setLineWidth(2) frame.setMidLineWidth(3) layout.addWidget(linetriggerLabel, 0, 0, 1, 3) layout.addWidget(self.linetriggerButton, 1, 0, 1, 3) layout.addWidget(QtWidgets.QLabel("Offset duration (us): "), 2, 0) layout.addWidget(self.linetriggerLineEdit, 2, 1, 1, 2) frame.setLayout(layout) return frame def create_picomotor_frame(self): layout = QtWidgets.QGridLayout() frame = QtWidgets.QFrame() frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) frame.setLineWidth(2) frame.setMidLineWidth(3) piezoLabel = boldLabel("PICOMOTOR") ctls = ["Local X", "Local Y", "Global X", "Global Y"] self.piezoStepSize = dict() self.piezoCurrentPos = dict() self.piezoLastPos = dict() layout.addWidget(piezoLabel, 0, 0, 1, 3) for i, ctl in enumerate(ctls): layout.addWidget(QtWidgets.QLabel(ctl + ": "), i + 1, 0) self.piezoStepSize[ctl] = customIntSpinBox(0, (0, 300)) self.piezoStepSize[ctl].setToolTip("Set step size.") self.piezoStepSize[ctl].setObjectName(ctl) self.piezoStepSize[ctl].setKeyboardTracking(False) self.piezoStepSize[ctl].valueChanged.connect(self.piezo_step_size_changed) self.piezoStepSize[ctl].setRange(0, 300) layout.addWidget(self.piezoStepSize[ctl], i + 1, 1) self.piezoCurrentPos[ctl] = QtWidgets.QSpinBox() self.piezoCurrentPos[ctl].setSingleStep(0) self.piezoCurrentPos[ctl].setToolTip("Current position.") self.piezoCurrentPos[ctl].setRange(-100000, 100000) self.piezoCurrentPos[ctl].setObjectName(str(i + 1)) self.piezoLastPos[i + 1] = 0 self.piezoCurrentPos[ctl].setKeyboardTracking(False) self.piezoCurrentPos[ctl].valueChanged.connect(self.piezo_changed) layout.addWidget(self.piezoCurrentPos[ctl], i + 1, 2) frame.setLayout(layout) return frame def create_dds_frame(self): layout = QtWidgets.QGridLayout() frame = QtWidgets.QFrame() frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) frame.setLineWidth(2) frame.setMidLineWidth(3) ddsLabel = boldLabel("DDS Control") layout.addWidget(ddsLabel, 0, 0, 1, 2) home_dir = os.path.expanduser("~") dir_ = os.path.join(home_dir, "artiq-master/HardwareConfiguration.py") settings = run_path(dir_) dds_dict = settings["dds_config"] self.all_dds_specs = dict() self.dds_widgets = dict() try: cxn = labrad.connect() p = cxn.parametervault for (name, specs) in dds_dict.items(): try: params = p.get_parameter(["dds_cw_parameters", name]) freq, amp, state, att = params[1] except: freq = str(specs.default_freq) att = str(specs.default_att) amp = str(1.) state = str(0) p.new_parameter("dds_cw_parameters", name, ("cw_settings", [freq, amp, state, att])) self.all_dds_specs[name] = {"cpld": int(specs.urukul), "frequency": float(freq) * 1e6, "att": float(att), "state": bool(int(state)), "amplitude": float(amp)} for i, (name, specs) in enumerate(sorted(dds_dict.items())): widget = ddsControlWidget(name, specs, self.scheduler, self) layout.addWidget(widget, i // 2 + 1 , i % 2) self.dds_widgets[name] = widget frame.setLayout(layout) cxn.disconnect() except: logger.error("Failed to initially connect to labrad.") return frame def set_state(self, override=False): if override: flag = True else: flag = self.onButton.isChecked() if flag: if self.rid is None: if self.setMode.currentText() == "continuous": self.rid = self.scheduler.submit("main", self.expid_continuous, 0) else: self.rid = self.scheduler.submit("main", self.expid_pulsed, 0) self.onButton.setText("Off") else: if self.rid is None: return # Shouldn't happen else: self.scheduler.request_termination(self.rid) self.rid = None self.onButton.setText("On") def set_dc_and_state_readout(self): self.scheduler.submit("main", self.expid_dc, 2) def clear_pmt_plot(self): self.dataset_db.set("clear_pmt_plot", True) @inlineCallbacks def duration_changed(self, *args, **kwargs): # connect to parametervault here if self.pv is None: self.pv = yield self.acxn.get_server("ParameterVault") sender = self.sender() validator = sender.validator() state = validator.validate(sender.text(), 0)[0] if state == QtGui.QValidator.Acceptable: color = "#c4df9b" # green elif state == QtGui.QValidator.Intermediate: color = "#fff79a" # yellow else: color = "#f6989d" # red sender.setStyleSheet("QLineEdit { background-color: %s }" %color) try: min = 1e-3 # 1 us raw_duration = float(sender.text()) duration = raw_duration if raw_duration >= min else min yield self.pv.set_parameter(["PmtReadout", "duration", U(duration, "ms")]) a = yield self.pv.get_parameter(["PmtReadout", "duration"]) if self.rid is None: return else: self.scheduler.request_termination(self.rid) self.rid = None self.set_state(True) except ValueError: # Shouldn't happen yield print("") logger.warning("Problem trying to update duration", exc_info=True) def set_mode(self): txt = str(self.setMode.currentText()) if self.rid is None: self.pulsed = txt == "pulsed" elif txt == "continuous": if not self.pulsed: return else: self.pulsed = False self.scheduler.request_termination(self.rid) self.rid = self.scheduler.submit("main", self.expid_continuous, 0) else: if self.pulsed: return else: self.pulsed = True self.scheduler.request_termination(self.rid) self.rid = self.scheduler.submit("main", self.expid_pulsed, 0) def updateLCD(self): if not self.onButton.isChecked(): self.countDisplay.display(0) return try: raw_val = self.dset_ctl.get("pmt_counts")[-1] try: # duration in mseconds duration = float(self.duration.text()) except ValueError: # picked up a backspace or something logger.warning("Failed to update LCD", exc_info=True) return val = raw_val / duration # kcounts / second self.countDisplay.display(val) except KeyError: # dataset doesn't exist logger.info("dataset doesn't exist yet") self.countDisplay.display(0) except IndexError: # timer too fast pass @inlineCallbacks def toggle_linetrigger(self, *args): sender = self.sender() flag = sender.isChecked() if flag: sender.setText("Off") yield self.pv.set_parameter(["line_trigger_settings", "enabled", True]) else: sender.setText("On") yield self.pv.set_parameter(["line_trigger_settings", "enabled", False]) @inlineCallbacks def linetrigger_duration_changed(self, *args): value = float(self.sender().text()) yield self.pv.set_parameter(["line_trigger_settings", "offset_duration", value]) def piezo_step_size_changed(self): sender = self.sender() step = int(sender.value()) ctl = sender.objectName() self.piezoCurrentPos[ctl].setSingleStep(step) @inlineCallbacks def piezo_changed(self, *args): if self.pm is None: yield print("not connected to picomotor") sender = self.sender() piezo = int(sender.objectName()) current_pos = int(sender.value()) last_pos = self.piezoLastPos[piezo] self.piezoLastPos[piezo] = current_pos move = current_pos - last_pos yield self.pm.relative_move(piezo, move) @inlineCallbacks def toggle_autoload(self, *args): sender = self.autoLoadButton flag = sender.isChecked() if flag: try: self.check_pmt_data_length = len(self.dataset_db.get("pmt_counts")) except KeyError: sender.setChecked(False) return sender.setText("Off") self.expid_ttl.update({"arguments": {"device": "blue_PIs", "state": True}}) if not hasattr(self, "check_pmt_timer"): self.check_pmt_timer = QtCore.QTimer() self.check_pmt_timer.timeout.connect(self.check_pmt_counts) self.check_pmt_timer.start(100) yield self.bb.connect() yield self.bb.set_current(self.autoLoadCurrentSpin.value()) yield self.bb.on() else: sender.setText("On") if not hasattr(self, "check_pmt_timer"): return self.check_pmt_timer.stop() self.expid_ttl.update({"arguments": {"device": "blue_PIs", "state": False}}) yield self.bb.off() self.scheduler.submit("main", self.expid_ttl, priority=1) def check_pmt_counts(self): try: counts = self.dataset_db.get("pmt_counts")[self.check_pmt_data_length:] except KeyError: return if len(counts) == 0: return if max(counts) > int(self.autoLoadSpin.value()): self.autoLoadButton.setChecked(False) self.autoLoadButton.clicked.emit() def save_state(self): return {"ctl": {ctl: self.piezoStepSize[ctl].value() for ctl in self.piezoStepSize.keys()}, "offset": self.linetriggerLineEdit.text(), "autoload": self.autoLoadSpin.value(), "mode": self.setMode.currentText(), "ltrigger": self.linetriggerButton.isChecked(), "current": self.autoLoadCurrentSpin.value()} def restore_state(self, state): for ctl, value in state["ctl"].items(): self.piezoStepSize[ctl].setValue(value) self.piezoCurrentPos[ctl].setSingleStep(int(value)) self.linetriggerLineEdit.setText(state["offset"]) self.autoLoadSpin.setValue(int(state["autoload"])) self.setMode.setCurrentText(state["mode"]) self.linetriggerButton.setChecked(state["ltrigger"]) d = {False: "On", True: "Off"} self.linetriggerButton.setText(d[state["ltrigger"]]) self.autoLoadCurrentSpin.setValue(state["current"]) def setup_listeners(self): self.acxn.add_on_connect("ParameterVault", self.parameter_vault_connect) self.acxn.add_on_disconnect("ParameterVault", self.parameter_vault_disconnect) self.acxn.add_on_connect("picomotorserver", self.picomotor_connect) self.acxn.add_on_disconnect("picomotorserver", self.picomotor_disconnect) self.acxn.add_on_connect("barebonese3663a", self.barebones_connect) self.acxn.add_on_disconnect("barebonese3663a", self.barebones_disconnect) def parameter_vault_connect(self): self.duration.setDisabled(False) def parameter_vault_disconnect(self): self.duration.setDisabled(True) def picomotor_connect(self): for spinbox in self.piezoStepSize.values(): spinbox.setDisabled(False) for spinbox in self.piezoCurrentPos.values(): spinbox.setDisabled(False) def picomotor_disconnect(self): for spinbox in self.piezoStepSize.values(): spinbox.setDisabled(True) for spinbox in self.piezoCurrentPos.values(): spinbox.setDisabled(True) def barebones_connect(self): self.autoLoadCurrentSpin.setDisabled(False) def barebones_disconnect(self): self.autoLoadCurrentSpin.setDisabled(True) @inlineCallbacks def connect_servers(self): if self.pv is None: try: self.pv = yield self.acxn.get_server("ParameterVault") except: self.parameter_vault_disconnect() if self.pm is None: try: self.pm = yield self.acxn.get_server("picomotorserver") except: self.picomotor_disconnect() if self.bb is None: try: self.bb = yield self.acxn.get_server("barebonese3663a") except: self.barebones_disconnect()