diff --git a/src/analysis.py b/src/analysis.py index 3a70722..1952e9e 100644 --- a/src/analysis.py +++ b/src/analysis.py @@ -25,12 +25,12 @@ matplotlib.use('Agg') _pymodule = os.path.basename(__file__) -PROGRESS_BAR_THREAD_INIT = 0 -PROGRESS_BAR_THREAD_START = 1 -PROGRESS_BAR_THREAD_ABORTING = 2 -PROGRESS_BAR_THREAD_ABORTED = 3 -PROGRESS_BAR_THREAD_ERROR = 4 -PROGRESS_BAR_THREAD_END = 100 +PROGRESS_THREAD_INIT = 0 +PROGRESS_THREAD_START = 1 +PROGRESS_THREAD_ABORTING = 2 +PROGRESS_THREAD_ABORTED = 3 +PROGRESS_THREAD_ERROR = 4 +PROGRESS_THREAD_END = 100 class AnalysisProcedure(QObject): @@ -56,15 +56,15 @@ class AnalysisProcedure(QObject): self.all_data = {} self.all_data['Input data'] = {} self.all_data['Ambient data'] = {} - self.all_data['Application Raw data'] = {} + #self.all_data['Application Raw data'] = {} self.all_data['Processed data'] = {} self.all_data['Figure data'] = {} self.all_data['Raw data'] = {} self.raw_data = {} - - self.ring_cyclotron = 'Cyclotron' - self.injector_2 = 'Injector' - + + self.injector_2 = self.parent.injector_2 + self.ring_cyclotron = self.parent.ring_cyclotron + #Declare input parameters self.input_data = None @@ -97,12 +97,12 @@ class AnalysisProcedure(QObject): Set abort variable to interrupt measurement """ self.abort = True - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_ABORTING) + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_ABORTING) def aborting(self, line_no): self.abort = False #mess = "Measurement aborted" - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_ABORTED) + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_ABORTED) #########INITIALIZE THE INPUTS FOM THE GUI####################### @@ -110,7 +110,8 @@ class AnalysisProcedure(QObject): def initialize_input_parameters(self, input_data: dict): self.input_data = input_data - + self.all_data['Input data'] = self.input_data + if 'debug' in self.input_data.keys(): self.debug = self.input_data['debug'] @@ -142,7 +143,7 @@ class AnalysisProcedure(QObject): print("self.accelerator-F", self.accelerator, flush=True) self.harmonic_no = float( self.input_data[self.accelerator]['harmonic']) - self.dTcable = int( + self.dTcable = float( self.input_data[self.accelerator]['deltaTcable']) self.dNpickup = int( self.input_data[self.accelerator]['deltaNpickup']) @@ -169,19 +170,17 @@ class AnalysisProcedure(QObject): self.logger.info("Harmonic No. {0}".format(self.harmonic_no)) self.logger.info("dT Cable {0}".format(self.dTcable)) self.logger.info("dN Pickup {0}".format(self.dNpickup)) - - + except KeyError as ex: - self.logger.debug("KeyError {0}".format(ex)) + self.logger.error("KeyError {0}".format(ex)) except ValueError as ex: - self.logger.debug("ValueError {0}".format(ex)) + self.logger.error("ValueError {0}".format(ex)) except Exception as ex: - self.logger.debug("Exception {0}".format(ex)) + self.logger.error("Exception {0}".format(ex)) def measure_and_analyze(self, input_data=None): - ''' - This method is initiated by the START button in Procedure panel + '''This method is initiated by the START button in Procedure panel ''' if input_data is None: mess = "No input parameters given; no measurement performed" @@ -191,50 +190,38 @@ class AnalysisProcedure(QObject): #Read the input parameters from the GUI self.initialize_input_parameters(input_data) - - - #If Injector, add Not Implement QMessagge Box - if self.injector_2 in self.accelerator: - raise NotImplementedError - + #Step 1 - Collect ambient data relate to the machine self.all_data['Ambient data'] = self.collect_ambient_data() - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_START) + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_START) #Step 2 - Perform measurement and return data for processing - self.raw_data = self.measure() - #print("raw data", self.raw_data) - - if self.raw_data is None: - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_ERROR) + self.all_data['Raw data'] = self.measure() + + if self.all_data['Raw data'] is None: + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_ERROR) return None - - self.all_data['Application Raw data'] = self.raw_data - + #Step 3 - Process the raw data self.all_data['Processed data'] = self.process() #Step 4 - Provide plots self.all_data['Figure data'] = self.make_figs() - - #Step 5 - Package to all_data dictionary - #all_data = self.combine_data(self.all_data['Ambient data'], proc_data, fig_data) - #print("all data", all_data) - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_END) + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_END) return self.all_data def load_hdf_file(self, hdf_filename_loaded): - print("raw_data==>", hdf_filename_loaded, flush=True) + print("load_hdf_file==>", hdf_filename_loaded, flush=True) raw_data = h5_storage.loadH5Recursive(hdf_filename_loaded) self.raw_data = raw_data - print("loadH5Recursive", self.raw_data, flush=True) + print("loadH5Recursive", raw_data, flush=True) - return self.raw_data + return raw_data def reanalyze(self, all_data): @@ -248,16 +235,20 @@ class AnalysisProcedure(QObject): ambient_data = all_data['Ambient_data'] self.raw_data = all_data['Raw_data'] + self.all_data['Raw data'] = self.raw_data ambient_data['Time in seconds'] = int(ambient_data['Time in seconds']) - - self.parent.from_hdf = True + self.time_stamp = ambient_data['Time stamp'] - proc_data = self.process(from_hdf5=True) - fig_data = self.make_figs() - all_data_new = self.combine_data(ambient_data, proc_data, - fig_data) - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_END) - return(all_data_new) + self.all_data['Ambient data'] = ambient_data + + self.parent.from_hdf = True + + + self.all_data['Processed data'] = self.process(from_hdf5=True) + self.all_data['Figure data'] = self.make_figs() + + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_END) + return(self.all_data) def collect_ambient_data(self): @@ -265,7 +256,7 @@ class AnalysisProcedure(QObject): """ # Time in seconds in an integer and can be stored in hdf5 time_in_seconds = time.time() - time_stamp = datetime.fromtimestamp( + self.time_stamp = datetime.fromtimestamp( time_in_seconds).strftime('%a %d-%m-%Y %H:%M:%S') #EPICS... @@ -286,13 +277,13 @@ class AnalysisProcedure(QObject): self.aborting(_line()) return {} - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_ERROR) + self.parent.trigger_progressbar.emit(PROGRESS_THREAD_ERROR) return {} ambient_data = { 'Time in seconds': int(time_in_seconds), - 'Time stamp': time_stamp, + 'Time stamp': self.time_stamp, } @@ -300,8 +291,35 @@ class AnalysisProcedure(QObject): return ambient_data - def measure(self): + + def extract_peak_data(self): + y1_peaks_pre = signal.find_peaks(self.y1_sample, height=0.005) + y1_peaks_avg = np.average(y1_peaks_pre[1]['peak_heights']) + y1_height = y1_peaks_avg * 0.666667 + y2_peaks_pre = signal.find_peaks(self.y2_sample, height=0.005) + y2_peaks_avg = np.average(y2_peaks_pre[1]['peak_heights']) + y2_height = y2_peaks_avg * 0.666667 + print("AVG = ", y1_height, y2_height, flush=True) + + y1_peaks = signal.find_peaks(self.y1_sample, height=y1_height) + y2_peaks = signal.find_peaks(self.y2_sample, height=y2_height) + print("PEAKS==>", y1_peaks, y2_peaks, len(y1_peaks[0]), len(y2_peaks[0]), flush=True) + print(y1_peaks[1]['peak_heights'] ,flush=True) + #import sys + #sys.exit() + self.y1_pulse = (y1_peaks[1]['peak_heights']) + self.y2_pulse = (y2_peaks[1]['peak_heights']) + print(type(self.y1_pulse), flush=True) + + + def measure(self): + ''' Enable DAQ and read in the collected data from EPICS + ''' + if self.abort: + self.aborting(utils.line_no()) + return None + self.parent.from_hdf = False #Start and Stop Run #Collect Data and out into numpy array @@ -321,78 +339,63 @@ class AnalysisProcedure(QObject): for count, entry in enumerate(self.content[5:]): entry=entry.replace('\n','') val=entry.split('\t') - self.t_sample.append(t_inc) + self.t_sample.append(float(t_inc)) self.y1_sample.append(float(val[1])*(-1)) self.y2_sample.append(float(val[2])) t_inc += self.t_stepsize - - y1_peaks = signal.find_peaks(self.y1_sample, height=0.03) - y2_peaks = signal.find_peaks(self.y2_sample, height=0.02) - print("PEAKS", y1_peaks, y2_peaks, len(y1_peaks[0]), len(y2_peaks[0]), flush=True) - print(y1_peaks[1]['peak_heights'] ,flush=True) - #import sys - #sys.exit() - self.y1_pulse = (y1_peaks[1]['peak_heights']) - self.y2_pulse = (y2_peaks[1]['peak_heights']) - - def extract_peak_data(): - - index_max_y1 = np.argmax(self.y1_sample[0:int(self.t_interval*1.5)]) - index_max_y2 = np.argmax(self.y2_sample[0:int(self.t_interval*1.5)]) - index_max = min(index_max_y1, index_max_y2) - print(self.t_interval, index_max_y1, index_max_y2, flush=True) - start = self.t_interval - index_max - stop = min(len(self.y1_sample), len(self.y2_sample)) - t_inc = 0 - icount = 0 - for val in range(start, stop, self.t_interval): - search_array1 = self.y1_sample[val:val+self.t_interval] - search_array2 = self.y2_sample[val:val+self.t_interval] - self.y1_pulse.append(np.array(search_array1).max()) - self.y2_pulse.append(np.array(search_array2).max()) - - self.t_pulse.append(t_inc) - #t_inc += self.t_stepsize * self.t_interval - t_inc += self.pulse_stepsize - icount +=1 - t_offset = icount/2 - self.t_pulse = np.array(self.t_pulse) - t_offset*self.t_stepsize*self.t_interval - - print("length====>", len(self.y1_pulse), len(self.y2_pulse) ) - if self.simulation: self.parent.trigger_progressbar.emit(20) print("open File", flush=True) file = open('/hipa/bd/data/measurements/tina/20240710-223007_2000.txt','r') self.parent.trigger_progressbar.emit(40) + if self.abort: + file.close() + self.aborting(utils.line_no()) + return None self.content = file.readlines() file.close() + if self.abort: + self.aborting(utils.line_no()) + return None print("close File", flush=True) self.parent.trigger_progressbar.emit(60) extract_raw_data() - #extract_peak_data() - - + + + self.extract_peak_data() + if self.abort: + self.aborting(utils.line_no()) + return None + #extract_peak_data() + self.parent.trigger_progressbar.emit(70) #Fill Raw data here - raw_data = { - 'y1': self.y1_sample, - 'y2': self.y2_sample, - 't': self.t_sample + + rawdata = { + 'y1': list(self.y1_sample), + 'y2': list(self.y2_sample), + 't': list(self.t_sample), } - - - return raw_data + + return rawdata def unpack_hdf_data(self): - self.y1_pulse = self.raw_data['y1'] - self.y2_pulse = self.raw_data['y2'] + self.y1_sample = self.raw_data['y1'] + self.y2_sample = self.raw_data['y2'] + self.t_sample = self.raw_data['t'] + self.extract_peak_data() def process(self, from_hdf5=False): + ''' Process the collected data + ''' - self.parent.trigger_progressbar.emit(PROGRESS_BAR_THREAD_START) + if self.abort: + self.aborting(utils.line_no()) + return None + + self.parent.trigger_progressbar.emit(80) if from_hdf5: self.unpack_hdf_data() @@ -410,9 +413,9 @@ class AnalysisProcedure(QObject): len(self.normalized_amplitude_envelope_2), len(self.normalized_amplitude_envelope_1), mode='full') - self.lag_full = self.lags_full_array[np.argmax(self.corr_full)] + self.lag_full = int( self.lags_full_array[np.argmax(self.corr_full)]) #self.delay = self.lag_full * self.t_stepsize*self.t_interval - self.delay = self.lag_full * self.pulse_stepsize + self.delay = float (self.lag_full * self.pulse_stepsize) print("lag", self.lag_full) print("delay", self.delay, flush=True) print("dTcable", self.dTcable, flush=True) @@ -426,12 +429,17 @@ class AnalysisProcedure(QObject): print("lag = {0}, delay = {1}, nturns={2}".format( self.lag_full, self.delay, self.N_turns)) + - #Fill Raw data here + if self.abort: + self.aborting(utils.line_no()) + return None + + #Fill Processed data here proc_data = { - 'y1': self.y1_pulse, - 'y2': self.y2_pulse, - 't': self.t_pulse, + 'y1': self.y1_pulse.tolist(), + 'y2': self.y2_pulse.tolist(), + 't_stepsize': self.pulse_stepsize, 'lag': self.lag_full, 'delay': self.delay, 'nturns': self.N_turns @@ -440,6 +448,9 @@ class AnalysisProcedure(QObject): return proc_data def make_figs(self): + ''' Figure construction with matplotlib + ''' + fig, (ax) = plt.subplots(nrows=2, ncols=1, figsize=(18,9), layout='tight') fig2, (ax2) = plt.subplots(nrows=1, ncols=1, figsize=(18,9)) fig.patch.set_facecolor('#FAF9F6') @@ -451,44 +462,55 @@ class AnalysisProcedure(QObject): #ax[0].ticklabel_format(useOffset=False, style='plain') - ax[0].plot(self.t_sample[s:e], self.y1_sample[s:e], 'r-', label='') + ax[0].plot(self.t_sample[s:e], self.y1_sample[s:e], '.r-', label='') ax[1].plot(self.t_sample[s:e], self.y2_sample[s:e], '.r-', label='' ) - ax[0].xaxis.set_major_locator(ticker.MultipleLocator(self.t_stepsize*self.t_interval)) + ax[0].xaxis.set_major_locator( + ticker.MultipleLocator(self.t_stepsize*self.t_interval)) ax[0].set_xlabel('Time [s]') ax[0].set_ylabel('Amplitude') ax[0].set_title('Pulse at Entry') ax[0].set_facecolor("lightgrey") #ax[0].legend() - ax[0].grid(visible=True, which='major', axis='both', linestyle='--', linewidth=0.8) - ax[1].xaxis.set_major_locator(ticker.MultipleLocator(self.t_stepsize*self.t_interval)) + ax[0].grid(visible=True, which='major', axis='both', + linestyle='--', linewidth=0.8) + ax[1].xaxis.set_major_locator( + ticker.MultipleLocator(self.t_stepsize*self.t_interval)) ax[1].set_xlabel('Time [s]') ax[1].set_ylabel('Amplitude') ax[1].set_title('Pulse at Exit') ax[1].set_facecolor("lightgray") - ax[1].grid(visible=True, which='major', axis='both', linestyle='--', linewidth=0.8) + ax[1].grid(visible=True, which='major', axis='both', + linestyle='--', linewidth=0.8) #ax[1].legend() - + ax2.set_title('Cross-correlation between {0} Entrance and Exit'.format( + self.accelerator), fontsize=16) + ax2.set_ylabel('x-corr', fontsize=14) + ax2.set_xlabel('t (units of {0:.2f} ns)'.format( + self.pulse_stepsize*10**9), fontsize=14) + ax2.grid(visible=True, which='major', axis='both', linestyle='--', + linewidth=0.8) ax2.set_facecolor("#F5F5F5") ax2.plot(self.lags_full_array[:], self.corr_full[:]) xmin, xmax, ymin, ymax = ax2.axis() lineStart = ymin #self.corr_full.min() lineEnd = ymax #self.corr_full.max() print("start end ", lineStart, lineEnd) - ax2.plot([1107, 1107], [lineStart, lineEnd], '-', color = 'r') + ax2.plot([self.lag_full, self.lag_full], [lineStart, lineEnd], + ':', color = 'r') ax2.set_ylim(lineStart, lineEnd) #ax2[1].plot(self.lags_full_array[:], self.corr_full[:], 'yo') + text = "No of Turns = {0}".format(int(self.N_turns)) + plt.figtext(0.65, 0.82, self.accelerator, weight='bold', fontsize=16) + plt.figtext(0.65, 0.77, text, weight='bold', fontsize=16) + plt.figtext(0.7, 0.72, "lag = {0}".format(self.lag_full), weight='normal', + fontsize=14) + text = 'delay = {0:.3f} (\u00B5s)'.format(self.delay*10**6) + plt.figtext(0.7, 0.67, text, weight='normal', fontsize=14) + if self.settings.data["GUI"]["showDate"]: + plt.figtext(0.75, 0.12, self.time_stamp, size='small') + fig_data = {'Canvas 1': [fig2], 'Canvas 2': [fig]} - + return fig_data - - def combine_data(self, ambient_data, proc_data, fig_data): - - all_data = {'Input data': self.input_data} - all_data['Ambient data'] = ambient_data - all_data['Application Raw data'] = self.raw_data - all_data['Processed data'] = proc_data - all_data['Figure data'] = fig_data - all_data['Raw data'] = {} - - return(all_data) + diff --git a/tina.json b/tina.json index 57b3103..0ec32d4 100755 --- a/tina.json +++ b/tina.json @@ -51,6 +51,7 @@ "GUI": { "resultsTabTitle" : "Plots", "subResultsTabTitle" : ["Correlations", "Raw Data"], - "resultsSeq" : [1, 1] + "resultsSeq" : [1, 1], + "showDate" : 1 } } diff --git a/tina.py b/tina.py index 3137d45..5ac9cba 100644 --- a/tina.py +++ b/tina.py @@ -34,7 +34,9 @@ _appname = "Tina" class StartMain(BaseWindow): trigger_log_message = Signal(str, str, int, str, dict) - + ring_cyclotron = 'Cyclotron' + injector_2 = 'Injector' + def __init__(self, parent=None): super().__init__( parent=parent, pymodule=_pymodule, appversion=_appversion, @@ -44,7 +46,8 @@ class StartMain(BaseWindow): self.appname = _appname self.source_file = _abspath #required for HDF self.elog_enum = ElogHIPA() - self.accelerator = 'Cyclotron' + + self.accelerator = self.ring_cyclotron #default #self.from_hdf = False in base class self.message = "" self.gui = AppGui(self) @@ -53,10 +56,13 @@ class StartMain(BaseWindow): """Prepare results message """ try: - self.no_turns = self.all_data["Processed data"]["nturns"] - except KeyError as ex: - self.no_turns = 0 - + self.no_turns = self.all_data["Processed data"]["nturns"] + lag_full = self.all_data["Processed data"]["lag"] + delay = self.all_data["Processed data"]["delay"] + except KeyError: + self.message = "" + return + try: self.accelerator = self.all_data["Input data"]["accelerator"] except KeyError as ex: @@ -70,8 +76,11 @@ class StartMain(BaseWindow): _mess = "Reanalysis from HDF5. " if self.from_hdf else "" self.message = (_mess + - "The number of turns measured in the {0} = {1} ({2:.2f})".format( - self.accelerator, int(self.no_turns), self.no_turns)) + ''' + The number of turns measured in the {0} = {1} ({2:.2f}) + lag = {3}, delay = {4:.3f} (\u00B5s) + '''.format(self.accelerator, int(self.no_turns), self.no_turns, + lag_full, delay*10**6)) @@ -103,14 +112,39 @@ class StartMain(BaseWindow): self.logbook = "Sandkasten" if simulation else "HIPA" self.title = _title - + def verify_analysis_preconditions(self): + if self.injector_2 in self.input_parameters['accelerator']: + mess = ("Measurement procedure for Injector 2 \n" + + "has not yet been implementented.") + QMessageBox.information(self, "Injector 2", mess, QMessageBox.Ok) + + QApplication.processEvents() + return False + return True + @Slot() def analysis_thread_finished(self): BaseWindow.analysis_thread_finished(self) - if self.all_data is not None: - if self.all_data['Figure data'] is not None: - self.gui_frame.central_tab_widget.setCurrentIndex(1) + try: + if self.all_data['Figure data'] is not None: + self.gui_frame.central_tab_widget.setCurrentIndex(1) + except KeyError: + print("No analysis performed") + return + else: + print("thread finished with no data") + ncanvas = len(self.settings.data["GUI"]["subResultsTabTitle"]) + + dict_fig = {} + dict_fig['Figure data'] = {} + for i in range(0, ncanvas): + canvas = "Canvas {0}".format(i+1) + dict_fig['Figure data'][canvas] = None + #Delete old figures + self.gui.gui_frame.canvas_update(dict_fig['Figure data']) + return + self.prepare_results_message() self.show_log_message(MsgSeverity.INFO, _pymodule, utils.line_no(), self.message) @@ -122,7 +156,7 @@ class StartMain(BaseWindow): self.show_log_message(MsgSeverity.INFO, _pymodule, utils.line_no(), self.message) - ''' + @Slot() def save_to_hdf_dialog(self): @@ -135,7 +169,7 @@ class StartMain(BaseWindow): return False BaseWindow.save_to_hdf_dialog(self) - ''' + @Slot() @@ -168,18 +202,26 @@ class StartMain(BaseWindow): """ if self.all_data is not None: #All but 'Figure data' - self.all_data['Raw data']['Input_data'] = self.all_data[ + print("add_to_hdf") + #print(self.all_data['Rawdata'], flush=True) + #print("raw data==========>", self.all_data['Raw data']['y1'][0:5], flush=True) + #print("raw data==========>", self.all_data['Raw data']['y2'][0:5], flush=True) + #print("raw data==========>", self.all_data['Rawdata']['t'][0:5], flush=True) + _all_data = {} + _all_data ['Raw data'] = {} + print("add_to_hdf//") + _all_data['Raw data']['Input_data'] = self.all_data[ 'Input data'] - self.all_data['Raw data']['Ambient_data'] = self.all_data[ + _all_data['Raw data']['Ambient_data'] = self.all_data[ 'Ambient data'] - self.all_data['Raw data']['Processed_data'] = self.all_data[ + _all_data['Raw data']['Processed_data'] = self.all_data[ 'Processed data'] - self.all_data['Raw data']['Raw_data'] = self.all_data[ - 'Application Raw data'] - #self.all_data['Raw_data'] = self.all_data['Application Raw data'] + _all_data['Raw data']['Raw_data'] = self.all_data[ + 'Raw data'] + #_all_data['Raw_data'] = self.all_data['Rawdata'] #del self.all_data['Figure data'] h5_storage.saveH5Recursive( - self.hdf_filename, self.all_data['Raw data'], dataH5) + self.hdf_filename, _all_data['Raw data'], dataH5) @Slot() def send_to_elog(self): @@ -247,7 +289,6 @@ class StartMain(BaseWindow): def save_to_epics(self): """ Write the number of turns calculated to an EPICS PV """ - print(self.all_data) if not BaseWindow.verify_save_to_epics(self): return False if self.from_hdf: