From ce9d2d78e391cc86e26e2059339c2fec9c006cda Mon Sep 17 00:00:00 2001 From: Martin Killenberg <martin.killenberg@desy.de> Date: Fri, 11 Aug 2023 15:53:28 +0200 Subject: [PATCH] chore: code cleanup - remove commit log comments - fix code formatting --- Python_script/MeasurementPlot.py | 181 ++++++++++++------------------- Python_script/PostPlot.py | 132 +++++++--------------- Python_script/analysis.py | 13 +-- Python_script/climate-lab-gui.py | 22 +--- Python_script/prototype.py | 151 +++++++++----------------- 5 files changed, 169 insertions(+), 330 deletions(-) diff --git a/Python_script/MeasurementPlot.py b/Python_script/MeasurementPlot.py index b99762e..fd9fb2e 100644 --- a/Python_script/MeasurementPlot.py +++ b/Python_script/MeasurementPlot.py @@ -13,7 +13,7 @@ class PlottingError(Exception): class MeasurementPlot: def __init__(self, title='', trace_subplot5 ='', legend_loc = 'upper left',\ legend_bbox_to_anchor = (1.09, 1)): - # set python for opening an separate plot window when starting from anaconda, Michael + # set python for opening an separate plot window when starting from anaconda if 'ipykernel' in sys.modules: from IPython import get_ipython get_ipython().run_line_magic('matplotlib', 'qt') @@ -24,45 +24,40 @@ class MeasurementPlot: self.legend_loc = legend_loc self.legend_bbox_to_anchor = legend_bbox_to_anchor - # Third, Fourth and Fifth plot for additional external sensors, Michael + # Prepare for five subplots self.fig, self.ax1 = plt.subplots(5, figsize=(25, 20)) self.fig.subplots_adjust(bottom= 0.1, right=0.8, hspace = 0.4) self.fig.suptitle("Measurement "+title, color="red") - - # First plot: Phase and magnitude, Michael + # First plot: Phase and magnitude self.path_collection_phase = self.ax1[0].scatter([], [], c='red', marker='<', label='DUT Phase') self.path_collection_fit = self.ax1[0].scatter([], [], c='green', marker='.', label = ' ') self.magnitude_axis = self.ax1[0].twinx() - # in entry of legend for phase and magnitude the word DUT is added in front, Michael self.path_collection_mag = self.magnitude_axis.scatter([], [], c='#3120E0', marker='4', label='DUT Magnitude') self.equi_axis0 = self.ax1[0].twinx() self.equi_axis0.spines['right'].set_position(('outward', 75)) self.path_collection_equi0 = self.equi_axis0.scatter([], [], c='black', marker=".", label='Equilibrium_Indicator') - # units added to y-axes of subplot 1, Michael self.ax1[0].set_xlabel("TIMESTAMP") self.ax1[0].set_ylabel("PHASE [°]", color='red') self.magnitude_axis.set_ylabel("MAGNITUDE [dB]", color='#3120E0') - # label of y-axis for equilibrium indicator is changed to Indiccator Value because it's shorter, Michael self.equi_axis0.set_ylabel("INDICATOR VALUE", color='black') # fix range to 0..31 with some extra margin for plotting self.equi_axis0.set_ylim(-1, 32) self.ax1[0].grid(True, linestyle=":") - all_path_collections = [self.path_collection_phase, self.path_collection_mag, \ + all_path_collections = [self.path_collection_phase, self.path_collection_mag, self.path_collection_equi0, self.path_collection_fit] labels = [pc.get_label() for pc in all_path_collections] - self.phase_legend = self.ax1[0].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor = \ - self.legend_bbox_to_anchor) + self.phase_legend = self.ax1[0].legend(all_path_collections, labels, loc=self.legend_loc, + bbox_to_anchor=self.legend_bbox_to_anchor) ax = self.fig.axes - # plot delta values for phase and magnitude at left position outside the plot, Michael - self.annotation = ax[0].annotate('' ,xy=(0,1), \ - xycoords='axes fraction', xytext=(-0.16,1), \ - textcoords='axes fraction',fontsize = '16', \ - horizontalalignment='left',verticalalignment='bottom') + self.annotation = ax[0].annotate('', xy=(0, 1), + xycoords='axes fraction', xytext=(-0.16, 1), + textcoords='axes fraction', fontsize='16', + horizontalalignment='left', verticalalignment='bottom') # Second plot: Humidity and temperature of climate chamber requested from internal sensors of chamber self.path_collection_temp = self.ax1[1].scatter([], [], c='blue', marker='p', label="Chamber Temperature") @@ -70,33 +65,26 @@ class MeasurementPlot: self.path_collection_hum = self.humidity_axis.scatter([], [], c='green', marker="*", label="Chamber Humidity") self.equi_axis1 = self.ax1[1].twinx() self.equi_axis1.spines['right'].set_position(('outward', 75)) - self.path_collection_equi1 = self.equi_axis1.scatter([], [], c='black', marker=".", label="Equilibrium_Indicator") + self.path_collection_equi1 = self.equi_axis1.scatter([], [], c='black', marker=".", + label="Equilibrium Indicator") - # units added to y-axes of subplot 2, Michael self.ax1[1].set_xlabel("TIMESTAMP") self.ax1[1].set_ylabel("TEMPERATURE [°C] ", color='blue') self.humidity_axis.set_ylabel("HUMIDITY [%RH]", color='green') - # label of y-axis for equilibrium indicator is changed to Indiccator Value because it's shorter, Michael self.equi_axis1.set_ylabel("INDICATOR VALUE", color='black') self.equi_axis1.set_ylim(-1, 32) self.ax1[1].grid(True, linestyle=":") all_path_collections = [self.path_collection_temp, self.path_collection_hum, self.path_collection_equi1] labels = [pc.get_label() for pc in all_path_collections] - self.ax1[1].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor = \ - self.legend_bbox_to_anchor) - - - # Third plot: parameter of external sensors - # DUT temperature, DUT humidity, Michael - # configuration of used sensor port in ext_sens_data.json, Michael - - self.path_collection_temp_dut = self.ax1[2].scatter([],[], c='red', marker='p', \ - label='DUT temperature') + self.ax1[1].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor=self.legend_bbox_to_anchor) + + # Third plot: parameter of external sensors DUT temperature, DUT humidity + self.path_collection_temp_dut = self.ax1[2].scatter([], [], c='red', marker='p', label='DUT temperature') self.ext_sens_hum_axis = self.ax1[2].twinx() - self.path_collection_hum_dut = self.ext_sens_hum_axis.scatter([],[], c='purple', \ - marker='*', label='DUT humidity') + self.path_collection_hum_dut = self.ext_sens_hum_axis.scatter([], [], c='purple', marker='*', + label='DUT humidity') self.ax1[2].set_xlabel("TIMESTAMP") @@ -106,71 +94,58 @@ class MeasurementPlot: self.ax1[2].grid(True, linestyle=":") all_path_collections = [self.path_collection_temp_dut, self.path_collection_hum_dut] labels = [pc.get_label() for pc in all_path_collections] - self.ax1[2].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor = \ - self.legend_bbox_to_anchor) - - # Forth plot: parameter of external sensors - # room temperature, room humidity , air pressure room, Michael - # configuration of used sensor port in ext_sens_data.json, Michael + self.ax1[2].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor=self.legend_bbox_to_anchor) - self.path_collection_temp_room = self.ax1[3].scatter([],[], c='green', marker='*', label='room temperature') + # Forth plot: parameter of external sensors: room temperature, room humidity , air pressure room + self.path_collection_temp_room = self.ax1[3].scatter([], [], c='green', marker='*', label='room temperature') self.sec_ext_hum_sens_axis = self.ax1[3].twinx() - self.path_collection_hum_room = self.sec_ext_hum_sens_axis.scatter([],[], c='orange', marker='>', label='room humidity') + self.path_collection_hum_room = self.sec_ext_hum_sens_axis.scatter([], [], c='orange', marker='>', + label='room humidity') self.press_axis = self.ax1[3].twinx() self.press_axis.spines['right'].set_position(('outward', 60)) - self.path_collection_air_press_room = self.press_axis.scatter([],[], c='grey', marker='4', label='air pressure room') + self.path_collection_air_press_room = self.press_axis.scatter([], [], c='grey', marker='4', + label='air pressure room') self.ax1[3].set_xlabel("TIMESTAMP") self.ax1[3].set_ylabel("TEMPERATURE [°C]", color='green') - self.sec_ext_hum_sens_axis.set_ylabel("HUMIDITY [%RH]", color = 'orange') - self.press_axis.set_ylabel("AIR PRESSURE [mb]", color ='grey') + self.sec_ext_hum_sens_axis.set_ylabel("HUMIDITY [%RH]", color='orange') + self.press_axis.set_ylabel("AIR PRESSURE [mb]", color='grey') - self.ax1[3].grid(True, linestyle=":") - all_path_collections = [self.path_collection_temp_room, self.path_collection_hum_room, \ + self.ax1[3].grid(True, linestyle=":") + all_path_collections = [self.path_collection_temp_room, self.path_collection_hum_room, self.path_collection_air_press_room] labels = [pc.get_label() for pc in all_path_collections] - self.ax1[3].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor = \ - self.legend_bbox_to_anchor) + self.ax1[3].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor=self.legend_bbox_to_anchor) - # Fifth plot: parameter of external sensors - # meas instruments temperature, meas instruments humidity, Michael - # configuration of used sensor port in ext_sens_data.json, Michael + # Fifth plot: parameter of external sensors: meas instruments temperature, meas instruments humidity + subplot_dict = self.config_fifth_subplot() - subplot_dict = self.config_fith_subplot() - - self.path_collection_trace_1 = self.ax1[4].scatter([], [], c='black', \ - marker='p', label = subplot_dict.get('label_trace_1')) + self.path_collection_trace_1 = self.ax1[4].scatter([], [], c='black', marker='p', + label=subplot_dict.get('label_trace_1')) self.sec_plot_param_axis = self.ax1[4].twinx() - self.path_collection_trace_2 = self.sec_plot_param_axis.scatter([], [], c='brown', \ - marker="*",label = subplot_dict.get('label_trace_2')) + self.path_collection_trace_2 = self.sec_plot_param_axis.scatter([], [], c='brown', marker="*", + label=subplot_dict.get('label_trace_2')) - # # units added to y-axes of subplot 5, Michael self.ax1[4].set_xlabel("TIMESTAMP") self.ax1[4].set_ylabel(subplot_dict.get('y_axis'), color='black') self.sec_plot_param_axis.set_ylabel(subplot_dict.get('sec_y_axis'), color='brown') self.ax1[4].grid(True, linestyle=":") - all_path_collections = [self.path_collection_trace_1, \ - self.path_collection_trace_2] + all_path_collections = [self.path_collection_trace_1, self.path_collection_trace_2] labels = [pc.get_label() for pc in all_path_collections] - self.ax1[4].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor = \ - self.legend_bbox_to_anchor) - - - # sclae fontsize of all plot window elements to size 16, Michael - plt.rcParams.update({'font.size':16}) - + self.ax1[4].legend(all_path_collections, labels, loc=self.legend_loc, bbox_to_anchor=self.legend_bbox_to_anchor) + + plt.rcParams.update({'font.size': 16}) def draw(self, data_frame, pdf_name=''): timestamps = data_frame.TIMESTAMP minimum, maximum = self.get_extended_min_max(timestamps) self.ax1[0].set_xlim(minimum, maximum) self.ax1[1].set_xlim(minimum, maximum) - # because subplots where increase x-axis limits has to be set equal to first and second plot, Michael self.ax1[2].set_xlim(minimum, maximum) self.ax1[3].set_xlim(minimum, maximum) self.ax1[4].set_xlim(minimum, maximum) @@ -200,7 +175,7 @@ class MeasurementPlot: self.humidity_axis.set_ylim(minimum, maximum) self.path_collection_hum.set_offsets(np.c_[timestamps, humidities]) - # refresh temperatures for used external sensors in subplots, Michael + # refresh temperatures for used external sensors in subplots temp_dut = data_frame.TEMP_DUT temp_room = data_frame.TEMP_ROOM @@ -213,11 +188,9 @@ class MeasurementPlot: self.path_collection_temp_room.set_offsets(np.c_[timestamps, temp_room]) # refresh humidities external sensors in subplots for DUT humidity and room humidity - # humidity, Michael hum_dut = data_frame.HUM_DUT hum_room = data_frame.HUM_ROOM - - + minimum, maximum = self.get_extended_min_max(hum_dut) self.ext_sens_hum_axis.set_ylim(minimum, maximum) @@ -226,17 +199,15 @@ class MeasurementPlot: self.path_collection_hum_dut.set_offsets(np.c_[timestamps, hum_dut]) self.path_collection_hum_room.set_offsets(np.c_[timestamps, hum_room]) - - # refresh air pressure of external sensor in subplot for air pressure room, Michael + # refresh air pressure of external sensor in subplot for air pressure room air_press_room = data_frame.AIR_PRESS_ROOM minimum, maximum = self.get_extended_min_max(air_press_room) self.press_axis.set_ylim(minimum, maximum) self.path_collection_air_press_room.set_offsets(np.c_[timestamps, air_press_room]) # refresh temperature and humidity of external sensor in subplot for measurement - # for instrument temperature and measurement instrument humidity, Michael - - val_trace_1, val_trace_2 = self.refresh_param_fith_subplot(data_frame) + # for instrument temperature and measurement instrument humidity + val_trace_1, val_trace_2 = self.refresh_param_fifth_subplot(data_frame) minimum, maximum = self.get_extended_min_max(val_trace_1) self.ax1[4].set_ylim(minimum, maximum) @@ -245,8 +216,7 @@ class MeasurementPlot: self.sec_plot_param_axis.set_ylim(minimum, maximum) self.path_collection_trace_1.set_offsets(np.c_[timestamps, val_trace_1]) self.path_collection_trace_2.set_offsets(np.c_[timestamps, val_trace_2]) - - + self.path_collection_equi0.set_offsets(np.c_[timestamps, data_frame.EQUILIBRIUM_INDICATOR]) self.path_collection_equi1.set_offsets(np.c_[timestamps, data_frame.EQUILIBRIUM_INDICATOR]) @@ -261,42 +231,39 @@ class MeasurementPlot: except Exception as e: raise PlottingError from e - def config_fith_subplot(self): + def config_fifth_subplot(self): - # key names for config parameter fith subplot + # key names for config parameter fifth subplot keys = ["label_trace_1", "label_trace_2", "y_axis", "sec_y_axis"] if self.trace_subplot5 == 'logger_sens': - # values for plotting evironmental conditions in measurement instrument chamber - values = ["temperature\nalmemo sensors\nmeas instr chamber",\ - "humidity\nalmemo sensors\nmeas instr chamber", \ - "TEMPERATURE [°C]", "HUMIDITY [%RH]"] + # values for plotting evnironmental conditions in measurement instrument chamber + values = ["temperature\nalmemo sensors\nmeas instr chamber", + "humidity\nalmemo sensors\nmeas instr chamber", + "TEMPERATURE [°C]", "HUMIDITY [%RH]"] elif self.trace_subplot5 == 'chamber_sens': - # values for plotting evironmental conditions in measurement instrument chamber - values = ["temperature\nchamber sensors\nmeas instr chamber", \ - "humidity\nchamber sensors\nmeas instr chamber", \ - "TEMPERATURE [°C]", "HUMIDITY [%RH]"] + # values for plotting environmental conditions in measurement instrument chamber + values = ["temperature\nchamber sensors\nmeas instr chamber", + "humidity\nchamber sensors\nmeas instr chamber", + "TEMPERATURE [°C]", "HUMIDITY [%RH]"] else: - # values for plotting heater activity in fith subplot - values = ["Temp Heater\nDUT chamber", "Humidity Heater\n DUT chamber", \ - "ACTIVITY [%]","ACTIVITY [%]"] + # values for plotting heater activity in fifth subplot + values = ["Temp Heater\nDUT chamber", "Humidity Heater\n DUT chamber", + "ACTIVITY [%]","ACTIVITY [%]"] - - # generate dictionary from selection + # generate dictionary from selection config_subplot_dict = dict(zip(keys, values)) return config_subplot_dict - - - def refresh_param_fith_subplot(self, data_frame): + + def refresh_param_fifth_subplot(self, data_frame): if self.trace_subplot5 == 'logger_sens': - - # chose sensor values for refreshing fith subplot + # chose sensor values for refreshing fifth subplot val_trace_1 = data_frame.TEMP_MEAS_INSTR val_trace_2 = data_frame.HUM_MEAS_INSTR @@ -307,14 +274,12 @@ class MeasurementPlot: else: - # chose heater values for refreshing fift plot + # chose heater values for refreshing fifth plot val_trace_1 = data_frame.TEMP_HEATER val_trace_2 = data_frame.HUM_HEATER - - + return val_trace_1, val_trace_2 - - + # add 5 % of the distance between min and max to the range @staticmethod def get_extended_min_max(array): @@ -323,6 +288,7 @@ class MeasurementPlot: distance = 1 return array.min()-0.05*distance, array.max()+0.05*distance + # test procedure for measurement plot procedure if __name__ == '__main__': # possible selections trace subplot 5 @@ -330,7 +296,7 @@ if __name__ == '__main__': # logger_sens # heater_dut_chamber - m = MeasurementPlot(trace_subplot5 = "chamber_sens") + m = MeasurementPlot(trace_subplot5="chamber_sens") plt.ion() measurements = [] @@ -344,29 +310,18 @@ if __name__ == '__main__': 'S21_PHASE': 20 - 2*i, 'S21_MAGNITUDE': 0.3*i, - #acivity temp heater and hum heater, Michael 'TEMP_HEATER': 10, - # activity humidity heater, Michael, Michael 'HUM_HEATER': 3, - # DUT Temperature of external temp sensor added, Michael 'TEMP_DUT': i, - # room temperature of external temp sensor added, Michael 'TEMP_ROOM': 25-i, - # DUT Humidity of external hum sensor added, Michael 'HUM_DUT': 40, - # room humidity of external hum sensor added, Michael 'HUM_ROOM': 45, - # Air pressure of external sensor added, Michael 'AIR_PRESS_ROOM': 1000+10*i, - # measurement instrument temperature of external temp sensor added, Michael 'TEMP_MEAS_INSTR': 40-1.5*i, - # measurement instrument humidity of external hum sensor added, Michael 'HUM_MEAS_INSTR': 55, 'READBACK_TEMP_MEAS_INSTR': 50-2*i, - # measurement instrument humidity of external hum sensor added, Michael 'READBACK_HUM_MEAS_INSTR': 39 - } measurements.append(measurement) diff --git a/Python_script/PostPlot.py b/Python_script/PostPlot.py index 09a1af1..f75d13e 100644 --- a/Python_script/PostPlot.py +++ b/Python_script/PostPlot.py @@ -1,10 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Mar 7 10:15:55 2023 - -@author: pawelzik -""" - import pandas as pd import sys from pathlib import Path @@ -12,23 +5,20 @@ import os from MeasurementPlot import MeasurementPlot import numpy as np - - -# after measurement has finished from stored csv-data a post plot of all temperature steps is plotted and store in -# results folder. class generates a result plot, Michael -# oftional correclation and regression can be calculated and plotted in results, Michael -# option is configureable via ext_sens_data.json, Michael class PostPlot: - def __init__(self, trace_subplot5 = '', legend_loc = 'upper left', \ - legend_bbox_to_anchor = (1.09, 1)): - # set python for opening an separate plot window + """ + Generate a plot of all data files from the individual measurements to have one plot of the whole + data taking sequence. + Optionally, correlation and regression can be calculated and plotted. + """ + def __init__(self, trace_subplot5='', legend_loc='upper left', legend_bbox_to_anchor=(1.09, 1)): + # set python for opening an separate plot window if 'ipykernel' in sys.modules: from IPython import get_ipython get_ipython().run_line_magic('matplotlib', 'qt') - - self.measplot = MeasurementPlot(trace_subplot5 = trace_subplot5,\ - legend_loc = legend_loc, legend_bbox_to_anchor = legend_bbox_to_anchor) + self.measplot = MeasurementPlot(trace_subplot5=trace_subplot5, + legend_loc=legend_loc, legend_bbox_to_anchor=legend_bbox_to_anchor) self.legend_loc = legend_loc self.legend_bbox_to_anchor = legend_bbox_to_anchor @@ -43,7 +33,6 @@ class PostPlot: return data_frame - # plot function for data frame data, Michael def plot_frame_data(self, data_frame, title ='', time_unit ='min'): # set title of plot @@ -56,86 +45,71 @@ class PostPlot: for element in self.measplot.ax1: element.set_xlabel("Time [%s]" %time_unit) - # make a copy of data_frame in parameterlist without changing original during modification + # make a copy of data_frame in parameter list without changing original during modification postplot_data_frame = data_frame.copy() - # time stamp of index = 0 is the start point of time axis, Michael - # substract all other timestamps with time stamp of index zero to get time scale in - # seconds, Michael + # time stamp of index = 0 is the start point of time axis time_sec = postplot_data_frame.TIMESTAMP - postplot_data_frame.TIMESTAMP[0] - # set scaling of time axis depending on the time unit given in parameter list, Michael - # default unit is minutes, Michael + # Set scaling of time axis depending on the time unit given in parameter list. + # Default unit is minutes time_vals = self.scaling_time_axes(time_sec, time_unit) # update Timestamps with calculated time values postplot_data_frame.update(time_vals) - # refresh subflots with data in data frame - self.measplot.draw(postplot_data_frame, pdf_name = '') + # refresh subplots with data in data frame + self.measplot.draw(postplot_data_frame, pdf_name='') # cal PK2PK values of magnitude and phase PK2PK = self.calc_mag_phase_pkpk_values(data_frame) - self.edit_annotation_in_plot(annotate_string = PK2PK) - + self.edit_annotation_in_plot(annotate_string=PK2PK) - - def edit_annotation_in_plot(self, annotate_string ='', anno_fontsize = 16): + def edit_annotation_in_plot(self, annotate_string='', anno_fontsize=16): - # update text of annotation in first subplot self.measplot.annotation.set_text(annotate_string) - - # edit annotation fontsize in first subplot self.measplot.annotation.set_fontsize(anno_fontsize) - - + def calc_mag_phase_pkpk_values(self, data_frame): # calc PK2PK values of magnitude and phase delta_phase = max(data_frame.S21_PHASE) - min(data_frame.S21_PHASE) delta_magnitude = max(data_frame.S21_MAGNITUDE) - min(data_frame.S21_MAGNITUDE) - # generate text for annoation in first subplot + # generate text for annotation in first subplot delta_vals_string = "$\Delta\phi$($S_{21PkPk}$): %.3f °\n$\Delta$|$S_{21PkPk}$|: %.3f dB" \ % (delta_phase, delta_magnitude) return delta_vals_string - - def add_curvefit_to_plot(self, y_lim = None , xvals = [], yvals = [], \ - trace_color = 'None', trace_label = ''): - - if y_lim != None: - # set axis for phaseplot to min, max values + def add_curvefit_to_plot(self, y_lim=None, xvals=[], yvals=[], trace_color='None', trace_label=''): + if y_lim is not None: + # set axis for phase plot to min, max values self.measplot.ax1[0].set_ylim(y_lim) - # set color of trace for curvefit result to green (trace is visible) self.measplot.path_collection_fit.set_color(trace_color) - - # set label of trace curvefit results to name of fit function name self.measplot.path_collection_fit.set_label(trace_label) - # refrest data in meas plot + # refresh data in meas plot self.measplot.path_collection_fit.set_offsets(np.c_[xvals, yvals]) # get legend handles and labels y-axes from subplot magnitude, phase - handles_phase, labels_phase = self.measplot.ax1[0].get_legend_handles_labels() - handles_mag, labels_mag = self.measplot.magnitude_axis.get_legend_handles_labels() - handles_equi0, labels_eqi0 = self.measplot.equi_axis0.get_legend_handles_labels() + handles_phase, labels_phase = self.measplot.ax1[0].get_legend_handles_labels() + handles_mag, labels_mag = self.measplot.magnitude_axis.get_legend_handles_labels() + handles_equi0, labels_eqi0 = self.measplot.equi_axis0.get_legend_handles_labels() - handles = handles_phase + handles_mag + handles_equi0 + handles = handles_phase + handles_mag + handles_equi0 labels = labels_phase + labels_mag + labels_eqi0 # update legend subplot phase, magnitude - self.measplot.ax1[0].legend(handles, labels, loc = self.legend_loc, \ - bbox_to_anchor = self.legend_bbox_to_anchor) + self.measplot.ax1[0].legend(handles, labels, loc=self.legend_loc, + bbox_to_anchor=self.legend_bbox_to_anchor) # refresh plot window self.measplot.fig.canvas.flush_events() - # save figure under the given storepath in parameterlist with the given filename in parameter list, Michael - # file extension is part of filename, Michael + # save figure under the given path and file name def save_fig(self, storepath, filename): if not os.path.exists(storepath): os.makedirs(storepath) @@ -158,24 +132,16 @@ class PostPlot: return time_vals - - -# for manually redo post plot after measurement has finished, Michael if __name__ == '__main__': # set result path for post plot - Results_Path = r'C:\git\climate-lab-test-stand\Python_script\TestData_JBY240' + Results_Path = r'TestData_JBY240' - - # set time unit for post post plot - # default is minutes if entry in parameterlist left empty - # possible entries: 'min' for minutes, 'hours' for hours and 'sec' for seconds time_unit = 'min' - # set storepath for the post plots, Michael - storepath = Results_Path + '\\PostPlots' + storepath = Results_Path + '\\PostPlots' # search all csv files in results folder csv_file_list = list(Path(Results_Path).glob("**/*.csv")) @@ -191,45 +157,29 @@ if __name__ == '__main__': trace_selection = "" - - # create postplot object, Michael plot_obj = PostPlot(trace_subplot5= trace_selection) - # empty data frame for concat the data frames from csv import to plot full transistion + # empty data frame for concat the data frames from csv import to plot full transition concat_data_frame = pd.DataFrame() # plot results for each csv-file for index, csv_file in enumerate(csv_file_list): - # import csv-data from csv-files in list, Michael data_frame = plot_obj.import_csv(str(csv_file)) - - # determine title of plot from csv-filename, Michael title = csv_file.name - # concate datesframe for plotting full transistion data - concat_data_frame = pd.concat([concat_data_frame,data_frame],ignore_index=True, sort = False) - - - # plot frame data - plot_obj.plot_frame_data(data_frame, title, time_unit) - - - # set filename of post plot, Michael + # concatenate data frames for plotting full transition data + concat_data_frame = pd.concat([concat_data_frame,data_frame], ignore_index=True, sort=False) + + plot_obj.plot_frame_data(data_frame, title, time_unit) + filename = str(csv_file.stem) + '.pdf' - - # store post plot under the path taht is set in storepath with the earlier defined filename plot_obj.save_fig(storepath, filename) - - # plot of all steps of a sweep is plotted in one diagram, Michael - # title of this plot will be always full transistion, Michel - plot_obj.plot_frame_data(concat_data_frame, 'Full Transistion', time_unit) - - # filename of plot that contains all steps of sweep is set to FullTransistion, Michael - filename = 'Full_Transistion' + '.pdf' + # plot of all steps of a sweep is plotted in one diagram + plot_obj.plot_frame_data(concat_data_frame, 'Full Transition', time_unit) - # plot with the results of all steps is store under the predefined storpath with the earlier defined filename, Michael + filename = 'Full_Transition' + '.pdf' plot_obj.save_fig(storepath, filename) \ No newline at end of file diff --git a/Python_script/analysis.py b/Python_script/analysis.py index cb22373..fed48b8 100644 --- a/Python_script/analysis.py +++ b/Python_script/analysis.py @@ -4,10 +4,10 @@ import math def extract_stable_data(datafile): datapoint = {} - #df is a pandas data frame + # df is a pandas data frame df = pd.read_csv(datafile) - #extract phase mean and variance for stable measurements (don't ask what loc means) + # extract phase mean and variance for stable measurements (don't ask what loc means) phases = df.loc[df['EQUILIBRIUM_INDICATOR'] == 31, 'S21_PHASE'] if phases.size == 0: return None @@ -55,13 +55,6 @@ def plot_sweep(temperatures, humidities, basename, sweep_type): magnitudes.append(datapoint['magnitude_mean']) magnitude_vars.append(datapoint['magnitude_var']) - # logarithmization - # magnitudes_db = [20 * math.log10(x) for x in magnitudes ] - # is taken to logarithmic scale during measurement, Michael - magnitudes_db = magnitudes - # approximate formula for small errors (linear approximation) - magnitude_vars_db = [20 / math.log(10) * dx/x for x, dx in zip(magnitudes, magnitude_vars)] - fig, ax1 = plt.subplots() if sweep_type == 'temperature': @@ -77,7 +70,7 @@ def plot_sweep(temperatures, humidities, basename, sweep_type): ax1.set_ylabel('S21 phase [deg]', color='blue') ax2 = ax1.twinx() - ax2.errorbar(x_data, magnitudes_db, magnitude_vars_db, marker='x', color='red', linewidth=0) + ax2.errorbar(x_data, magnitudes, magnitude_vars, marker='x', color='red', linewidth=0) ax2.set_ylabel('S21 magnitude [dB]', color='red') fig.tight_layout() # otherwise the right y-label is slightly clipped diff --git a/Python_script/climate-lab-gui.py b/Python_script/climate-lab-gui.py index a0cf1b4..c30bd98 100755 --- a/Python_script/climate-lab-gui.py +++ b/Python_script/climate-lab-gui.py @@ -62,7 +62,6 @@ class TestStandMainWindow(QMainWindow): with open('test_stand_parameter.json', 'r') as f: config_data = json.load(f) - # open json file ext_sens_data.joson and load values from file with open('ext_sensor_channels.json', 'r') as f2: ext_sensor_channels = json.load(f2) @@ -75,9 +74,6 @@ class TestStandMainWindow(QMainWindow): self.setEnabled(True) return - - # ext_sens_data are the imported values from json file ext_sens_data.json , Michael - # config_data['logger_ip'] is the imported ip from jason file test_stand_parameter.json, Michael meas = prototype.Measurements(config_data['chamber_ip'], config_data['vna_ip'], output_basename, False, config_data, ext_sensor_channels, config_data['logger_ip']) try: @@ -92,10 +88,8 @@ class TestStandMainWindow(QMainWindow): analysis.plot_sweep(temperatures, [self.fixedParameter.value()] * len(temperatures), output_basename, 'temperature') - # add ext_sens_data to parameterlist of plot_output method, Michael - # ext_sens_data are the imported values from json file ext_sens_data.json , Michael - prototype.plot_output(output_basename, temp_extensions, True, config_data, ext_sensor_channels, output_basename + - ': Temperature sweep ' + str(temperatures[0]) + '--' + + prototype.plot_output(output_basename, temp_extensions, True, config_data, ext_sensor_channels, + output_basename + ': Temperature sweep ' + str(temperatures[0]) + '--' + str(temperatures[-1]) + ' degC @ ' + str(self.fixedParameter.value()) + ' % r.h.') elif self.humSweepButton.isChecked(): @@ -109,23 +103,19 @@ class TestStandMainWindow(QMainWindow): analysis.plot_sweep([self.fixedParameter.value()] * len(humidities), humidities, output_basename, 'humidity') - # add ext_sens_data to parameterlist of plot_output method, Michael - # ext_sens_data are the imported values from json file ext_sens_data.json, Michael - prototype.plot_output(output_basename, hum_extensions, True, config_data, ext_sensor_channels, output_basename + - ': Humidity sweep ' + str(humidities[0]) + '--' + + prototype.plot_output(output_basename, hum_extensions, True, config_data, ext_sensor_channels, + output_basename + ': Humidity sweep ' + str(humidities[0]) + '--' + str(humidities[-1]) + ' % r.h. @ ' + str(self.fixedParameter.value()) + ' degC') elif self.measurementFileButton.isChecked(): try: n_measurements = meas.perform_measurements(os.path.join(self.start_dir, self.measurementFile.text())) - # add ext_sens_data to parameterlist of plot_output method, Michael - # ext_sens_data are the imported values from json file ext_sens_data.json, Michael - prototype.plot_output(output_basename, range(n_measurements), True, ext_sensor_channels, output_basename) + prototype.plot_output(output_basename, range(n_measurements), True, ext_sensor_channels, + output_basename) except FileNotFoundError as e: QtWidgets.QMessageBox.warning(self, 'Warning', str(e)) - finally: meas.chamber.close() meas.ext_sensors.close() diff --git a/Python_script/prototype.py b/Python_script/prototype.py index d8741b4..365b6d1 100755 --- a/Python_script/prototype.py +++ b/Python_script/prototype.py @@ -32,10 +32,8 @@ MEASUREMENT_STABLE = 0x10 # Class has only attributes which we are using in the read_data_function to read data from VNA and Chamber class MeasurementData: - # values for percentage temp_heater, percentage humidity heater, temperature sensor0, temperature sensor1, air pressure, Michael - # values for measurement instruments temperature, measurement instruments humidity - def __init__(self, timestamp, temp, hum, power, frequency, s11, s21, s12, s22, perc_temp_heater, \ - perc_hum_heater, temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, \ + def __init__(self, timestamp, temp, hum, power, frequency, s11, s21, s12, s22, perc_temp_heater, + perc_hum_heater, temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room, temp_chamber_meas_instr, hum_chamber_meas_instr): self.timestamp = timestamp self.temp = temp @@ -46,27 +44,23 @@ class MeasurementData: self.s21 = s21 self.s12 = s12 self.s22 = s22 - # new variables for percentage temp heater and percentage humidity heater + self.perc_temp_heater = perc_temp_heater self.perc_hum_heater = perc_hum_heater - # new variables for DUT temp, room temp, Michael - # new variable for measurement instruments temp, Michael self.temp_dut = temp_dut self.temp_room = temp_room self.temp_meas_instr = temp_meas_instr self.temp_chamber_meas_instr=temp_chamber_meas_instr - # new variables for DUT humidity, room humidity, air pressure room, Michael self.hum_dut = hum_dut self.hum_room = hum_room self.air_press_room = air_press_room - # new variable for measurement instruments humidity, Michael self.hum_meas_instr = hum_meas_instr self.hum_chamber_meas_instr = hum_chamber_meas_instr class Measurements: - def __init__(self, chamber_address, vna_address, output_basename, standby, config_data, \ - ext_sensor_channels, logger_adress): + def __init__(self, chamber_address, vna_address, output_basename, standby, config_data, + ext_sensor_channels, logger_address): self.max_delta_temp = config_data['delta_temp'] self.max_delta_hum = config_data['delta_hum'] self.max_delta_mag = config_data['delta_mag'] @@ -81,11 +75,13 @@ class Measurements: self.instr_chamber = climate_chamber.create_chamber(config_data['instr_chamber_ip'], target_accuracy) self.vna = VNA.create_vna(vna_address, target_accuracy) - # new object for external sensors, decice 'ALMEMO710', fixed IP in code for device, Michael + + # data logger for external sensors if self.logger_model == '710': - self.ext_sensors = almemo710(ip = logger_adress, timeout = 10) + self.ext_sensors = almemo710(ip=logger_address, timeout=10) else: - self.ext_sensors = almemo2490(ip = logger_adress, timeout = 10) + self.ext_sensors = almemo2490(ip=logger_address, timeout=10) + self.standby = standby self.output_basename = output_basename self.clock = virtual_time.get_clock(chamber_address, target_accuracy) @@ -101,11 +97,9 @@ class Measurements: self.vna.create_new_trace("Trace4", "S22") self.postplot_obj = None - # request all sensor channels from ALMEMO710 of all sensors that are connected to this device, Michael self.ext_sensors.request_sens_channel_list() - self.measurement_plot = MeasurementPlot.MeasurementPlot(trace_subplot5 = \ - config_data['trace_subplot5']) + self.measurement_plot = MeasurementPlot.MeasurementPlot(trace_subplot5=config_data['trace_subplot5']) self.data_collection = [] def perform_measurements(self, sweep_file): @@ -121,15 +115,16 @@ class Measurements: self.measurement_plot.fig.suptitle("Measurement "+str(measurement_number)+': ' + str(next_temp) + ' degC, ' + str(next_hum) + ' rel. hum.', color="red") - self.perform_single_measurement(self.output_basename+'_'+str(measurement_number)+'.csv', next_temp, next_hum, - next_soaking, next_reads) + self.perform_single_measurement(self.output_basename+'_'+str(measurement_number)+'.csv', next_temp, + next_hum, next_soaking, next_reads) measurement_number += 1 except KeyboardInterrupt: pass except MeasurementPlot.PlottingError: # Just the plotting failed, probably because the window was closed - # The measurement was partly done and should be plottet, so the following code has to see the correct number of successful measurements + # The measurement was partly done and should be plotted, so the following code has to see the correct + # number of successful measurements measurement_number += 1 plt.close() @@ -172,19 +167,14 @@ class Measurements: pass except MeasurementPlot.PlottingError: # Remove the remaining measurements from the list. - # One measurement was partly done and should be plottet, so we leave 'val' in the list - del sweep_values[sweep_values.index(val)+1 : ] - - + # One measurement was partly done and should be plotted, so we leave 'val' in the list + del sweep_values[sweep_values.index(val)+1:] plt.close() return sweep_values def perform_single_measurement(self, output, target_temp, target_hum, soaking_time, n_stable_reads): with open(output, mode='w', newline='') as csv_file: - # fieldnames for percentage temperature heater and percntage humidity heater, - # DUT Temperature, DUT humidity, room temperature, room humidity, airpressure room added, - # measurement instruments temperature, measurement instruments humidity, Michael fieldnames = ['TIMESTAMP', 'TARGET_TEMPERATURE', 'READBACK_TEMPERATURE', 'TARGET_HUMIDITY', 'READBACK_HUMIDITY', 'RF_POWER', 'RF_FREQUENCY', 'DUT_IDENTIFIER', 'RUN_ID', 'EQUILIBRIUM_INDICATOR', 'TEMP_HEATER', 'HUM_HEATER', @@ -207,8 +197,8 @@ class Measurements: number_of_soaking_reads = soaking_time / self.sleep_time + 1 do_another_measurement = True - #next_read_time is the starttime of the second read (i.e. read after this one). The time of - #this read (i.e. the first read in the measurement) is now(). + # next_read_time is the starttime of the second read (i.e. read after this one). The time of + # this read (i.e. the first read in the measurement) is now(). next_read_time = self.clock.time() + self.sleep_time while do_another_measurement: # wait until set point is reached (+soaking time) @@ -312,9 +302,9 @@ class Measurements: def read_data(self): [temp, hum, mode, alarms] = self.chamber.read_monitor().split(',') - # read percentage temperature heater, humidity heater from climate chamber, Michael perc_temp_heater, perc_hum_heater = self.chamber.get_heater_percentage() - # loop with 10 Itherations in case of VNA readout error, Michael + + # loop with 10 iterations in case of VNA readout error iteration = 0 while iteration < 10: try: @@ -325,32 +315,27 @@ class Measurements: s12 = self.get_trace_data("Trace2") s21 = self.get_trace_data("Trace3") s22 = self.get_trace_data("Trace4") - # if readout is successful, leave loop, Michael + # if readout is successful, leave loop break # exception for pyvisa error or timeout except pyvisa.errors.VisaIOError: - # call reset function for VNA statstus register and error queue + # call reset function for VNA status register and error queue self.vna.reset_status() - print('An error occured during VNA read out') - # wait one second for next try, Michael + print('An error occurred during VNA read out') + # wait one second for next try time.sleep(1) - iteration+=1 - + iteration += 1 - # request temperatures, humidities and and air pressure from external sensors, Michael - temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, \ - air_press_room = self.get_ext_sensor_values() + temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room = \ + self.get_ext_sensor_values() [temp_chamber_meas_instr, hum_chamber_meas_instr, mode_meas_instr, alarms_meas_instr] = \ self.instr_chamber.read_monitor().split(',') - # values percentage temp_heater, percentage hum_heater, temp_dut, temp_room, temp_meas_instr, - # hum_dut, hum_room, hum_meas_instr air_press_room added to parameterlist in returned - # values, Michael - return MeasurementData(int(self.clock.time()), temp, hum, power, frequency, s11, s21, s12, s22, \ - perc_temp_heater, perc_hum_heater, temp_dut, temp_room, temp_meas_instr, \ - hum_dut, hum_room, hum_meas_instr, air_press_room, \ - temp_chamber_meas_instr, hum_chamber_meas_instr) + return MeasurementData(int(self.clock.time()), temp, hum, power, frequency, s11, s21, s12, s22, + perc_temp_heater, perc_hum_heater, temp_dut, temp_room, temp_meas_instr, + hum_dut, hum_room, hum_meas_instr, air_press_room, temp_chamber_meas_instr, + hum_chamber_meas_instr) def store_and_plot_data(self, target_temp, target_hum, data, equi_indicator): measurement = { @@ -362,23 +347,14 @@ class Measurements: 'RF_POWER': data.power, 'RF_FREQUENCY': data.frequency, 'EQUILIBRIUM_INDICATOR': equi_indicator, - # percentage temperature heater added, Michael 'TEMP_HEATER': data.perc_temp_heater, - # percentage humidity heater added, Michael - 'HUM_HEATER': data.perc_hum_heater, - # Temperature for DUT of external temp sensors added, Michael + 'HUM_HEATER': data.perc_hum_heater, 'TEMP_DUT': data.temp_dut, - # Temperature for room of external temp sensors added, Michael 'TEMP_ROOM': data.temp_room, - # Temperature for measure instruments of external temp sensors added, Michael - 'TEMP_MEAS_INSTR': data.temp_meas_instr, - # Humidity for DUT of external hum sensors added, Michael + 'TEMP_MEAS_INSTR': data.temp_meas_instr, 'HUM_DUT': data.hum_dut, - # Humidity for room of external hum sensors added, Michael 'HUM_ROOM': data.hum_room, - # Humidity for measurement instruments of external hum sensors added, Michael - 'HUM_MEAS_INSTR': data.hum_meas_instr, - # Air pressure for room of external sensors added, Michael + 'HUM_MEAS_INSTR': data.hum_meas_instr, 'AIR_PRESS_ROOM': data.air_press_room, 'READBACK_TEMP_MEAS_INSTR': float(data.temp_chamber_meas_instr), 'READBACK_HUM_MEAS_INSTR': float(data.hum_chamber_meas_instr), @@ -401,45 +377,34 @@ class Measurements: def get_trace_data(self, trace): return self.vna.get_list_of_measurement_values(trace, "SDAT") - - # new method to request temperaure values from external sensors of ahlborn, Michael def get_ext_sensor_values(self): - # empty dictonary for sensor values sens_vals_dict = dict() - - # request temperatures, humidities and and air pressure from external sensors, Michael self.ext_sensors.request_meas_vals_all_channels() - # request sensor value vor channel in dictonary and add item to dictionary of sensor values for key, value in self.ext_sensor_channels.items(): - - sens_vals_dict[key] = self.ext_sensors.fetch_channel_param_from_meas_buffer(pattern = \ - value)[0].meas_val + sens_vals_dict[key] = self.ext_sensors.fetch_channel_param_from_meas_buffer(pattern=value)[0].meas_val # set values from series to variables sens_vals_series = pd.Series(sens_vals_dict) # set series items for sensor value to matching variable - temp_dut = sens_vals_series.temp_dut - temp_room = sens_vals_series.temp_room - hum_dut = sens_vals_series.hum_dut - hum_room = sens_vals_series.hum_room + temp_dut = sens_vals_series.temp_dut + temp_room = sens_vals_series.temp_room + hum_dut = sens_vals_series.hum_dut + hum_room = sens_vals_series.hum_room air_press_room = sens_vals_series.air_press_room - - + if self.logger_model == '710': - temp_meas_instr = sens_vals_series.temp_meas_instr - hum_meas_instr = sens_vals_series.hum_meas_instr + temp_meas_instr = sens_vals_series.temp_meas_instr + hum_meas_instr = sens_vals_series.hum_meas_instr else: temp_meas_instr = -30 hum_meas_instr = 0 - return temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, \ - air_press_room + return temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room - def calculate_complex_numbers(self, values_list): real_num = values_list[::2] imaginary_num = values_list[1::2] @@ -491,37 +456,25 @@ class Measurements: return (target_hum-self.max_delta_hum <= float(readback_hum)) and \ (float(readback_hum) <= target_hum+self.max_delta_hum) + def plot_output(output_basename, measurements_appendices, show_blocking_plot, config_data, ext_sens_data, title = ''): - # set time unit for PostPlot of measurement results to imported value from json file - # test_stand_parameter.json, Michael - time_unit = str(config_data['time_unit']) + time_unit = str(config_data['time_unit']) list_of_frames = [] - # storepath is set to working directory with subfolder Plots, Michael - storepath = os.path.join(os.getcwd(),'PostPlots') - # create objet for PostPlot class in PostPlot module, Michael - - post_plot = PostPlot.PostPlot(trace_subplot5 = config_data['trace_subplot5']) + storepath = os.path.join(os.getcwd(), 'PostPlots') + + post_plot = PostPlot.PostPlot(trace_subplot5=config_data['trace_subplot5']) for index, m in enumerate(measurements_appendices): measurement_name = output_basename+'_'+str(m) list_of_frames.append(pd.read_csv(measurement_name+'.csv')) - - # plot frame data - post_plot.plot_frame_data(list_of_frames[-1], title, time_unit,) - - # store generated postplot to the in storepath defined destination, Michael + post_plot.plot_frame_data(list_of_frames[-1], title, time_unit,) post_plot.save_fig(storepath, measurement_name+'.pdf') combined_data_frame = pd.concat(list_of_frames, ignore_index=True, sort=False) - - # create postplot of all measurements, Michael - # add the imported time unit from ext_sens_data.json to parameterlist post_plot.plot_frame_data(combined_data_frame, title, time_unit) - - # save postplot of all measurents to the in storpath defined destination and name it FullTransistion post_plot.save_fig(storepath, 'FullTransistion.pdf') if show_blocking_plot: @@ -531,7 +484,6 @@ def plot_output(output_basename, measurements_appendices, show_blocking_plot, co config_data['trace_subplot5'] ) plot.draw(combined_data_frame, output_basename + '_graph.pdf') - def run_temperature_sweep_from_file(temperature_sweep_file, meas): with open(temperature_sweep_file) as file: @@ -601,7 +553,7 @@ if __name__ == '__main__': plot_output(output_basename, range(n_measurements), args.plot, output_basename) if args.temperaturesweepfile: temperatures, humidity = run_temperature_sweep_from_file(args.temperaturesweepfile, mes) - #run analysis here + # run analysis here temp_extensions = [] for t in temperatures: temp_extensions.append(str(t)+'deg_'+str(humidity)+'rh') @@ -614,4 +566,3 @@ if __name__ == '__main__': mes.ext_sensors.close() # mes.plot_output(output) - -- GitLab