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='', legend_loc = 'lower right', legend_bbox_to_anchor = None): # 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') # 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.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] labels = [pc.get_label() for pc in all_path_collections] 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 self.path_collection_temp_meas_instr = self.ax1[4].scatter([], [], c='black', marker='p', label="meas instr temperature") self.third_ext_hum_sens_axis = self.ax1[4].twinx() self.path_collection_hum_meas_instr = self.third_ext_hum_sens_axis.scatter([], [], c='brown', marker="*", label="meas instr humidity") # units added to y-axes of subplot 5, Michael self.ax1[4].set_xlabel("TIMESTAMP") self.ax1[4].set_ylabel("TEMPERATURE [°C] ", color='black') self.third_ext_hum_sens_axis.set_ylabel("HUMIDITY [%RH]", color='brown') self.ax1[4].grid(True, linestyle=":") all_path_collections = [self.path_collection_temp_meas_instr, \ self.path_collection_hum_meas_instr] 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) ###################################################################################### ## Block for plot Heater percentage in fifth subplot # heater activity of temperature heater and humidity heater # values requested from environmental test chamber # self.path_collection_temp_heater = self.ax1[4].scatter([],[], c='black', marker='<', label='Temp Heater') # self.path_collection_hum_heater = self.ax1[4].scatter([],[], c='brown', marker='o', label='Hum Heater') # self.ax1[4].set_xlabel("TIMESTAMP") # self.ax1[4].set_ylabel("HEATER PERCENTAGE [%]", color='black') # self.ax1[4].grid(True, linestyle=":") # all_path_collections = [self.path_collection_temp_heater, \ # self.path_collection_hum_heater] # 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]) # 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, 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 temp_meas_instr = data_frame.TEMP_MEAS_INSTR hum_meas_instr = data_frame.HUM_MEAS_INSTR minimum, maximum = self.get_extended_min_max(temp_meas_instr) self.ax1[4].set_ylim(minimum, maximum) minimum, maximum = self.get_extended_min_max(hum_meas_instr) self.third_ext_hum_sens_axis.set_ylim(minimum, maximum) self.path_collection_temp_meas_instr.set_offsets(np.c_[timestamps, temp_meas_instr]) self.path_collection_hum_meas_instr.set_offsets(np.c_[timestamps, hum_meas_instr]) ######################################################################### ## Block for refreshing heater percentages in subplot for heater values # refresh heater percentage values of climate chamber in subplot of heater values, Michael # temp_heater = data_frame.TEMP_HEATER # hum_heater = data_frame.HUM_HEATER # min_temp_heater, max_temp_heater = self.get_extended_min_max(temp_heater) # min_hum_heater, max_hum_heater = self.get_extended_min_max(hum_heater) # minimum = min(min_temp_heater, min_hum_heater) # maximum = max(max_temp_heater, max_hum_heater) # self.ax1[4].set_ylim(minimum-0.05*(maximum-minimum), maximum+0.05*(maximum-minimum)) # self.path_collection_temp_heater.set_offsets(np.c_[timestamps, temp_heater]) # self.path_collection_hum_heater.set_offsets(np.c_[timestamps, hum_heater]) ######################################################################### 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 # 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() 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, ############################################################### ##Block for simulation acivity temp heater and hum heater # 'TEMP_HEATER': 10, # # percentage humidity heater, 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 } 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')