def plot_id_focalplane(figsize=(30, 30)): ''' plot all the different identity names of each pixel in the focal plane FPidentity is a recarray of shape 34*34 ''' global FPidentity if FPidentity is None: FPidentity = make_id_focalplane() scale_factor = figsize[0] title_fontsize = 0.67 * scale_factor label_fontsize = 0.2 * scale_factor fig = plt.figure(figsize=figsize) figure_window_title(fig, 'QUBIC Focal Plane ID Matrix') ax = fig.add_axes([0, 0, 1, 1]) ax.text(0.5, 0.96, 'QUBIC Focal Plane ID Matrix', ha='center', va='bottom', transform=ax.transAxes, fontsize=title_fontsize) ax.set_xlim(-1, 35) ax.set_ylim(-1, 35) ax.set_aspect('equal') for fp_idx in range(len(FPidentity)): txt = 'Q%i' % (FPidentity[fp_idx].quadrant) quadrant = FPidentity[fp_idx].quadrant asic = FPidentity[fp_idx].ASIC colour = asic_colour[asic - 1] row = FPidentity[fp_idx].row col = FPidentity[fp_idx].col if FPidentity[fp_idx].TES == 0: colour = 'black' txt += '\nFP%4i' % FPidentity[fp_idx].index else: txt += ' %s\nFP%4i\nPIX%03i\nASIC%i\nTES%03i'\ % (FPidentity[fp_idx].matrix.decode('UTF-8'), FPidentity[fp_idx].index, FPidentity[fp_idx].PIX, FPidentity[fp_idx].ASIC, FPidentity[fp_idx].TES) plot_square(col, row, colour=colour, labelcolour='white', label=txt, fontsize=label_fontsize) return
def plot_instrument_layout(q): ''' plot the layout of the focal plane according to the qubicsoft simulation software q is a QubicInstrument object ''' basename = os.path.basename(q.calibration.detarray) config = q.config ttl = 'Layout for %s Instrument' % config fig = plt.figure(figsize=(20, 20)) figure_window_title(fig, ttl) ax = fig.add_axes([0, 0, 1, 1]) ax.set_aspect('equal') q.plot() for idx, pos in enumerate(q.detector.center[:, 0:2]): quadrant_idx = q.detector.quadrant[idx] quadrant = quadrant_idx + 1 fpindex = q.detector.index[idx] lbl = 'Q%i\n%i' % (quadrant, fpindex) plt.text(pos[0], pos[1], lbl, va='center', ha='center', fontsize=12, color=quadrant_colour[quadrant_idx]) ax.text(0.5, 0.99, ttl, va='top', ha='center', fontsize=20, transform=ax.transAxes) return
def demodulate(self, asic=None, TES=None, offset=None, interval=None, calsource=True, period=None, align_clocks=False, timeaxistype='pps', doplot=True, xwin=True): ''' Interpolating source data to the timestamps of the data asic: the asic number (1 to 16) TES: the TES number (1 to 128) keyword 'all' means average all TES an array of size 128 of bool with the TES to average together offset: the constant offset between calsource timestamps and data timestamps this is of the order of 0.1sec interval is the time interval in which to calculate the demodulation calsource: you can force to not use the calsource period: fold at the given period. Default is to get it from the calsource information align_clocks: you can force the timestamps of the calsource and the data to start at the same time doplot: make plots xwin: if making a plot, do not plot to screen if xwin==False ''' if asic is None: print("\nPlease give an asic number\n") return None if TES is None: print( "\nPlease give a TES number or an array of TES to average together, or 'all'\n" ) return None if interval is None: # set interval to have no effect t0_interval = 0 tend_interval = float( (dt.datetime.utcnow() + dt.timedelta(days=7)).strftime('%s')) interval = (t0_interval, tend_interval) interval = np.array(interval, dtype=np.float) given_period = period errlist = [] retval = {} retval['asic'] = asic retval['TES'] = TES retval['dataset'] = self.dataset_name retval['calsource info'] = self.calsource_info() if isinstance( TES, np.ndarray) and TES.shape == (128, ) and TES.dtype == np.bool: self.printmsg('taking average of %i TES from ASIC %i' % (TES.sum(), asic)) TESstr = 'average of %i selected TES' % TES.sum() adu = self.timeline_array(asic=asic) data = adu[TES].mean(axis=0) elif TES == 'all': self.printmsg('taking average of all TES from ASIC %i' % asic) TESstr = 'all TES' adu = self.timeline_array(asic=asic) data = adu.mean(axis=0) else: try: data = self.timeline(asic=asic, TES=TES) if data is None: return retval TESstr = 'TES%03i' % TES except: self.printmsg('ERROR! Inappropriate argument for TES.') return retval t_data_orig = self.timeaxis(datatype='sci', asic=asic, axistype=timeaxistype) t_data = t_data_orig.copy() t0_data = t_data[0] if t0_data > 1494453600: # if the timeaxis is made from the sampling time and starts at zero do_tzone_correction = True else: do_tzone_correction = False t_src_orig, v_src = self.calsource() if v_src is None or not calsource: use_calsource = False msg = 'No calsource.' self.printmsg(msg, verbosity=3) errlist.append(msg) t_src = t_data data_src = data else: # shift source to oscillate around zero use_calsource = True t_src = t_src_orig.copy() data_src = -v_src + v_src.mean( ) # source sampling inverted compared to data t0_src = t_src[ 0] # this will be modified after the offset is calculated below retval['t0 source'] = t0_src # for some data, the source is in UT while the detector data is in localtime tz_offset = 0.0 tz_count = 0 if do_tzone_correction: delta_tz = np.abs(t0_data - t0_src) while delta_tz > 3599: tz_offset += 3600 delta_tz -= 3600 tz_count += 1 if (t0_data - t0_src) < 0: tz_offset = -tz_offset retval['time zone offset'] = tz_offset retval['time zone offset hours'] = tz_count self.printmsg('time zone offset %f' % tz_offset, verbosity=3) # put data in UT (assume calsource was in UT) t_data -= tz_offset t0_data = t_data[0] interval -= tz_offset # if forcing the alignment of the clocks, make t_src start at the same time as t_data if align_clocks: self.printmsg('forcing alignment of the clocks', verbosity=3) align_offset = t_src[0] - t_data[0] t_src -= align_offset t0_src = t_src[0] self.printmsg('t0_src=%f' % t0_src, verbosity=3) else: align_offset = 0 retval['clock forced realignment offset'] = align_offset # truncate the data timeline to overlapping time or to the given interval t0_list = [t0_data, t0_src, interval[0]] self.printmsg('t0_list = %s' % t0_list, verbosity=3) tend_list = [t_data[-1], t_src[-1], interval[1]] self.printmsg('tend_list = %s' % tend_list, verbosity=3) tstart = max(t0_list) tend = min(tend_list) self.printmsg('tstart,tend = %.6f, %.6f' % (tstart, tend), verbosity=3) if tend < tstart: self.printmsg( 'Error! The given interval does not have overlapping times with calsource and data.' ) self.printmsg(' Maybe try with option: align_clocks=True') return retval idx_start, idx_end = get_index_interval(t_data, (tstart, tend)) retval['data time start index'] = idx_start retval['data time end index'] = idx_end self.printmsg('data: idx_start, idx_end = %i, %i' % (idx_start, idx_end), verbosity=3) t_data = t_data[idx_start:idx_end] data = data[idx_start:idx_end] t0_data = t_data[0] retval['t0 data'] = t0_data t0_str = dt.datetime.utcfromtimestamp(t0_data).strftime( '%Y-%m-%d %H:%M:%S.%f') t0_filenamestr = dt.datetime.utcfromtimestamp(t0_data).strftime( '%Y%m%d-%H%M%S') # truncate the source timeline to overlapping time or to the given interval idxsrc_start, idxsrc_end = get_index_interval(t_src, (tstart, tend)) self.printmsg('src: idx_start, idx_end = %i, %i' % (idxsrc_start, idxsrc_end), verbosity=3) t_src = t_src[idxsrc_start:idxsrc_end] data_src = data_src[idxsrc_start:idxsrc_end] # fit data to sine curve amplitude = 0.5 * (data.max() - data.min()) calinfo = self.calsource_info() if given_period is None: if calinfo is not None and 'frequency' in calinfo['modulator'].keys(): period = self.calsource_info()['modulator']['frequency'] else: period = 1.0 timeshift = 0.0 amplitude_offset = data.mean() first_guess = (amplitude, period, timeshift, amplitude_offset) data_fit = fit_sine_curve(t_data - t0_data, data, first_guess=first_guess) retval['data fit'] = data_fit model_data = sine_curve_model(t_data - t0_data, data_fit['amplitude'], data_fit['period'], data_fit['timeshift'], data_fit['offset']) # find the first time of max (when sin(2pi((x+timeshift)/period) = 1) t_data_max = data_fit['period'] / 4 - data_fit['timeshift'] # sometimes, curve_fit returns a negative amplitude, which is equivalent to a half-period phase shift if data_fit['amplitude'] < 0: t_data_max += 0.5 * data_fit['period'] retval['t_data max'] = t_data_max # fit source to sine curve if use_calsource: amplitude = 0.5 * (data_src.max() - data_src.min()) if self.calsource_info() is not None: period = self.calsource_info()['modulator']['frequency'] elif given_period is None: period = 1.0 else: period = given_period timeshift = 0.0 amplitude_offset = 0.0 first_guess = (amplitude, period, timeshift, amplitude_offset) source_fit = fit_sine_curve(t_src - t0_data, data_src, first_guess=first_guess) # find the first time of max (when sin(2pi((x+timeshift)/period) = 1) t_src_max = source_fit['period'] / 4 - source_fit['timeshift'] # sometimes, curve_fit returns a negative amplitude, which is equivalent to a half-period phase shift if source_fit['amplitude'] < 0: t_src_max += 0.5 * source_fit['period'] else: source_fit = data_fit t_src_max = t_data_max data_src = data t_src = t_data retval['source fit'] = source_fit retval['t_src max'] = t_src_max if given_period is None: period = source_fit['period'] else: period = given_period retval['period'] = period # this is the period we use for demodulation # find the constant timestamp offset between source and data # for more info: http://qubic.in2p3.fr/wiki/pmwiki.php/TD/Demodulation if offset is None: offset = t_data_max - t_src_max retval['source-data time offset'] = offset self.printmsg('source-data time offset %f' % offset, verbosity=3) # adjust the source timeline by the constant offset # go back to the original source data before applying the offset # and then find the appropriate interval if use_calsource: t_src_orig, v_src = self.calsource() t_src = t_src_orig.copy() data_src = -v_src + v_src.mean() t_src += (offset - align_offset) t0_src = t_src[0] # truncate the source timeline to overlapping time or to the given interval idxsrc_start, idxsrc_end = get_index_interval(t_src, (tstart, tend)) retval['source time start index'] = idxsrc_start retval['source time end index'] = idxsrc_end self.printmsg('src: idx_start, idx_end = %i, %i' % (idxsrc_start, idxsrc_end), verbosity=3) t_src = t_src[idxsrc_start:idxsrc_end] data_src = data_src[idxsrc_start:idxsrc_end] model_src = sine_curve_model(t_src - t0_data, source_fit['amplitude'], source_fit['period'], source_fit['timeshift'] - offset, source_fit['offset']) # some values to return npts = len(data) retval['t_data'] = t_data retval['data'] = data retval['n data points'] = npts retval['interval'] = (t_data[0], t_data[-1]) ### Interpolating source data to the timestamps of the data ### and making the product of the detector and the source ### the source is renormalized, but the data is only shifted to oscillate around 0 if use_calsource: self.printmsg( 'demodulate: number of points for t_data, t_src, data_src: %i, %i, %i' % (len(t_data), len(t_src), len(data_src)), verbosity=3) data_src_interp = np.interp(t_data - t0_data, t_src - t0_data, data_src) else: data_src_interp = data product = renorm(data_src_interp) * (data - data.mean()) retval['calsource interpolated to data time axis'] = data_src_interp # demodulate, smoothed over a period freq_sampling = 1. / ((t_data[-1] - t_data[0]) / npts) size_period = int(freq_sampling * period) + 1 filter_period = np.ones(size_period) / size_period try: demodulated = fftconvolve(product, filter_period, mode='same') except: msg = 'ERROR calculating demodulation' errlist.append(msg) self.printmsg(msg, verbosity=3) demodulated = np.zeros(size_period) retval['freq_sampling'] = freq_sampling retval['size_period'] = size_period retval['filter_period'] = filter_period retval['demodulated'] = demodulated retval['n demodulated points'] = len(demodulated) # rebin the result by period bins period_index = ((t_data - t_data[0]) / period).astype(int) allperiods = np.unique(period_index) binned_t = np.zeros(len(allperiods)) binned_demodulated = np.zeros((len(allperiods))) binned_stdev = np.zeros((len(allperiods))) binned_npts = np.zeros((len(allperiods))) for idx, p_index in enumerate(allperiods): idxrange = period_index == p_index binned_t[idx] = np.mean(t_data[idxrange] - t_data[0]) binned_demodulated[idx] = np.mean(demodulated[idxrange]) binned_stdev[idx] = np.std( demodulated[idxrange]) # / np.sqrt(idxrange.sum()) really? binned_npts[idx] = idxrange.sum() retval['binned t'] = binned_t retval['binned demodulated'] = binned_demodulated retval['binned stdev'] = binned_stdev retval['demodulated signal'] = binned_demodulated[1:-1].mean() retval['demodulated signal error'] = binned_stdev[1:-1].mean() retval['npts per bin'] = binned_npts # fold the data and the calsource folded_t_data, folded_data, folded_dataerr = fold_data( t_data - t0_data, data, period) retval['folded t_data'] = folded_t_data retval['folded data'] = folded_data retval['folded data error'] = folded_dataerr if use_calsource: folded_t_src, folded_src, folded_srcerr = fold_data( t_src - t0_data, data_src, period) retval['folded t_src'] = folded_t_src retval['folded src'] = folded_src retval['folded src error'] = folded_srcerr # if not plotting, exit now if not doplot: retval['axes'] = None retval['fig'] = None return retval if xwin: plt.ion() else: plt.ioff() fig = plt.figure() retval['fig'] = fig figure_window_title(fig, 'data/calsource interpolation') axes = [] # first column of plots data_label = 'ASIC %i, %s' % (asic, TESstr) axes.append(fig.add_axes((0.03, 0.68, 0.62, 0.3))) axes[-1].plot((t_data - t0_data), renorm(data), label=data_label, ls='none', marker='x') axes[-1].plot((t_data - t0_data), renorm(model_data), label='data sine model', color='orange') axes[-1].plot([t_data_max, t_data_max], [-2, 2], color='orange', linewidth=3, label='first data peak') if use_calsource: axes[-1].plot((t_src - t0_data), renorm(data_src), label='source', ls='none', marker='+') axes[-1].plot((t_src - t0_data), renorm(model_src), label='source sine model', color='red') axes[-1].plot([t_src_max, t_src_max], [-2, 2], color='red', linewidth=3, label='first source peak') axes[-1].legend() axes[-1].text(0.5, 1.01, self.infotext(), ha='center', va='bottom', transform=axes[-1].transAxes) axes[-1].set_xlim(0, t_data[-1] - t0_data) axes[-1].tick_params(labelbottom=False) axes.append(fig.add_axes((0.03, 0.35, 0.62, 0.3))) axes[-1].plot((t_data - t0_data), renorm(data), label=data_label, marker='v', ls='none') if use_calsource: axes[-1].plot((t_data - t0_data), renorm(data_src_interp), label='SRC (interp on data)', marker='^', ls='none') axes[-1].set_xlim(0, t_data[-1] - t0_data) axes[-1].legend() axes[-1].tick_params(labelbottom=False) axes.append(fig.add_axes((0.03, 0.03, 0.62, 0.3))) axes[-1].plot((t_data - t0_data), product, label='data $\\times$ source', marker='o', markersize=1, ls='none') axes[-1].legend() axes[-1].set_xlabel('date / seconds since %s' % t0_str) axes[-1].set_xlim(0, t_data[-1] - t0_data) # second column of plots # folded axes.append(fig.add_axes((0.70, 0.68, 0.28, 0.3))) axes[-1].plot((t_data - t0_data) % period, data, label='folded %s' % data_label, ls='none', marker='x') axes[-1].plot(folded_t_data, folded_data, label='folded and averaged %s' % data_label, ls='solid', linewidth=3, color='yellow') axes[-1].errorbar(folded_t_data, folded_data, yerr=folded_dataerr, ls='none', color='yellow', capthick=2, capsize=2) if use_calsource: axcal = axes[-1].twinx() axcal.plot((t_src - t0_data) % period, data_src, label='folded source', ls='none', marker='+') axcal.plot(folded_t_src, folded_src, label='folded and averaged source', ls='solid', linewidth=3, color='red') axes[-1].text(0.5, 1.0, 'folding period = %.6f seconds' % period, ha='center', va='bottom', transform=axes[-1].transAxes) axes[-1].legend() # demodulated axes.append(fig.add_axes((0.70, 0.35, 0.28, 0.3))) axes[-1].plot((t_data - t0_data), demodulated, label='demodulated', ls='solid') axes[-1].errorbar(binned_t, binned_demodulated, yerr=binned_stdev, label='binned', marker='x', markersize=3, ls='none', color='red', capsize=3, capthick=3) axes[-1].legend(loc='lower left') axes[-1].tick_params(labelbottom=False) # try: # expo = int(np.log10(retval['demodulated signal'])) # val = retval['demodulated signal']/10**expo # sig_str = '$%.6f\\times10^{%i}$' % (val,expo) # expo = int(np.log10(retval['demodulated signal error'])) # val = retval['demodulated signal error']/10**expo # err_str = '$%.2f\\times10^{%i}$' % (val,expo) # except: sig_str = '%.6f' % retval['demodulated signal'] err_str = '%.2f' % retval['demodulated signal error'] txt = 'demodulated signal = %s $\\pm$ %s' % (sig_str, err_str) if len(errlist) > 0: txt = '\n'.join([txt] + errlist) boxprops = dict(boxstyle='round', facecolor='wheat', alpha=0.5) axes[-1].text(0.5, 0.05, txt, ha='center', va='bottom', transform=axes[-1].transAxes, bbox=boxprops) xlim = axes[-1].axis()[:2] # number of points per bin axes.append(fig.add_axes((0.70, 0.03, 0.28, 0.3))) axes[-1].bar(binned_t, binned_npts, align='center') axes[-1].set_xlim(xlim) axes[-1].set_xlabel('period / seconds') axes[-1].set_ylabel('number of points per bin') pngname = 'demodulation_diagnostic_ASIC%i_%s_%s.png' % ( asic, TESstr.replace(' ', ''), t0_filenamestr) fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') retval['pngname'] = pngname retval['axes'] = axes if not xwin: retval['fig'] = None retval['axes'] = None plt.close(fig) return retval
def plot_timestamp_diagnostic_fig2(self, analysis=None, hk=None, zoomx=None, zoomy=None, asic=None, ax=None, fontsize=12): ''' make a plot of the timestamp diagnostic with the slope removed ''' if analysis is None: analysis = self.timestamp_diagnostic(hk=hk, asic=asic) if analysis is None: return ttl = '%s horizontal' % analysis['tstamps_title'] png_rootname = '%s_%s' % (ttl.lower().replace( ' ', '_'), self.obsdate.strftime('%Y%m%d-%H%M%S')) newplot = False if ax is None: fig = plt.figure(figsize=(16, 8)) figure_window_title(fig, ttl) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.08, 0.9, 0.8)) ax.text(0.50, 1.00, ttl, ha='center', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.99, 1.01, analysis['sample_period_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.01, 1.05, 'avg number of samples between events: %.2f' % analysis['samples_per_pps'].mean(), fontsize=fontsize, transform=ax.transAxes) if analysis['lost_txt'] is not None: ax.text(0.99, 1.1, analysis['lost_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) tstamps_horiz = analysis['tstamps'] - analysis['indextime'] compstamps_horiz = analysis['compstamps'] - analysis['indextime'] gps_horiz = analysis['gps'] - analysis['indextime'] pps_indexes = analysis['pps_indexes'] yminmax = (tstamps_horiz.min(), tstamps_horiz.max()) # measure the peak to peak between the first and last PPS tstamps_horiz_between_pps = tstamps_horiz[pps_indexes[0]:pps_indexes[-1]] peak2peak = tstamps_horiz_between_pps.max( ) - tstamps_horiz_between_pps.min() peak2peak_txt = 'peak to peak variation: %.2f msec' % (1000 * peak2peak) self.printmsg(peak2peak_txt, verbosity=2) ax.text(0.01, 1.0, peak2peak_txt, ha='left', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.plot([0, len(analysis['indextime'])], [0, 0], label='index time') ax.plot(tstamps_horiz, ls='none', marker='o', label='derived timestamps') ax.plot(compstamps_horiz, ls='none', marker='*', label='computer time') ax.plot(gps_horiz, ls='none', marker='x', label='GPS date') ax.set_ylabel('seconds', fontsize=fontsize) ax.set_xlabel('sample number', fontsize=fontsize) ax.set_ylim(yminmax) for idx in analysis['weird_idx']: ax.plot((idx, idx), yminmax, color='red', ls='dashed') ax.legend(fontsize=fontsize, loc='upper right') if newplot: pngname = '%s_full.png' % png_rootname fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') if zoomx is not None: ax.set_xlim(zoomx) if zoomy is not None: ax.set_ylim(zoomy) if zoomx is not None or zoomy is not None: if newplot: pngname = '%s_zoom.png' % png_rootname fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return
def plot_timestamp_diagnostic_fig1(self, analysis=None, hk=None, zoomx=None, zoomy=None, asic=None, ax=None, fontsize=12): ''' make a plot of the timestamp diagnostic ''' if analysis is None: analysis = self.timestamp_diagnostic(hk=hk, asic=asic) if analysis is None: return ttl = analysis['tstamps_title'] png_rootname = '%s_%s' % (ttl.lower().replace( ' ', '_'), self.obsdate.strftime('%Y%m%d-%H%M%S')) newplot = False if ax is None: newplot = True fig = plt.figure(figsize=(16, 8)) figure_window_title(fig, ttl) fig.suptitle(self.infotext(), fontsize=fontsize) ax = fig.add_axes((0.05, 0.08, 0.9, 0.8)) ax.text(0.50, 1.00, ttl, ha='center', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.99, 1.01, analysis['sample_period_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.01, 1.05, 'avg number of samples between events: %.2f' % analysis['samples_per_pps'].mean(), fontsize=fontsize, transform=ax.transAxes) if analysis['lost_txt'] is not None: ax.text(0.99, 1.1, analysis['lost_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) # mark problems with a vertical line yminmax = (analysis['compstamps'].min(), analysis['compstamps'].max()) pps_high = analysis['pps_high'] pps_indexes = analysis['pps_indexes'] ax.plot(analysis['indextime'], ls='none', marker='d', label='index time') ax.plot(analysis['tstamps'], ls='none', marker='o', label='derived timestamps') ax.plot(analysis['compstamps'], ls='none', marker='*', label='computer time') ax.plot(analysis['gps'], ls='none', marker='x', label='GPS date') ax.plot(pps_high, analysis['tstamps'][pps_high], ls='none', marker='^', label='pps high') ax.plot(pps_indexes, analysis['tstamps'][pps_indexes], ls='none', marker='o', label='pps indexes', markerfacecolor='none', markersize=16, color='black') for idx in analysis['weird_idx']: ax.plot((idx, idx), yminmax, color='red', ls='dashed') ax.set_ylabel('seconds', fontsize=fontsize) ax.set_ylim(yminmax) ax.set_xlabel('sample number', fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) ax.legend(fontsize=fontsize, loc='upper left') if newplot: pngname = '%s_full.png' % png_rootname fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') if zoomx is not None: ax.set_xlim(zoomx) ax.set_ylim(tstamps[zoomx[0]], tstamps[zoomx[1]]) if newplot: pngname = '%s_zoom.png' % png_rootname ax.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return
def plot_pps_nsamples(self, analysis=None, hk=None, zoomx=None, zoomy=None, asic=None, ax=None, fontsize=12): ''' make a plot of the number of samples per PPS ''' if analysis is None: analysis = self.timestamp_diagnostic(hk=hk, asic=asic) if analysis is None: if ax is None: return ax.text(0.5, 0.5, 'Insufficient PPS Data', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) return ax ttl = analysis['nsamples_title'] png_rootname = '%s_%s' % (ttl.lower().replace( ' ', '_'), self.obsdate.strftime('%Y%m%d-%H%M%S')) newplot = False if ax is None: newplot = True fig = plt.figure(figsize=(16, 8)) figure_window_title(fig, ttl) fig.suptitle(self.infotext(), fontsize=fontsize) ax = fig.add_axes((0.05, 0.08, 0.9, 0.8)) ax.text(0.01, 1.00, ttl, ha='left', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.99, 1.01, analysis['sample_period_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.text(0.01, 1.05, analysis['avg samples_per_pps txt'], fontsize=fontsize, transform=ax.transAxes) if analysis['lost_txt'] is not None: ax.text(0.99, 1.1, analysis['lost_txt'], ha='right', va='bottom', fontsize=fontsize, transform=ax.transAxes) if analysis['samples_per_pps'] is not None: ax.plot(analysis['samples_per_pps']) else: ax.text(0.5, 0.5, 'Insufficient PPS data', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.set_ylabel('Number of samples per PPS event', fontsize=fontsize) ax.set_xlabel('PPS event number', fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) for idx in analysis['weird_event']: ax.plot((idx, idx), (analysis['samples_per_pps'].min(), analysis['samples_per_pps'].max()), color='red', ls='dashed') if newplot: pngname = '%s_full.png' % png_rootname fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') if zoomx is not None: ax.set_xlim(zoomx) if newplot: pngname = '%s_zoom.png' % png_rootname fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return
def cal_sketch( pointing_ang=50, # QUBIC nominal pointing elevation angle D=9.0, # horizontal distance to QUBIC from the calibration source Hqubic=2.571, # vertical height of QUBIC window from the floor Hmirror=3.7, # vertical height of the centre of the flat mirror Hsource=4.0, # vertical height of the calibration source mirror_len=1.4, # length of the mirror cryo_width=1.436752, # width of the QUBIC cryostat cryo_height=1.542618, # height of the QUBIC cryostat win_offset=0.241979, # distance of the window from the centre axis of the cryostat xlength=13, # x extent of the drawing ylength=7, # y extent of the drawing transparency=True, figsize=(20, 16), fontsize=16): window_pos = [-D, Hqubic] cal_pos = [0, Hsource] pointing_ang_rad = radians(pointing_ang) fig = plt.figure(figsize=figsize) figure_window_title(fig, 'Calibration Source Sketch') ax = fig.add_axes((0.05, 0.05, 0.90, 0.90)) # QUBIC window marker x = window_pos[0] y = window_pos[1] ax.plot(x, y, color='red', linestyle='none', marker='D') txt = ' QUBIC position (%.2fm, %.2fm)' % (D, Hqubic) ax.text(x + 0.7, y, txt, color='black', fontsize=fontsize, ha='left', va='bottom') # cal source position marker x = cal_pos[0] y = cal_pos[1] ax.plot(x, y, color='green', linestyle='none', marker='D') txt = 'cal source height %.2fm ' % cal_pos[1] ax.text(x, y, txt, color='black', fontsize=fontsize, ha='right', va='bottom') # line showing QUBIC line of sight s = 2 hyp = s / sin(pointing_ang_rad) c = hyp * cos(pointing_ang_rad) pt1 = window_pos pt2 = [window_pos[0] - c, window_pos[1] + s] # intersection of cal mirror height with QUBIC line of sight # y = mx+b : line of sight # y = Hmirror m = (pt2[1] - pt1[1]) / (pt2[0] - pt1[0]) b = pt1[1] - m * pt1[0] xmirror = (Hmirror - b) / m mirror_pos = [xmirror, Hmirror] x = [window_pos[0], mirror_pos[0]] y = [window_pos[1], mirror_pos[1]] ax.plot(x, y, color='red', linewidth=3) # line from cal source to mirror x = [cal_pos[0], mirror_pos[0]] y = [cal_pos[1], mirror_pos[1]] ax.plot(x, y, color='green', linewidth=3) delta_s = y[0] - y[1] delta_c = -(x[1] - x[0]) delta_ang_rad = atan(delta_s / delta_c) delta_ang = degrees(delta_ang_rad) # reflection angle at the mirror reflection_ang = delta_ang + pointing_ang # mirror has normal at half the reflection angle from the QUBIC pointing angle mirror_norm_ang = pointing_ang - 0.5 * reflection_ang mirror_norm_ang_rad = radians(mirror_norm_ang) mirror_ang = mirror_norm_ang + 90 mirror_ang_rad = radians(mirror_ang) # a dashed line to show the mirror normal L = 1.0 dx = L * cos(mirror_norm_ang_rad) dy = L * sin(mirror_norm_ang_rad) x2 = mirror_pos[0] + dx y2 = mirror_pos[1] - dy x = [mirror_pos[0], x2] y = [mirror_pos[1], y2] ax.plot(x, y, color='black', linestyle='dashed', linewidth=1) txt = 'reflection angle at mirror = %.2f$^\circ$' % reflection_ang ax.text(x2, y2, txt, color='black', fontsize=fontsize, ha='left', va='center') # a line 1.4m long to show the mirror mirror_dx = 0.5 * mirror_len * cos(mirror_ang_rad) mirror_dy = 0.5 * mirror_len * sin(mirror_ang_rad) x = [mirror_pos[0] + mirror_dx, mirror_pos[0] - mirror_dx] y = [mirror_pos[1] - mirror_dy, mirror_pos[1] + mirror_dy] ax.plot(x, y, color='black', linewidth=3) # a dashed line showing the mirror horizontal extension x = [mirror_pos[0] - mirror_dx, mirror_pos[0] + mirror_dx] y = [mirror_pos[1] + mirror_dy, mirror_pos[1] + mirror_dy] ax.plot(x, y, color='black', linestyle='dashed') #ax.plot([x[1],x[1]],[y[1],0],color='black',linestyle='dashed') txt = 'mirror horizontal extension = %.2fm' % abs(2 * mirror_dx) txt = '%.2fm' % abs(2 * mirror_dx) ax.text(x[0], y[0], txt, ha='right', va='bottom', fontsize=fontsize, color='black') if mirror_ang > 90: txt = ' mirror angle = %.2f$^\circ$' % (mirror_ang - 90) else: txt = ' mirror angle = %.2f$^\circ$' % mirror_ang ax.text(x[0], y[0], txt, ha='left', va='top', fontsize=fontsize, color='black') # dashed line to show mirror position x = [mirror_pos[0], mirror_pos[0]] y = [mirror_pos[1], 0] ax.plot(x, y, color='black', linestyle='dashed') x = mirror_pos[0] y = mirror_pos[1] txt = ' flat mirror at\n%.2fm, %.2fm' % (abs(mirror_pos[0]), mirror_pos[1]) ax.text(x, y, txt, color='black', ha='right', va='bottom', fontsize=fontsize) # total optical path Dcal_mirror = sqrt((cal_pos[0] - mirror_pos[0])**2 + (cal_pos[1] - mirror_pos[1])**2) Dqubic_mirror = sqrt((mirror_pos[0] - window_pos[0])**2 + (mirror_pos[1] - window_pos[1])**2) optical_dist = Dcal_mirror + Dqubic_mirror # edge line x = [0, 0] y = [0, Hsource] #ax.plot(x,y,color='blue') # bottom line x = [1 - xlength, 1] y = [0, 0] ax.plot(x, y, color='black', linewidth=0.5, linestyle='dotted') # horizontal line at window height to compare to the drawing by Didier x = [1 - xlength, 1] y = [window_pos[1], window_pos[1]] ax.plot(x, y, color='black', linewidth=0.5, linestyle='dotted') # draw a box outline of the QUBIC cryostat top_centre = [ window_pos[0] - win_offset * sin(pointing_ang_rad), window_pos[1] - win_offset * cos(pointing_ang_rad) ] bot_centre = [ top_centre[0] + cryo_height * cos(pointing_ang_rad), top_centre[1] - cryo_height * sin(pointing_ang_rad) ] x = [bot_centre[0], top_centre[0]] y = [bot_centre[1], top_centre[1]] ax.plot(x, y, color='black', linewidth=1, linestyle='dotted') #ax.plot(top_centre[0],top_centre[1],marker='X',color='black') cryo_corner1 = (top_centre[0] - 0.5 * cryo_width * sin(pointing_ang_rad), top_centre[1] - 0.5 * cryo_width * cos(pointing_ang_rad)) cryo_corner2 = (top_centre[0] + 0.5 * cryo_width * sin(pointing_ang_rad), top_centre[1] + 0.5 * cryo_width * cos(pointing_ang_rad)) cryo_corner3 = (bot_centre[0] + 0.5 * cryo_width * sin(pointing_ang_rad), bot_centre[1] + 0.5 * cryo_width * cos(pointing_ang_rad)) cryo_corner4 = (bot_centre[0] - 0.5 * cryo_width * sin(pointing_ang_rad), bot_centre[1] - 0.5 * cryo_width * cos(pointing_ang_rad)) pts = [ cryo_corner1, cryo_corner2, cryo_corner3, cryo_corner4, cryo_corner1 ] x = [val[0] for val in pts] y = [val[1] for val in pts] ax.plot(x, y, color='black', linestyle='dotted') # plot extents ax.set_xlim(1 - xlength, 1) ax.set_ylim(-1, -1 + ylength) # equal aspect ratio #ax.set_aspect('equal', 'datalim') ax.set_aspect('equal') # labels txt = 'Total optical path length = %.2fm' % optical_dist ax.text(0.5, 0.1, txt, va='top', ha='center', fontsize=fontsize, transform=ax.transAxes) ax.text(0.5, 1.01, 'QUBIC Calibration Source layout', ha='center', va='bottom', fontsize=fontsize, transform=ax.transAxes) ax.tick_params(labelsize=fontsize) pngname = '/home/work/qubic/pix/calsource_setup/QUBIC_calibration_source_layout_sketch.png' fig.savefig(pngname, format='png', dpi=300, bbox_inches='tight', transparent=transparency) return
def plot_ASD(self, TES=None, timeline_index=0, save=True, ax_timeline=None, ax_asd=None, xwin=True, amin=None, amax=None, imin=None, imax=None, nbins=None, indmin=None, indmax=None): #MP ''' plot the Amplitude Spectral Density ''' if not self.exist_timeline_data(): print('ERROR! No timeline data!') return None ntimelines = self.ntimelines() if timeline_index >= ntimelines: print( 'ERROR! timeline index out of range. Enter an index between 0 and %i' % (ntimelines - 1)) return None if TES is None: print('Please enter a valid TES number, between 1 and %i' % self.NPIXELS) return None TES_idx = TES_index(TES) if nbins is None: nbins = 1 result = {} result['timeline_index'] = timeline_index result['TES'] = TES result['nbins'] = nbins timeline = self.timeline(TES, timeline_index) obsdate = self.tdata[timeline_index]['BEG-OBS'] result['obsdate'] = obsdate tinteg = self.tinteg if 'INT-TIME' in self.tdata[timeline_index].keys(): tinteg = self.tdata[timeline_index]['INT-TIME'] Tbath = self.tdata[timeline_index]['TES_TEMP'] result['Tbath'] = Tbath min_bias = self.min_bias if 'BIAS_MIN' in self.tdata[timeline_index].keys(): min_bias = self.tdata[timeline_index]['BIAS_MIN'] if min_bias is None: min_bias = self.min_bias result['min_bias'] = min_bias max_bias = self.max_bias if 'BIAS_MAX' in self.tdata[timeline_index].keys(): max_bias = self.tdata[timeline_index]['BIAS_MAX'] if max_bias is None: max_bias = self.max_bias result['max_bias'] = max_bias n_masked = self.n_masked() result['n_masked'] = n_masked current = self.ADU2I(timeline) # uA timeline_npts = len(timeline) result['timeline_npts'] = timeline_npts if indmin is None: indmin = 0 #MP if indmax is None: indmax = timeline_npts - 1 #MP # bin_npts=timeline_npts//nbins #MP bin_npts = (indmax - indmin + 1) // nbins #MP result['bin_npts'] = bin_npts if 'NPIXSAMP' in self.tdata[timeline_index].keys(): npixsampled = self.tdata[timeline_index]['NPIXSAMP'] else: npixsampled = self.NPIXELS sample_period = self.sample_period() time_axis = sample_period * np.arange(timeline_npts) ttl = 'Timeline and Amplitude Spectral Density' subttl = '\nQUBIC Array %s, ASIC %i, TES #%i, T$_\mathrm{bath}$=%.1f mK' % ( self.detector_name, self.asic, TES, 1000 * Tbath) pngname='QUBIC_Array-%s_ASIC%i_TES%03i_timeline%03i_AmplitudeSpectralDensity_%s.png'\ % (self.detector_name,self.asic,TES,timeline_index,obsdate.strftime('%Y%m%dT%H%M%SUTC')) pngname_fullpath = self.output_filename(pngname) result['pngname'] = pngname_fullpath # setup plot if we haven't done so already if xwin: plt.ion() else: plt.ioff() if ax_timeline is None or ax_asd is None: nrows = 1 ncols = 2 fig, axes = plt.subplots(nrows, ncols, sharex=False, sharey=False) ax_timeline = axes[0] ax_asd = axes[1] if xwin: figure_window_title(fig, ttl) fig.suptitle(ttl + subttl, fontsize=16) result['ax_timeline'] = ax_timeline result['ax_asd'] = ax_asd txt_x = 0.05 txt_y = 0.02 time_txt = obsdate.strftime('%Y-%m-%d %H:%M:%S UTC') time_label = '%s Tbath=%.1fmK' % (time_txt, Tbath * 1000) full_label='%s\nTbath=%.1fmK\nsample period=%.3fmsec\nintegration time=%.1fsec\nnbins=%i\nmin bias=%.2fV\nmax bias=%.2fV\nNpix sampled=%i'\ % (time_txt,1000*Tbath,1000*sample_period,tinteg,nbins,min_bias,max_bias,npixsampled) # sampling frequency fs = 1.0 / self.sample_period() PSD, freqs = mlab.psd(current[indmin:indmax], Fs=fs, NFFT=bin_npts, window=mlab.window_hanning, detrend='mean') ax_timeline.cla() ax_timeline.plot(time_axis[indmin:indmax], current[indmin:indmax]) ax_timeline.text(txt_x, txt_y, time_label, transform=ax_timeline.transAxes) ax_timeline.set_xlabel('time / seconds') ax_timeline.set_ylabel('Current / $\mu$A') if imin is None: imin = min(current) if imax is None: imax = max(current) ax_timeline.set_ylim((imin, imax)) if xwin: plt.pause(0.01) ASD = np.sqrt(PSD) # in uA ax_asd.cla() ax_asd.loglog(freqs, ASD) boxprops = dict(boxstyle='round', facecolor='wheat', alpha=0.5) ax_asd.text(txt_x, txt_y, full_label, transform=ax_asd.transAxes, ha='left', va='bottom', bbox=boxprops) ax_asd.set_xlabel('frequency') ax_asd.set_ylabel('Amplitude / $\mu$A') if amin is None: amin = min(ASD) if amax is None: amax = max(ASD) ax_asd.set_ylim((amin, amax)) if xwin: plt.pause(0.01) if save: plt.savefig(pngname_fullpath, format='png', dpi=100, bbox_inches='tight') if xwin: plt.show() else: plt.close(fig) return result
def plot_azel(self, ax=None, fontsize=12): ''' plot the azimuth and elevation positions ''' az = self.azimuth() el = self.elevation() t = self.timeaxis('platform') ttl = 'Platform position' pngname = self.assign_imagename(ttl.replace(' ', '_')) if ax is None: newplot = True ttl += '\n' + self.infotext() plt.ion() fig = plt.figure() figure_window_title(fig, '%s for dataset %s' % (ttl, self.dataset_name)) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.1, 0.9, 0.75)) else: newplot = False ax.text(0.5, 1.0, ttl, va='bottom', ha='center', fontsize=fontsize, transform=ax.transAxes) if t is None: ax.text(0.5, 0.5, 'No Platform information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) self.printmsg('No housekeeping information!', verbosity=2) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax tdate = [] for tstamp in t: tdate.append(dt.datetime.utcfromtimestamp(tstamp)) if az is not None: ax.plot(tdate, az, marker='D', markersize=0.2 * fontsize, ls='none', color='blue', label='Azimuth') if el is not None: ax.plot(tdate, el, marker='D', markersize=0.2 * fontsize, ls='none', color='red', label='Elevation') if az is None: ax.text(0.5, 0.5, 'No azimuth information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) if el is None: ax.text(0.5, 0.4, 'No elevation information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.set_ylabel('Position / Degrees', fontsize=fontsize) ax.set_xlabel('Date / UT', fontsize=fontsize) ax.legend(fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax
def plot_temperatures(self, ax, label, ttl, fontsize=12): ''' plot a collection of temperatures arguments: ax is the plot axis (possibly None) label is a dictionary of HK key with sensor label ttl is the main title of the plot ''' val = {} for sensor in label.keys(): val[sensor] = self.get_hk(sensor) t = self.get_hk(data='RaspberryDate', hk='EXTERN_HK') pngname = self.assign_imagename(ttl.replace(' ', '_')) if ax is None: newplot = True ttl += '\n' + self.infotext() plt.ion() fig = plt.figure() figure_window_title(fig, '%s for dataset %s' % (ttl, self.dataset_name)) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.1, 0.9, 0.8)) else: newplot = False ax.text(0.5, 1.0, ttl, va='bottom', ha='center', fontsize=fontsize, transform=ax.transAxes) if t is None: ax.text(0.5, 0.5, 'No Temperature Information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax tdate = [] # qubic-central was changed to UTC on 2020-02-27 if t[0] > float(qc_utc_date.strftime('%s.%f')): for tstamp in t: tdate.append(dt.datetime.utcfromtimestamp(tstamp)) else: for tstamp in t: tdate.append(dt.datetime.fromtimestamp(tstamp)) for key in val.keys(): if val[key] is not None: plt.plot(tdate, val[key], label=label[key], marker='D', markersize=0.2 * fontsize) ax.set_ylabel('Temperature / K', fontsize=fontsize) ax.set_xlabel('Date / UT', fontsize=fontsize) ax.legend(fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax
def plot_fits_layout(filename): ''' plot the QUBIC focal plane detector layout as found in the CalQubic_DetArray_...fits file ''' basename = os.path.basename(filename) ttl = 'FITS Layout for %s' % basename hdulist = fits.open(filename) hdu_centre = hdulist[1] hdu_vertices = hdulist[2] hdu_removed = hdulist[3] hdu_index = hdulist[4] hdu_quadrant = hdulist[5] xmax = np.nanmax(hdu_vertices.data[:, :, :, 0]) xmin = np.nanmin(hdu_vertices.data[:, :, :, 0]) xspan = xmax - xmin plt_xmin = xmin - 0.1 * xspan plt_xmax = xmax + 0.1 * xspan ymax = np.nanmax(hdu_vertices.data[:, :, :, 1]) ymin = np.nanmin(hdu_vertices.data[:, :, :, 1]) yspan = ymax - ymin plt_ymin = ymin - 0.1 * yspan plt_ymax = ymax + 0.1 * yspan fig = plt.figure(figsize=(20, 20)) figure_window_title(fig, basename) ax = fig.add_axes([0, 0, 1, 1]) ax.set_aspect('equal') ax.set_xlim([plt_xmin, plt_xmax]) ax.set_ylim([plt_ymin, plt_ymax]) ax.text(0.5, 1.0, ttl, va='top', ha='center', fontsize=20, transform=ax.transAxes) fp_idx = -1 for i in range(34): for j in range(34): #fp_idx += 1 fp_idx = 34 * i + j quadrant_idx = hdu_quadrant.data[i, j] if quadrant_idx > 3: continue removed = hdu_removed.data[i, j] if removed == 1: continue quadrant = quadrant_idx + 1 fpindex = hdu_index.data[i, j] x = hdu_centre.data[i, j, 0] y = hdu_centre.data[i, j, 1] xpts = np.append(hdu_vertices.data[i, j, :, 0], hdu_vertices.data[i, j, 0, 0]) ypts = np.append(hdu_vertices.data[i, j, :, 1], hdu_vertices.data[i, j, 0, 1]) plt.fill(xpts, ypts, color=quadrant_colour[quadrant_idx]) lbl = 'Q%i\n%04i\n%04i' % (quadrant, fpindex, fp_idx) ax.text(x, y, lbl, fontsize=6, color='white', ha='center', va='center') #ax.plot(x,y,ls='none',marker='s',markersize=10,color='black',fillstyle='none') return hdulist
def quicklook(self, TES=(54, 54), xwin=True, savedir=None, filename=None, figsize=(10.5, 14), dpi=100): ''' make a page with diagnostic info arguments: - TES : a list of TES to show as examples (one from ASIC1 and one form ASIC2) - xwin : plot to screen if True, otherwise, just make a file - savedir : directory name for the image file - filename : override the default filename - figsize : override the default figure size. Note that the intention is for quicklook to fit on A4 size - dpi : override the default dots per inch for the saved image file ''' if not self.exist_data(): return None ttl = 'Diagnostic for %s' % self.dataset_name #ttl += '\n'+self.infotext() if xwin: plt.ion() else: plt.close('all') plt.ioff() fig = plt.figure(figsize=figsize) scalesize = np.sqrt(np.sum(np.array(figsize)**2)) if xwin: figure_window_title(fig, '%s for dataset %s' % (ttl, self.dataset_name)) fig.suptitle(ttl, fontsize=10) fontsize = 0.3 * scalesize width = 0.37 height = 0.14 vspacing = 0.18 hspacing = 0.51 hpos1 = 0.07 hpos2 = hpos1 + hspacing vpos = 0.80 # calsource ax = fig.add_axes((hpos1, vpos, width, height)) self.plot_calsource(ax, fontsize=fontsize) # platform position ax = fig.add_axes((hpos2, vpos, width, height)) self.plot_azel(ax, fontsize=fontsize) vpos -= vspacing # 300mK temperatures ax = fig.add_axes((hpos1, vpos, width, height)) self.plot_300mKtemperatures(ax, fontsize=fontsize) # 1K temperatures ax = fig.add_axes((hpos2, vpos, width, height)) self.plot_1Ktemperatures(ax, fontsize=fontsize) vpos -= vspacing # horn switches activated ax = fig.add_axes((hpos1, vpos, width, height)) self.plot_switchstatus(ax, fontsize=fontsize) ax = fig.add_axes((hpos2, vpos, width, height)) self.plot_hwp(ax, fontsize=fontsize) vpos -= vspacing # PPS diagnostic ax = fig.add_axes((hpos1, vpos, width, height)) self.plot_pps_nsamples(hk='platform', ax=ax, fontsize=fontsize) ax = fig.add_axes((hpos2, vpos, width, height)) self.plot_pps_nsamples(hk='sci', asic=1, ax=ax, fontsize=fontsize) vpos -= vspacing # example timeline from ASIC 1 ax = fig.add_axes((hpos1, vpos, width, height)) f = self.plot_timeline(asic=1, TES=TES[0], ax=ax, fontsize=fontsize) if f is None: ax.text(0.5, 0.5, 'No Science Data for ASIC 1', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) # example timeline from ASIC 2 ax = fig.add_axes((hpos2, vpos, width, height)) f = self.plot_timeline(asic=2, TES=TES[1], ax=ax, fontsize=fontsize) if f is None: ax.text(0.5, 0.5, 'No Science Data for ASIC 2', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) # elapsed time delta = self.endobs - self.obsdate days = delta.days seconds = delta.seconds hours = seconds // 3600 remain_secs = seconds % 3600 mins = remain_secs // 60 secs = remain_secs % 60 usecs = delta.microseconds elapsed_time_txt = 'elapsed time: ' if days > 0: elapsed_time_txt += '%i days, ' % days elapsed_time_txt += '%i hours, %i minutes, %i.%06i seconds' % (hours, mins, secs, usecs) vpos -= 0.3 * vspacing # bottom of page to show elapsed time ax = fig.add_axes((hpos1, vpos, width + hspacing, 0.2 * height)) ax.text(0.5, 0.5, elapsed_time_txt, va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) if filename is None: imagename = 'QUBIC_quicklook_%s.png' % self.dataset_name else: imagename = filename if savedir is not None and os.path.isdir(savedir): image_fullname = '%s/%s' % (savedir, imagename) else: image_fullname = imagename fig.savefig(image_fullname, dpi=dpi, bbox_inches='tight') if not xwin: plt.close(fig) return image_fullname
def plot_hwp(self, ax=None, fontsize=12): ''' plot Half Wave Plate position ''' v = self.get_hk('HWP-Position') t = self.timeaxis('hwp') ttl = 'Half Wave Plate position' pngname = self.assign_imagename(ttl.replace(' ', '_')) if ax is None: newplot = True ttl += '\n' + self.infotext() plt.ion() fig = plt.figure() figure_window_title(fig, '%s for dataset %s' % (ttl, self.dataset_name)) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.1, 0.9, 0.75)) else: newplot = False ax.text(0.5, 1.0, ttl, va='bottom', ha='center', fontsize=fontsize, transform=ax.transAxes) if t is None: self.printmsg('No housekeeping information!', verbosity=2) ax.text(0.5, 0.5, 'No Half Wave Plate information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax tdate = [] for tstamp in t: tdate.append(dt.datetime.utcfromtimestamp(tstamp)) if v is not None: ax.plot(tdate, v, marker='D', markersize=0.2 * fontsize, ls='none', label='HWP Position') ax.set_ylim(0, 8) ax.set_ylabel('Position number', fontsize=fontsize) ax.set_xlabel('Date / UT', fontsize=fontsize) ax.legend(fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) if v is None or min(v) == 255: ax.text(0.5, 0.5, 'No Half Wave Plate information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax
def plot_calsource(self, ax=None, fontsize=12): ''' plot the calibration source ''' ttl = 'Calibration Source' pngname = self.assign_imagename('calsource') info = self.calsource_info() if info is None: ttl += ' NO INFORMATION' elif info['calsource']['status'] != 'ON': ttl += ' %s' % info['calsource']['status'] else: ttl += ' frequency=%.2fGHz' % info['calsource']['frequency'] if ax is None: newplot = True ttl = '%s %s' % (self.infotext(), ttl) plt.ion() fig = plt.figure() figure_window_title( fig, 'Calibration Source for dataset %s' % self.dataset_name) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.1, 0.9, 0.78)) else: newplot = False #ax.text(0.5,1.0,ttl,va='bottom',ha='center',fontsize=fontsize,transform=ax.transAxes) ax.set_ylabel('Calibration Source Power / arbitrary units', fontsize=fontsize) ax.set_xlabel('Date / UT', fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) ax.text(0.01, 1.0, self.calsource_infotext(), va='bottom', ha='left', fontsize=fontsize, transform=ax.transAxes) t, v = self.calsource() if t is None: ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) ax.text(0.5, 0.5, 'No Calsource Data', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) else: tdate = [] for tstamp in t: tdate.append(dt.datetime.utcfromtimestamp(tstamp)) ax.plot(tdate, v) if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax
t0 = min([el_t0, t_t0]) t0_date = dt.datetime.fromtimestamp(t0) # make plots plt.ion() for key in rec_names.keys(): idx_good = np.where(t_pts[key] != -1)[0] pngname = '%s_vertical-offset_temperature_%s.png' % ( key.upper(), t0_date.strftime('%Y%m%d')) ttl = 'Temperture during Synthetic Beam Mapping: %s' % t0_date.strftime( '%Y-%m-%d') t_label = '%s Temperature' % key.upper() fig = plt.figure(figsize=(16, 8)) figure_window_title(fig, 'Temperature vs Inclination') plt.title(ttl) date_pts = (t_date[idx_good] - t0) / 60 temp_pts = t_pts[key][idx_good] plt.plot(date_pts, temp_pts, ls='none', marker='*', color='green', label=t_label) t_ax = fig.axes[0] el_ax = t_ax.twinx()
def plot_fp(args): ''' plot curves and background colour on the QUBIC focal plane the argument is a dictionary with the curves and options valid keywords (n is the ASIC number): 'ASIC<n>' : the array of NPIXELS curves 'ASIC<n> x-axis' : x-axis (use this to plot bias curves) 'ASIC<n> bg' : the value to determine the background colour 'ASIC<n> good' : an array of bool for each TES (good or not) 'obsdate' : observation date (datetime object) 'title' : plot title 'subtitle' : plot subtitle 'lutmin' : min value for colour look up table 'lutmax' : max value for colour look up table 'pngname' : name of file for saving the plot 'nolabels' : if true, do not plot TES labels in each box 'quadrant' : quadrant in which to plot the quarter focal plane (default=3) ''' # initialize stuff pix_grid = assign_pix_grid() nrows = pix_grid.shape[0] ncols = pix_grid.shape[1] if 'figsize' in args.keys(): figsize = args['figsize'] else: figwidth = plt.rcParams['figure.figsize'][0] figsize = (figwidth, figwidth) fontsize = figsize[0] ttlfontsize = figsize[0] * 1.2 quadrant = 3 if 'quadrant' in args.keys(): quadrant = args['quadrant'] obsdate = None if 'obsdate' in args.keys(): obsdate = args['obsdate'] tes_grid = assign_tes_grid() if 'pngname' in args.keys(): pngname = args['pngname'] elif obsdate: pngname = 'QUBIC_focal_plane_%s.png' % obsdate.strftime( '%Y%m%dT%H%M%S') else: pngname = 'QUBIC_focal_plane.png' if 'title' in args.keys(): ttl = args['title'] else: ttl = 'QUBIC TES array' subttl = None if 'subtitle' in args.keys(): subttl = args['subtitle'] lutmin = 0.0 if 'lutmin' in args.keys(): lutmin = args['lutmin'] lutmax = 10.0 if 'lutmax' in args.keys(): lutmax = args['lutmax'] face_colours = {} face_colours['ASIC0'] = 'black' face_colours['ASIC1'] = 'white' face_colours['ASIC2'] = 'white' curve_colours = {} curve_colours['ASIC0'] = 'black' curve_colours['ASIC1'] = 'black' curve_colours['ASIC2'] = 'blue' label_boxprops = dict(boxstyle='round, pad=0.1', facecolor='white', alpha=1.0) label_colours = {} label_colours['ASIC0'] = 'black' label_colours['ASIC1'] = 'black' label_colours['ASIC2'] = 'blue' if 'nolabels' in args.keys() and args['nolabels']: print_labels = False else: print_labels = True plt.ion() fig, ax = plt.subplots(nrows, ncols, figsize=figsize) fig.text(0.5, 0.985, ttl, ha='center', fontsize=ttlfontsize) figure_window_title(fig, ttl) fig.suptitle(subttl, fontsize=ttlfontsize) for j in range(nrows): for i in range(ncols): if quadrant == 1: row = j col = i elif quadrant == 2: row = i col = j elif quadrant == 3: row = 16 - j col = 16 - i else: row = 16 - i col = 16 - j ax[row, col].get_xaxis().set_visible(False) ax[row, col].get_yaxis().set_visible(False) # the pixel identity associated with its physical location in the array TES = tes_grid[j, i].TES TES_str = '%03i' % TES asic = tes_grid[j, i].ASIC asic_str = '%i' % asic TES_idx = TES - 1 pix_label = TES_str asic_key = 'ASIC%s' % asic_str asicbg_key = '%s bg' % asic_key asicgood_key = '%s good' % asic_key xaxis_key = '%s x-axis' % asic_key face_colour = face_colours[asic_key] label_colour = label_colours[asic_key] curve_colour = curve_colours[asic_key] text_x = 0.5 text_y = 0.9 labelfontsize = ttlfontsize if asic_key in args.keys() and args[asic_key] is not None: if xaxis_key in args.keys() and args[xaxis_key] is not None: curve_x = args[xaxis_key][TES_idx] else: curve_x = range(args[asic_key].shape[1]) curve = args[asic_key][TES_idx] text_x = 0.5 text_y = 0.9 labelfontsize = 0.8 * fontsize if asicgood_key in args.keys( ) and not args[asicgood_key][TES_idx]: face_colour = 'black' label_colour = 'white' curve_colour = 'white' elif asicbg_key in args.keys(): if args[asicbg_key][TES_idx] is None: face_colour = 'white' else: face_colour = mylut(args[asicbg_key][TES_idx], lutmin, lutmax) ax[row, col].plot(curve_x, curve, color=curve_colour) #print('(%i,%i) : facecolour=%s, labelcolour=%s' % (row,col,face_colour,label_colour)) ax[row, col].set_facecolor(face_colour) label_boxprops['facecolor'] = face_colour if print_labels: ax[row, col].text(text_x, text_y, pix_label, va='top', ha='center', color=label_colour, fontsize=labelfontsize, bbox=label_boxprops, transform=ax[row, col].transAxes) plt.savefig(pngname, format='png', dpi=100, bbox_inches='tight') plt.show() return
def plot_timeline(self, TES, timeline_index=None, fit=False, ipeak0=None, ipeak1=None, plot_bias=True, xwin=True, timeaxis='pps', ax=None, fontsize=12): ''' plot the timeline ''' if not self.exist_timeline_data(): self.printmsg('ERROR! No timeline data.') return None if timeline_index is None: # by default, plot the first one. For QubicStudio files, there is only one timeline timeline_index = 0 ntimelines = self.ntimelines() if timeline_index >= ntimelines: self.printmsg('Please enter a timeline between 0 and %i' % (ntimelines - 1)) return None tdata = self.tdata[timeline_index] keys = tdata.keys() warning_str = '' if 'WARNING' in keys and tdata['WARNING']: warning_str = '\n'.join(tdata['WARNING']) if 'R_FEEDBK' in keys: self.Rfeedback = tdata['R_FEEDBK'] if 'NSAMPLES' in keys: self.nsamples = tdata['NSAMPLES'] if 'DATE-OBS' in keys: timeline_date = tdata['DATE-OBS'] else: timeline_date = self.obsdate if 'BEG-OBS' in keys: timeline_start = tdata['BEG-OBS'] else: timeline_start = timeline_date if 'BIAS_MIN' in keys: self.min_bias = tdata['BIAS_MIN'] if 'BIAS_MAX' in keys: self.max_bias = tdata['BIAS_MAX'] biasphase = self.bias_phase() ttl = str('QUBIC Timeline curve for TES#%3i (%s)' % (TES, timeline_start.strftime('%Y-%b-%d %H:%M UTC'))) if 'TES_TEMP' in keys: tempstr = '%.0f mK' % (1000 * tdata['TES_TEMP']) else: if self.temperature is None: tempstr = 'unknown' else: tempstr = str('%.0f mK' % (1000 * self.temperature)) fbstr = '' if 'R_FEEDBK' in keys: if tdata['R_HEATER'] == 1: onoff = 'ON' else: onoff = 'OFF' fbstr = ', Feedback Relay: %.0fk$\Omega$, Heater %s' % ( tdata['R_FEEDBK'] * 1e-3, onoff) subttl = str('Array %s, ASIC #%i, Pixel #%i, Temperature %s%s' % (self.detector_name, self.asic, tes2pix( TES, self.asic), tempstr, fbstr)) if xwin: plt.ion() else: plt.ioff() if ax is None: newplot = True fig = plt.figure(figsize=self.figsize) figure_window_title(fig, ttl) ax = plt.gca() else: newplot = False ax.set_xlabel('time / seconds', fontsize=fontsize) ax.set_ylabel('Current / $\mu$A', fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) if warning_str: boxprops = {} boxprops['alpha'] = 0.4 boxprops['color'] = 'red' boxprops['boxstyle'] = 'round' ax.text(0.5, 0.5, warning_str, ha='center', va='center', fontsize=2 * fontsize, transform=ax.transAxes, bbox=boxprops) TES_idx = TES_index(TES) timeline = self.timeline(TES, timeline_index) current = self.ADU2I(timeline) # uAmps timeline_npts = len(timeline) self.printmsg( 'DEBUG: calling timeline_timeaxis from plot_timeline() with axistype=%s' % timeaxis, verbosity=4) time_axis = self.timeline_timeaxis(timeline_index, axistype=timeaxis) fitparms = None if fit: fitparms = self.fit_timeline(TES, timeline_index, ipeak0, ipeak1) ipeak0 = 0 ipeak1 = timeline_npts - 1 peak0 = time_axis[ipeak0] peak1 = time_axis[ipeak1] if plot_bias: if self.timeline_conversion is None: self.timeline2adu(TES=TES, timeline_index=timeline_index, timeaxis=timeaxis) if self.timeline_conversion is None or self.min_bias is None or self.max_bias is None: plot_bias = False else: ipeak0 = self.timeline_conversion['ipeak0'] ipeak1 = self.timeline_conversion['ipeak1'] peak0 = self.timeline_conversion['peak0'] peak1 = self.timeline_conversion['peak1'] shift = self.timeline_conversion['shift'] if plot_bias: ysine = None if biasphase is not None: self.printmsg('DEBUG: taking ysine from QubicStudio FITS file', verbosity=4) ysine = self.timeline_vbias sinelabel = 'V$_\mathrm{bias}$ from QubicStudio FITS file' elif fitparms is None: self.printmsg('DEBUG: taking ysine from peak to peak', verbosity=4) bias_period = peak1 - peak0 amplitude = 0.5 * (self.max_bias - self.min_bias) offset = self.min_bias + amplitude sinelabel = 'sine curve period=%.2f seconds\npeaks determined from TES %i' % ( bias_period, self.timeline_conversion['TES']) ysine = offset + amplitude * np.sin( (time_axis - peak0) * 2 * np.pi / bias_period + 0.5 * np.pi + shift * 2 * np.pi) else: self.printmsg( 'DEBUG: taking ysine from timeline fit to sine curve', verbosity=4) bias_period = fitparms['period'] amplitude = fitparms['amplitude'] offset = fitparms['offset'] shift = fitparms['phaseshift'] if bias_period is not None and amplitude is not None: sinelabel = 'best fit sine curve: period=%.2f seconds, amplitude=%.2f $\mu$A' % ( bias_period, amplitude) ysine = self.model_timeline(time_axis, bias_period, shift, offset, amplitude) if ysine is None: plot_bias = False if newplot: fig.suptitle(ttl + '\n' + subttl, fontsize=fontsize) else: ax.text(0.5, 1.0, ttl + '\n' + subttl, va='bottom', ha='center', fontsize=fontsize, transform=ax.transAxes) curve1 = ax.plot(time_axis, current, label='I-V timeline', color='blue') #ymax=max([current[ipeak0],current[ipeak1]]) ymax = np.nanmax(current) if np.isnan(ymax): ymax = 1.0 ymin = np.nanmin(current) if np.isnan(ymin): ymin = 1.0 yrange = ymax - ymin if np.isnan(yrange) or yrange == 0: yrange = 0.1 yminmax = (ymin - 0.02 * yrange, ymax + 0.02 * yrange) ax.plot([peak0, peak0], yminmax, color='red') ax.plot([peak1, peak1], yminmax, color='red') ax.set_ylim(yminmax) if plot_bias: if fitparms is None: ax_bias = ax.twinx() ax_bias.set_ylabel('Bias / V', rotation=270, va='bottom', fontsize=fontsize) if self.min_bias == self.max_bias: ax_bias.set_ylim([self.min_bias - 1, self.max_bias + 1]) else: ax_bias.set_ylim([self.min_bias, self.max_bias]) ax_bias.tick_params(axis='both', labelsize=fontsize) curve2_ax = ax_bias else: curve2_ax = ax self.printmsg( 'DEBUG: plotting sine curve for bias: len(time_axis)=%i, len(ysine)=%i' % (len(time_axis), len(ysine)), verbosity=4) curve2 = curve2_ax.plot(time_axis, ysine, label=sinelabel, color='green') curves = curve1 + curve2 else: curves = curve1 labs = [l.get_label() for l in curves] ax.legend(curves, labs, loc=0, fontsize=fontsize) pngname = str('TES%03i_array-%s_ASIC%i_timeline_%s.png' % (TES, self.detector_name, self.asic, timeline_start.strftime('%Y%m%dT%H%M%SUTC'))) pngname_fullpath = self.output_filename(pngname) if newplot and isinstance(pngname_fullpath, str): plt.savefig(pngname_fullpath, format='png', dpi=100, bbox_inches='tight') if xwin: plt.show() else: plt.close('all') if fitparms: return fitparms return True
def plot_switchstatus(self, ax=None, fontsize=12): ''' plot which horn switches are closed ''' v1 = self.get_hk('switch1') v2 = self.get_hk('switch2') t = self.timeaxis('INTERN_HK') ttl = 'Closed Horn Switches' pngname = self.assign_imagename(ttl.replace(' ', '_')) if ax is None: newplot = True ttl += '\n' + self.infotext() plt.ion() fig = plt.figure() figure_window_title(fig, '%s for dataset %s' % (ttl, self.dataset_name)) fig.suptitle(ttl, fontsize=fontsize) ax = fig.add_axes((0.05, 0.1, 0.9, 0.8)) else: newplot = False ax.text(0.5, 1.0, ttl, va='bottom', ha='center', fontsize=fontsize, transform=ax.transAxes) if t is None or (v1 is None and v2 is None): ax.text(0.5, 0.5, 'No Horn Switch Information', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.get_yaxis().set_visible(False) ax.get_xaxis().set_visible(False) else: tdate = [] for tstamp in t: tdate.append(dt.datetime.utcfromtimestamp(tstamp)) if v1 is not None: ax.plot(tdate, v1, marker='D', markersize=0.2 * fontsize, ls='none', label='Switch 1 Closed') if v2 is not None: ax.plot(tdate, v2, marker='D', markersize=0.2 * fontsize, ls='none', label='Switch 2 Closed') if max(v1) == 0 and max(v2) == 0: ax.text(0.5, 0.5, 'All horns open', va='center', ha='center', fontsize=2 * fontsize, transform=ax.transAxes) ax.set_ylabel('Horn number', fontsize=fontsize) ax.set_xlabel('Date / UT', fontsize=fontsize) ax.tick_params(axis='both', labelsize=fontsize) ax.set_ylim((-1, 65)) ax.legend(fontsize=fontsize, loc='upper right') if newplot: fig.savefig(pngname, format='png', dpi=100, bbox_inches='tight') return ax