import pandas as pd import matplotlib.pyplot as plt import numpy as np import time import sys # Different exceptions can be thrown while plotting, depending on the backend. # We catch them all locally and raise our own exception instead class PlottingError(Exception): "Raised when plotting fails" pass class MeasurementPlot: def __init__(self, title='', env_cond_sensors ='', legend_loc = 'upper left',\ legend_bbox_to_anchor = (1.09, 1)): # set python for opening an separate plot window when starting from anaconda, Michael if 'ipykernel' in sys.modules: from IPython import get_ipython get_ipython().run_line_magic('matplotlib', 'qt') self.env_cond_sensors = env_cond_sensors # parameter for legend of subplots self.legend_loc = legend_loc self.legend_bbox_to_anchor = legend_bbox_to_anchor # Third, Fourth and Fifth plot for additional external sensors, Michael 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 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, \ 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) 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') # 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") self.humidity_axis = self.ax1[1].twinx() 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") # 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.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.ax1[2].set_xlabel("TIMESTAMP") self.ax1[2].set_ylabel("TEMPERATURE [°C]", color='red') self.ext_sens_hum_axis.set_ylabel("HUMIDITY [%RH]", color = 'purple') 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.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.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.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.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) # Fifth plot: parameter of external sensors # meas instruments temperature, meas instruments humidity, Michael # configuration of used sensor port in ext_sens_data.json, Michael 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.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')) # # 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] 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}) 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) # refresh data for phase in subplot for phase and magnitude phases = data_frame.S21_PHASE minimum, maximum = self.get_extended_min_max(phases) self.ax1[0].set_ylim(minimum, maximum) self.path_collection_phase.set_offsets(np.c_[timestamps, phases]) self.path_collection_fit.set_offsets(np.c_[[], []]) # refresh data for magnitude in subplot for phase and magnitude magnitudes = data_frame.S21_MAGNITUDE minimum, maximum = self.get_extended_min_max(magnitudes) self.magnitude_axis.set_ylim(minimum, maximum) self.path_collection_mag.set_offsets(np.c_[timestamps, magnitudes]) # refresh data for chamber temperature in subplot for chamber temperature and humidity temperatures = data_frame.READBACK_TEMPERATURE minimum, maximum = self.get_extended_min_max(temperatures) self.ax1[1].set_ylim(minimum, maximum) self.path_collection_temp.set_offsets(np.c_[timestamps, temperatures]) # refresh data for chamber humidity in subplot for chamber temperature and humidity humidities = data_frame.READBACK_HUMIDITY minimum, maximum = self.get_extended_min_max(humidities) 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 temp_dut = data_frame.TEMP_DUT temp_room = data_frame.TEMP_ROOM minimum, maximum = self.get_extended_min_max(temp_dut) self.ax1[2].set_ylim(minimum, maximum) self.path_collection_temp_dut.set_offsets(np.c_[timestamps, temp_dut]) minimum, maximum = self.get_extended_min_max(temp_room) self.ax1[3].set_ylim(minimum, maximum) 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) minimum, maximum = self.get_extended_min_max(hum_room) self.sec_ext_hum_sens_axis.set_ylim(minimum, maximum) 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 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) minimum, maximum = self.get_extended_min_max(val_trace_1) self.ax1[4].set_ylim(minimum, maximum) minimum, maximum = self.get_extended_min_max(val_trace_2) 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]) if not pdf_name == '': self.fig.savefig(pdf_name) plt.show() if plt.isinteractive(): try: self.fig.canvas.draw() self.fig.canvas.flush_events() except Exception as e: raise PlottingError from e def config_fith_subplot(self): # key names for config parameter fith subplot keys = ["label_trace_1", "label_trace_2", "y_axis", "sec_y_axis"] if self.env_cond_sensors == 'ext': # values for plotting evironmental conditions in measurement instrument chamber values = ["temp sensor\nmeas instruments", "hum sensor\nmeas instruments", \ "TEMPERATURE [°C]", "HUMIDITY [%RH]"] elif self.env_cond_sensors == 'int': # values for plotting evironmental conditions in measurement instrument chamber values = ["chamber temperature\nmeas instruments", "chamber humidity\nmeas instruments", \ "TEMPERATURE [°C]", "HUMIDITY [%RH]"] else: # values for plotting heater activity in fith subplot values = ["Temp Heater", "Temp Heater", \ "ACTIVITY [%]","ACTIVITY [%]"] # generate dictionary from selection config_subplot_dict = dict(zip(keys, values)) return config_subplot_dict def refresh_param_fith_subplot(self, data_frame): if self.env_cond_sensors == 'ext': # chose sensor values for refreshing fith subplot val_trace_1 = data_frame.TEMP_MEAS_INSTR val_trace_2 = data_frame.HUM_MEAS_INSTR elif self.env_cond_sensors == 'int': val_trace_1 = data_frame.READBACK_TEMP_MEAS_INSTR val_trace_2 = data_frame.READBACK_HUM_MEAS_INSTR else: # chose heater values for refreshing fift 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): distance = array.max() - array.min() if distance == 0.: distance = 1 return array.min()-0.05*distance, array.max()+0.05*distance # test procedure for measurement plot procedure if __name__ == '__main__': m = MeasurementPlot(env_cond_sensors = "int") plt.ion() measurements = [] # generation of datapoints for plot for i in range(20): measurement = { 'TIMESTAMP': i, 'READBACK_TEMPERATURE': 25 - i, 'READBACK_HUMIDITY': 10 + 0.1*i, 'EQUILIBRIUM_INDICATOR': i % 4, '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) data_frame = pd.DataFrame(measurements) # plot of data frame with test data for actual step m.draw(data_frame) # plot of step number print(str(i)) time.sleep(0.3) print('I am done. ') plt.ioff() m.draw(data_frame, 'the.pdf')