def tes2pix(TES, asic): global TES2PIX if TES2PIX is None: TES2PIX = assign_pix2tes() TES_idx = TES_index(TES) if TES_idx is None: return None if TES_idx == -1: return -1 PIX = TES2PIX[ASIC_index(asic), TES_idx] return PIX
def timeline(self,TES,timeline_index=0): ''' return the timeline for a given TES and timeline index ''' if not self.exist_timeline_data():return None ntimelines=self.ntimelines() if timeline_index>=ntimelines: self.printmsg('ERROR! timeline index out of range. Enter an index between 0 and %i' % (ntimelines-1)) return None TES_idx=TES_index(TES) if TES_idx is None:return None timeline=self.tdata[timeline_index]['TIMELINE'][TES_idx,:] return timeline
def fit_timeline(self,TES,timeline_index=None,ipeak0=None,ipeak1=None): ''' fit the timeline to a sine curve ''' # return a dictionary fit={} fit['TES']=TES fit['DET_NAME']=self.detector_name fit['ASIC']=self.asic if timeline_index is None:timeline_index=0 ntimelines=self.ntimelines() if timeline_index>=ntimelines: self.printmsg('Please enter a timeline between 0 and %i' % (ntimelines-1)) return None fit['timeline_index']=timeline_index fit['date']=self.tdata[timeline_index]['DATE-OBS'] fit['Tbath']=self.tdata[timeline_index]['TES_TEMP'] TES_idx=TES_index(TES) timeline=self.timeline(TES,timeline_index) current=self.ADU2I(timeline) timeline_npts=len(timeline) self.printmsg('DEBUG: calling timeline_timeaxis from fit_timeline()',verbosity=4) time_axis=self.timeline_timeaxis(timeline_index) # first guess; use the peak search algorithm biasmod = self.determine_bias_modulation(TES,timeline_index) if biasmod is None: self.printmsg('ERROR! Could not determine bias modulation.',verbosity=2) return None peak0 = biasmod['peak0'] peak1 = biasmod['peak1'] period = peak1-peak0 amplitude = 0.5*(max(current)-min(current)) offset = min(current)+amplitude phaseshift = peak0/period p0=[period,phaseshift,offset,amplitude] popt,pcov=curve_fit(self.model_timeline,time_axis,current,p0=p0) period,phaseshift,offset,amplitude=popt fit['period']=period fit['phaseshift']=phaseshift fit['offset']=offset fit['amplitude']=amplitude # this is in microAmps Vtes=self.Rshunt*( (self.max_bias*self.bias_factor)/self.Rbias - 1e-6*abs(amplitude) ) fit['R amplitude']=abs(Vtes/amplitude) return fit
def get_ASD(self, TES=1, tinteg=None, ntimelines=10, nbins=1): ''' get timeline data and plot the Amplitude Spectral Density timeline data is saved in FITS file, unless in monitor mode. in monitor mode, the plots will refresh indefinitely. exit with Ctrl-C ''' client = self.connect_QubicStudio() if client is None: return None TES_idx = TES_index(TES) monitor_mode = False if not isinstance(ntimelines, int) or ntimelines <= 0: monitor_mode = True save = not monitor_mode self.assign_integration_time(tinteg) self.assign_obsdate() # for noise measurements, we set the feedback resistance to 100kOhm self.set_Rfeedback(100) idx = 0 ax_timeline = None ax_asd = None while monitor_mode or idx < ntimelines: self.debugmsg('ASD monitoring loop count: %i' % idx) # read the bath temperature at each loop Tbath = self.oxford_read_bath_temperature() timeline = self.integrate_scientific_data( save=True) # have to save in memory for plotting afterwards self.debugmsg('ASD monitoring: ntimelines=%i' % self.ntimelines()) timeline_index = self.ntimelines() - 1 result = self.plot_ASD(TES, timeline_index, ax_timeline=ax_timeline, ax_asd=ax_asd, save=save, nbins=nbins) ax_asd = result['ax_asd'] ax_timeline = result['ax_timeline'] # if only monitoring, get rid of the one just plotted if monitor_mode: del (self.tdata[-1]) idx += 1 if not monitor_mode: self.write_fits() 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 determine_bias_modulation(self, TES, timeline_index=None, timeaxis='pps'): ''' determine the modulation of the bias voltage It should be close to the "bias_frequency" which is actually the period. ''' if not self.exist_timeline_data(): return None if not isinstance(timeline_index, int): timeline_index = 0 ntimelines = self.ntimelines() if timeline_index >= ntimelines: self.printmsg('Please enter a timeline between 0 and %i' % (ntimelines - 1)) return None retval = {} retval['TES'] = TES retval['timeline_index'] = timeline_index retval['timeaxis'] = timeaxis TES_idx = TES_index(TES) timeline = self.timeline(TES, timeline_index) timeline_npts = len(timeline) sample_period = self.sample_period(timeline_index) self.printmsg( 'DEBUG: calling timeline_timeaxis from determine_bias_modulation() with axistype=%s' % timeaxis, verbosity=4) time_axis = self.timeline_timeaxis(timeline_index, axistype=timeaxis) measured_sample_period = (time_axis[-1] - time_axis[0]) / (timeline_npts - 1) retval['measured_sample_period'] = measured_sample_period # use the bias_phase if it exists bias_phase = self.bias_phase() if bias_phase is not None: self.printmsg('getting bias variation from the saved data', verbosity=2) imin = np.argmin(bias_phase) imax = np.argmax(bias_phase) iperiod = 2 * abs(imax - imin) if imin == imax: imin = 0 imax = timeline_npts - 1 iperiod = imax - imin ipeak0 = min([imin, imax]) ipeak1 = ipeak0 + iperiod peak0 = time_axis[ipeak0] if ipeak1 >= timeline_npts: peak1 = peak0 + iperiod * measured_sample_period else: peak1 = time_axis[ipeak1] self.bias_period = peak1 - peak0 retval['ipeak0'] = ipeak0 retval['ipeak1'] = ipeak1 retval['peak0'] = peak0 retval['peak1'] = peak1 return retval # the so-called frequency of the bias modulation is, in fact, the period # In QubicStudio the selection of bias_frequency=99 changes the significance from frequency to period (confusing) if self.bias_frequency is None: period_firstguess = 92. # based on experience else: period_firstguess = self.bias_frequency bias_period_npts = int(period_firstguess / sample_period) self.debugmsg('period npts = %i' % bias_period_npts) # skip the first few seconds which are often noisy skip = int(3.0 / sample_period) self.debugmsg( 'looking for peaks in I-V timeline. Skipping the first %i points.' % skip) peak0_range = (skip, skip + bias_period_npts) peak1_range_end = skip + 2 * bias_period_npts if peak1_range_end >= timeline_npts: peak1_range_end = timeline_npts - 1 peak1_range = (skip + bias_period_npts, peak1_range_end) # try to find the peaks, otherwise return ipeak0=0, ipeak1=timeline_npts-1 try: ipeak0 = np.argmax(timeline[peak0_range[0]:peak0_range[1]]) ipeak0 += peak0_range[0] except: ipeak0 = 0 peak0 = time_axis[ipeak0] try: ipeak1 = np.argmax(timeline[peak1_range[0]:peak1_range[1]]) ipeak1 += peak1_range[0] except: ipeak1 = timeline_npts - 1 peak1 = time_axis[ipeak1] self.bias_period = peak1 - peak0 retval['ipeak0'] = ipeak0 retval['ipeak1'] = ipeak1 retval['peak0'] = peak0 retval['peak1'] = peak1 return retval
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 get_iv_data(self, replay=False, TES=None, monitor=False): ''' get IV data and make a running plot optionally, replay saved data. you can monitor the progress of a given TES by the keyword TES=<number> setting monitor=True will monitor *all* the TES, but this slows everything down enormously! Not recommended!! ''' client = self.connect_QubicStudio() if client is None: return None monitor_iv = False if isinstance(TES, int): monitor_TES_index = TES_index(TES) monitor_iv = True if replay: if not isinstance(self.adu, np.ndarray): print('Please read an I-V data file, or run a new measurement!') return None if not isinstance(self.vbias, np.ndarray): print('There appears to be I-V data, but no Vbias info.') print( 'Please run make_Vbias() with the correct max and min values') return None adu = self.adu else: client = self.connect_QubicStudio() if client is None: return None self.assign_obsdate(dt.datetime.utcnow()) if not isinstance(self.vbias, np.ndarray): vbias = make_Vbias() nbias = len(self.vbias) adu = np.empty((self.NPIXELS, nbias)) self.oxford_read_bath_temperature() vbias = self.vbias nbias = len(self.vbias) # figavg=self.setup_plot_Vavg() if monitor_iv: figiv, axiv = self.setup_plot_iv(TES) if monitor: nrows = 16 ncols = 8 figmulti, axmulti = self.setup_plot_iv_multi() for j in range(nbias): self.debugmsg("Vbias=%gV " % vbias[j]) if not replay: self.set_VoffsetTES(vbias[j], 0.0) self.wait_a_bit() Vavg = self.get_mean() adu[:, j] = Vavg self.oxford_read_bath_temperature() else: Vavg = adu[:, j] # print ("a sample of V averages : %g %g %g " %(Vavg[0], Vavg[43], Vavg[73]) ) # plt.figure(figavg.number) # self.plot_Vavg(Vavg,vbias[j]) if monitor_iv: plt.figure(figiv.number) I_tes = adu[monitor_TES_index, 0:j + 1] Iadjusted = self.ADU2I(I_tes) self.draw_iv(Iadjusted, axis=axiv) if monitor: # monitor all the I-V curves: Warning! Extremely slow!!! TES_idx = 0 for row in range(nrows): for col in range(ncols): axmulti[row, col].get_xaxis().set_visible(False) axmulti[row, col].get_yaxis().set_visible(False) Iadjusted = self.ADU2I(self.adu[TES_idx, 0:j + 1]) self.draw_iv(Iadjusted, colour='blue', axis=axmulti[row, col]) text_y = min(Iadjusted) axmulti[row, col].text(max(self.vbias), text_y, str('%i' % (TES_idx + 1)), va='bottom', ha='right', color='black') TES_idx += 1 # plt.show() self.endobs = dt.datetime.utcnow() self.assign_ADU(adu) if not replay: self.write_fits() return adu