class RedPitayaControlService(BaseService): """Control server that runs on the RP that provides high-level methods.""" def __init__(self, **kwargs): self._cached_data = {} self.exposed_is_locked = None super().__init__() from registers import Registers self.registers = Registers(**kwargs) self.registers.connect(self, self.parameters) def run_acquiry_loop(self): """Starts a background process that keeps polling control and error signal. Every received value is pushed to `parameters.to_plot`.""" 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 self.registers.run_data_acquisition(data_received) self.pause_acquisition() self.continue_acquisition() def _generate_signal_stats(self, to_plot): stats = {} for signal_name, signal in to_plot.items(): stats["%s_mean" % signal_name] = np.mean(signal) stats["%s_std" % signal_name] = np.std(signal) stats["%s_max" % signal_name] = np.max(signal) stats["%s_min" % signal_name] = np.min(signal) self.parameters.signal_stats.value = stats def exposed_write_data(self): """Syncs the parameters with the FPGA registers.""" self.registers.write_registers() def task_running(self): return (self.parameters.autolock_running.value or self.parameters.optimization_running.value or self.parameters.psd_acquisition_running.value or self.parameters.psd_optimization_running.value) def exposed_start_autolock(self, x0, x1, spectrum, additional_spectra=None): spectrum = pickle.loads(spectrum) # start_watching = self.parameters.watch_lock.value start_watching = False auto_offset = self.parameters.autolock_determine_offset.value if not self.task_running(): autolock = Autolock(self, self.parameters) self.parameters.task.value = autolock autolock.run( x0, x1, spectrum, should_watch_lock=start_watching, auto_offset=auto_offset, additional_spectra=pickle.loads(additional_spectra) if additional_spectra is not None else None, ) def exposed_start_optimization(self, x0, x1, spectrum): if not self.task_running(): optim = OptimizeSpectroscopy(self, self.parameters) self.parameters.task.value = optim optim.run(x0, x1, spectrum) def exposed_start_psd_acquisition(self): if not self.task_running(): self.parameters.task.value = PSDAcquisition(self, self.parameters) self.parameters.task.value.run() def exposed_start_pid_optimization(self): if not self.task_running(): self.parameters.task.value = PIDOptimization(self, self.parameters) self.parameters.task.value.run() def exposed_start_ramp(self): self.pause_acquisition() self.parameters.combined_offset.value = 0 self.parameters.lock.value = False self.exposed_write_data() self.continue_acquisition() def exposed_start_lock(self): self.pause_acquisition() self.parameters.lock.value = True self.exposed_write_data() self.continue_acquisition() def exposed_shutdown(self): """Kills the server.""" self.registers.acquisition.shutdown() _thread.interrupt_main() # we use SystemExit instead of os._exit because we want to call atexit # handlers raise SystemExit def exposed_get_server_version(self): import linien return linien.__version__ def exposed_get_restorable_parameters(self): return self.parameters._restorable_parameters def exposed_pause_acquisition(self): self.pause_acquisition() def exposed_continue_acquisition(self): self.continue_acquisition() def exposed_set_csr_direct(self, k, v): """Directly sets a CSR register. This method is intended for debugging. Normally, the FPGA should be controlled via manipulation of parameters.""" self.registers.set(k, v) def pause_acquisition(self): """Pause continuous acquisition. Call this before changing a parameter that alters the error / control signal. This way, no inconsistent signals reach the application. After setting the new parameter values, call `continue_acquisition`.""" self.parameters.pause_acquisition.value = True self.data_uuid = random() self.registers.acquisition.pause_acquisition() def continue_acquisition(self): """Continue acquisition after a short delay, when we are sure that the new parameters values have been written to the FPGA and that data that is now recorded is recorded with the correct parameters.""" self.parameters.pause_acquisition.value = False self.registers.acquisition.continue_acquisition(self.data_uuid)
class RedPitayaControlService(BaseService): """Control server that runs on the RP that provides high-level methods.""" def __init__(self, **kwargs): self._cached_data = {} self.exposed_is_locked = None super().__init__(Parameters) from registers import Registers self.registers = Registers(**kwargs) self.registers.connect(self, self.parameters) def run_acquiry_loop(self): """Starts a background process that keeps polling control and error signal. Every received value is pushed to `parameters.to_plot`.""" def data_received(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 s1, s2, slow_out = pickle.loads(plot_data) is_locked = self.parameters.lock.value if is_locked: data = {'error_signal': s1, 'control_signal': s2} if self.parameters.pid_on_slow_enabled.value: data['slow'] = slow_out else: data = {'error_signal_1': s1, 'error_signal_2': s2} self.parameters.to_plot.value = pickle.dumps(data) self.parameters.control_signal_history.value = \ update_control_signal_history( self.parameters.control_signal_history.value, data, is_locked, self.parameters.control_signal_history_length.value ) self.registers.run_data_acquisition(data_received) self.pause_acquisition() self.continue_acquisition() def exposed_write_data(self): """Syncs the parameters with the FPGA registers.""" self.registers.write_registers() def task_running(self): return self.parameters.autolock_running.value or \ self.parameters.optimization_running.value def exposed_start_autolock(self, x0, x1, spectrum, auto_offset=True): spectrum = pickle.loads(spectrum) start_watching = self.parameters.watch_lock.value auto_offset = self.parameters.autolock_determine_offset.value if not self.task_running(): autolock = Autolock(self, self.parameters) self.parameters.task.value = autolock autolock.run(x0, x1, spectrum, should_watch_lock=start_watching, auto_offset=auto_offset) def exposed_start_optimization(self, x0, x1, spectrum): if not self.task_running(): optim = OptimizeSpectroscopy(self, self.parameters) self.parameters.task.value = optim optim.run(x0, x1, spectrum) def exposed_start_ramp(self): self.pause_acquisition() self.parameters.combined_offset.value = 0 self.parameters.lock.value = False self.exposed_write_data() self.continue_acquisition() def exposed_start_lock(self): self.pause_acquisition() self.parameters.lock.value = True self.exposed_write_data() self.continue_acquisition() def exposed_shutdown(self): """Kills the server.""" self.registers.acquisition.shutdown() _thread.interrupt_main() os._exit(0) def exposed_get_server_version(self): import linien return linien.__version__ def exposed_get_restorable_parameters(self): return self.parameters.restorable_parameters def exposed_pause_acquisition(self): self.pause_acquisition() def exposed_continue_acquisition(self): self.continue_acquisition() def pause_acquisition(self): """Pause continuous acquisition. Call this before changing a parameter that alters the error / control signal. This way, no inconsistent signals reach the application. After setting the new parameter values, call `continue_acquisition`.""" self.parameters.pause_acquisition.value = True self.data_uuid = random() def continue_acquisition(self): """Continue acquisition after a short delay, when we are sure that the new parameters values have been written to the FPGA and that data that is now recorded is recorded with the correct parameters.""" self.parameters.pause_acquisition.value = False self.registers.acquisition.clear_data_cache(self.data_uuid)