def data_received(is_raw, plot_data, data_uuid): # When a parameter is changed, `pause_acquisition` is set. # This means that the we should skip new data until we are sure that # it was recorded with the new settings. if not self.parameters.pause_acquisition.value: if data_uuid != self.data_uuid: return data_loaded = pickle.loads(plot_data) if not is_raw: is_locked = self.parameters.lock.value if not check_plot_data(is_locked, data_loaded): print( "warning: incorrect data received for lock state, ignoring!" ) return self.parameters.to_plot.value = plot_data self._generate_signal_stats(data_loaded) # update signal history (if in locked state) ( self.parameters.control_signal_history.value, self.parameters.monitor_signal_history.value, ) = update_signal_history( self.parameters.control_signal_history.value, self.parameters.monitor_signal_history.value, data_loaded, is_locked, self.parameters.control_signal_history_length.value, ) else: self.parameters.acquisition_raw_data.value = plot_data
def update_std(self, to_plot, max_std_history_length=10): if self.parameters.lock.value and to_plot: to_plot = pickle.loads(to_plot) if to_plot and check_plot_data(True, to_plot): error_signal = to_plot.get('error_signal') control_signal = to_plot.get('control_signal') self.error_std_history.append(np.std(error_signal)) self.control_std_history.append(np.std(control_signal)) self.error_std_history = self.error_std_history[ -max_std_history_length:] self.control_std_history = self.control_std_history[ -max_std_history_length:] if error_signal and control_signal: self.ids.error_std.setText('%.2f' % np.mean(self.error_std_history)) self.ids.control_std.setText( '%.2f' % np.mean(self.control_std_history))
def replot(self, to_plot): time_beginning = time() if self._should_reposition_reset_view_button: self._should_reposition_reset_view_button = False self.position_reset_view_button() if ( time_beginning - self.last_plot_time <= self.plot_rate_limit and not self._plot_paused ): # don't plot too often at it only causes unnecessary load # this does not apply if plot is paused, because in this case we want # to collect all the data that we can get in order to pass it to the # autolock return self.last_plot_time = time_beginning # NOTE: this is necessary if OpenGL is activated. Otherwise, the # plot is way too small. This command apparently causes a repaint # and works fine even though the values are nonsense. if not self._fixed_opengl_bug: self._fixed_opengl_bug = True self.resize( self.parent().frameGeometry().width(), self.parent().frameGeometry().height(), ) if self.parameters.pause_acquisition.value: return if to_plot is not None: to_plot = pickle.loads(to_plot) if to_plot is None: return if not check_plot_data(self.parameters.lock.value, to_plot): return # we also call this if the laser is not locked because it resets # the history in this case history, slow_history = self.update_signal_history(to_plot) if self.parameters.lock.value: self.signal1.setVisible(False) self.signal2.setVisible(False) self.control_signal.setVisible(True) self.control_signal_history.setVisible(True) self.slow_history.setVisible(self.parameters.pid_on_slow_enabled.value) self.monitor_signal_history.setVisible( not self.parameters.dual_channel.value ) self.combined_signal.setVisible(True) self.signal_strength_a.setVisible(False) self.signal_strength_b.setVisible(False) self.signal_strength_a2.setVisible(False) self.signal_strength_b2.setVisible(False) self.signal_strength_a_fill.setVisible(False) self.signal_strength_b_fill.setVisible(False) error_signal, control_signal = ( to_plot["error_signal"], to_plot["control_signal"], ) all_signals = (error_signal, control_signal, history, slow_history) self.plot_data_locked(to_plot) self.plot_autolock_target_line(None) else: dual_channel = self.parameters.dual_channel.value self.signal1.setVisible(True) monitor_signal = to_plot.get("monitor_signal") error_signal_2 = to_plot.get("error_signal_2") self.signal2.setVisible( error_signal_2 is not None or monitor_signal is not None ) self.combined_signal.setVisible(dual_channel) self.control_signal.setVisible(False) self.control_signal_history.setVisible(False) self.slow_history.setVisible(False) self.monitor_signal_history.setVisible(False) s1 = to_plot["error_signal_1"] s2 = error_signal_2 if error_signal_2 is not None else monitor_signal combined_error_signal = combine_error_signal( (s1, s2), dual_channel, self.parameters.channel_mixing.value, self.parameters.combined_offset.value, ) if self._plot_paused: self._cached_plot_data.append(combined_error_signal) # don't save too much self._cached_plot_data = self._cached_plot_data[-20:] return all_signals = [s1, s2] + [combined_error_signal] self.last_plot_data = all_signals self.plot_data_unlocked((s1, s2), combined_error_signal) self.plot_autolock_target_line(combined_error_signal) if self.parameters.modulation_frequency.value != 0: # check whether to plot signal strengths using quadratures s1q = to_plot.get("error_signal_1_quadrature") s2q = to_plot.get("error_signal_2_quadrature") self.signal_strength_a.setVisible(s1q is not None) self.signal_strength_a2.setVisible(s1q is not None) self.signal_strength_a_fill.setVisible(s1q is not None) self.signal_strength_b.setVisible(s2q is not None) self.signal_strength_b2.setVisible(s2q is not None) self.signal_strength_b_fill.setVisible(s2q is not None) if s1q is not None: max_signal_strength_V = ( self.plot_signal_strength( s1, s1q, self.signal_strength_a, self.signal_strength_a2, self.signal_strength_a_fill, self.parameters.offset_a.value, self.parameters.plot_color_0.value, ) / V ) all_signals.append( [ max_signal_strength_V * V, -1 * max_signal_strength_V * V, ] ) self.signal_power1.emit( peak_voltage_to_dBm(max_signal_strength_V) ) else: self.signal_power1.emit(INVALID_POWER) if s2q is not None: max_signal_strength2_V = ( self.plot_signal_strength( s2, s2q, self.signal_strength_b, self.signal_strength_b2, self.signal_strength_b_fill, self.parameters.offset_b.value, self.parameters.plot_color_1.value, ) / V ) all_signals.append( [ max_signal_strength2_V * V, -1 * max_signal_strength2_V * V, ] ) self.signal_power2.emit( peak_voltage_to_dBm(max_signal_strength2_V) ) else: self.signal_power2.emit(INVALID_POWER) else: self.signal_strength_a.setVisible(False) self.signal_strength_b.setVisible(False) self.signal_strength_a2.setVisible(False) self.signal_strength_b2.setVisible(False) self.signal_strength_a_fill.setVisible(False) self.signal_strength_b_fill.setVisible(False) self.signal_power1.emit(INVALID_POWER) self.signal_power2.emit(INVALID_POWER) time_end = time() time_diff = time_end - time_beginning new_rate_limit = 2 * time_diff if new_rate_limit < DEFAULT_PLOT_RATE_LIMIT: new_rate_limit = DEFAULT_PLOT_RATE_LIMIT self.plot_rate_limit = new_rate_limit
def replot(self, to_plot): # NOTE: this is necessary if OpenGL is activated. Otherwise, the # plot is way too small. This command apparently causes a repaint # and works fine even though the values are nonsense. if not self._fixed_opengl_bug: self._fixed_opengl_bug = True self.resize(self.parent().frameGeometry().width(), self.parent().frameGeometry().height()) if self.parameters.pause_acquisition.value: return if to_plot is not None and not self.touch_start: to_plot = pickle.loads(to_plot) if to_plot is None: return if not check_plot_data(self.parameters.lock.value, to_plot): return # we also call this if the laser is not locked because it resets # the history in this case history, slow_history = self.update_control_signal_history(to_plot) if self.parameters.lock.value: self.last_plot_data = to_plot self.signal1.setVisible(False) self.signal2.setVisible(False) self.control_signal.setVisible(True) self.control_signal_history.setVisible(True) self.slow_history.setVisible( self.parameters.pid_on_slow_enabled.value) self.combined_signal.setVisible(True) error_signal, control_signal = to_plot[ 'error_signal'], to_plot['control_signal'] all_signals = (error_signal, control_signal, history, slow_history) self.plot_data_locked(to_plot) self.update_plot_scaling(all_signals) self.plot_autolock_target_line(None) else: dual_channel = self.parameters.dual_channel.value self.signal1.setVisible(True) self.signal2.setVisible(dual_channel) self.combined_signal.setVisible(dual_channel) self.control_signal.setVisible(False) self.control_signal_history.setVisible(False) self.slow_history.setVisible(False) s1, s2 = to_plot['error_signal_1'], to_plot['error_signal_2'] combined_error_signal = combine_error_signal( (s1, s2), dual_channel, self.parameters.channel_mixing.value) all_signals = [s1, s2] + [combined_error_signal] self.last_plot_data = all_signals self.plot_data_unlocked((s1, s2), combined_error_signal) self.plot_autolock_target_line(combined_error_signal) self.update_plot_scaling(all_signals if dual_channel else [s1])
def react_to_new_spectrum(self, plot_data): """React to new spectrum data. If this is executed for the first time, a reference spectrum is recorded. If the autolock is approaching the desired line, a correlation function of the spectrum with the reference spectrum is calculated and the laser current is adapted such that the targeted line is centered. After this procedure is done, the real lock is turned on and after some time the lock is verified. If automatic relocking is desired, the control and error signals are continuously monitored after locking. """ if self.parameters.pause_acquisition.value: return if plot_data is None or not self.parameters.autolock_running.value: return plot_data = pickle.loads(plot_data) if plot_data is None: return is_locked = self.parameters.lock.value # check that `plot_data` contains the information we need # otherwise skip this round if not check_plot_data(is_locked, plot_data): return if is_locked: error_signal = plot_data['error_signal'] control_signal = plot_data['control_signal'] else: combined_error_signal = combine_error_signal( (plot_data['error_signal_1'], plot_data['error_signal_2']), self.parameters.dual_channel.value, self.parameters.channel_mixing.value) try: if self.parameters.autolock_approaching.value: # we have already recorded a spectrum and are now approaching # the line by decreasing the scan range and adapting the # center current multiple times. if self.approacher is None: self.approacher = Approacher(self.control, self.parameters, self.first_error_signal, self.target_zoom, allow_ramp_speed_change=True) approaching_finished = self.approacher.approach_line( combined_error_signal) if approaching_finished: self._lock() elif self.parameters.autolock_watching.value: # the laser was locked successfully before. Now we check # periodically whether the laser is still in lock return self.watch_lock(error_signal, control_signal) else: # we are done with approaching and have started the lock. # skip some data and check whether we really are in lock # afterwards. return self.after_lock(error_signal, control_signal, plot_data.get('slow')) except SpectrumUncorrelatedException: print('spectrum uncorrelated') if self.parameters.watch_lock.value: print('retry') self.relock() else: self.exposed_stop() self.parameters.autolock_failed.value = True except Exception: traceback.print_exc() self.exposed_stop()
def react_to_new_spectrum(self, plot_data): """React to new spectrum data. If this is executed for the first time, a reference spectrum is recorded. If the autolock is approaching the desired line, a correlation function of the spectrum with the reference spectrum is calculated and the laser current is adapted such that the targeted line is centered. After this procedure is done, the real lock is turned on and after some time the lock is verified. If automatic relocking is desired, the control and error signals are continuously monitored after locking. """ if self.parameters.pause_acquisition.value: return if plot_data is None or not self.parameters.autolock_running.value: return plot_data = pickle.loads(plot_data) if plot_data is None: return is_locked = self.parameters.lock.value # check that `plot_data` contains the information we need # otherwise skip this round if not check_plot_data(is_locked, plot_data): return try: if not is_locked: combined_error_signal = combine_error_signal( (plot_data["error_signal_1"], plot_data.get("error_signal_2")), self.parameters.dual_channel.value, self.parameters.channel_mixing.value, self.parameters.combined_offset.value, ) if (self.autolock_mode_detector is not None and not self.autolock_mode_detector.done): self.autolock_mode_detector.handle_new_spectrum( combined_error_signal) self.additional_spectra.append(combined_error_signal) if self.autolock_mode_detector.done: self.start_autolock(self.autolock_mode_detector.mode) else: return return self.algorithm.handle_new_spectrum( combined_error_signal) else: error_signal = plot_data["error_signal"] control_signal = plot_data["control_signal"] return self.after_lock(error_signal, control_signal, plot_data.get("slow")) except Exception: traceback.print_exc() self.parameters.autolock_failed.value = True self.exposed_stop()