def plot_flags(timestamps, freqs, flags): """timestamps is an array of unix timstamps freqs is an array of frequencys in MHz flags is a 2D array of boolean with [time,freq] it plots the percentage of the data flagged as a function of time vs frequency """ fig = plt.figure(figsize=(10, 5)) # print((timestamps[:] - timestamps[0]).shape, (freqs).shape, ((flags).T).shape) plt.pcolormesh(timestamps[:] - timestamps[0], freqs, (flags).T, rasterized=True) plt.title('Flags in "quiet" part of the band') plt.xlabel('Time (s), since %s' % (katpoint.Timestamp(timestamps[0]).local(), )) plt.ylabel('Frequency/[MHz]') plt.xlim(0, timestamps[-1] - timestamps[0]) plt.ylim(freqs[0], freqs[-1]) plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) return fig
def plot_nd(freq, Tdiode, nd_temp, ant='', file_base=''): fig = plt.figure(figsize=(20, 5)) pols = ['v', 'h'] for p, pol in enumerate(pols): fig.add_subplot(1, 2, p + 1) # ax = plt.gca() ax.text(0.95, 0.01, git_info(), horizontalalignment='right', fontsize=10, transform=ax.transAxes) plt.title('%s Coupler Diode: %s pol: %s' % (ant, str(pol).upper(), file_base)) plt.ylim(0, 50) plt.ylabel('$T_{ND}$ [K]') #plt.xlim(900,1670) plt.xlabel('f [MHz]') #plt.ylabel(ant) plt.axhspan(14, 35, facecolor='g', alpha=0.5) plt.plot(freq / 1e6, Tdiode[pol], 'b.', label='Measurement: Y-method') plt.plot(freq / 1e6, nd_temp[pol], 'k.', label='Model: EMSS') plt.grid() plt.legend() return fig
def plot_raw_gain(gain_ts,phase=True,title=""): sub_title = "Amplitude" unit_str = "" unit = 1.0 if phase : unit = 180./np.pi unit_str = "(deg)" sub_title = "phase" pltobj2 = plt.figure(figsize=[11,11]) plt.suptitle(title) plt.subplots_adjust(bottom=0.15, hspace=0.35, top=0.95) plt.subplot(111) plt.title('Raw %s for %s'%(sub_title,pol)) (gain_ts* unit).plot(label='Raw %s'%(sub_title)) plt.legend(loc='best') plt.ylabel('%s %s'%(sub_title,unit_str)) plt.xlabel('Date/Time') plt.legend(loc='best') plt.figtext(0.89, 0.05, git_info(), horizontalalignment='right',fontsize=10) #xposition = [pd.to_datetime('2010-01-01'), pd.to_datetime('2015-12-31')] #for xc in xposition: # ax.axvline(x=xc, color='k', linestyle='-') return pltobj2
def plot_flagtype(flag_dat, labels): """flag_dat is an array of length labels of percentages. lables is a list of str corresponting to the bits in flags""" fig = plt.figure(figsize=(10, 5)) plt.xticks(list(range(len(labels))), labels, rotation=38) plt.ylabel('Percentage Flagged') plt.title('Flag Types ') plt.plot(flag_dat, '*', rasterized=True) plt.grid() plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) return fig
def plot_flag_data(label, spectrum, flagfrac, freqs, pdf, mask=None): """ Produce a plot of the average spectrum in H and V after flagging and attach it to the pdf output. Also show fraction of times flagged per channel. """ from katsdpscripts import git_info repo_info = git_info() # Set up the figure fig = plt.figure(figsize=(11.7, 8.3)) # Plot the spectrum ax1 = fig.add_subplot(211) ax1.text(0.01, 0.90, repo_info, horizontalalignment='left', fontsize=10, transform=ax1.transAxes) ax1.set_title(label) plt.plot(freqs, spectrum, linewidth=.5) ticklabels = ax1.get_xticklabels() plt.setp(ticklabels, visible=False) ticklabels = ax1.get_yticklabels() plt.setp(ticklabels, visible=False) plt.xlim((min(freqs), max(freqs))) plt.ylabel('Mean amplitude\n(arbitrary units)') # Plot the flags occupancies ax = fig.add_subplot(212, sharex=ax1) plt.plot(freqs, flagfrac, 'r-', linewidth=.5) plt.ylim((0., 1.)) plt.axhline(0.8, color='red', linestyle='dashed', linewidth=.5) plt.xlim((min(freqs), max(freqs))) minorLocator = ticker.MultipleLocator(10e6) plt.ylabel('Fraction flagged') ticklabels = ax.get_yticklabels() # Convert ticks to MHZ ticks = ticker.FuncFormatter(lambda x, pos: '{:4.0f}'.format(x / 1.e6)) ax.xaxis.set_major_formatter(ticks) ax.xaxis.set_minor_locator(minorLocator) plt.xlabel('Frequency (MHz)') plt.tight_layout() fig.subplots_adjust(hspace=0) pdf.savefig(fig) plt.close('all')
def plot_amp_freq(channel_freqs, a1234, title=''): """ channel_freqs is an array of channel frequencys in Hz a1234 is the closure quantity """ fig = plt.figure(figsize=(20, 10)) plt.title(title) plt.plot(channel_freqs / 1e6, a1234) plt.grid(True) plt.ylabel('Mean Amplitude Closure ') plt.xlabel('Frequency (MHz)') plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) return fig
def plot_flagtype(flag_dat, labels): """flag_dat is an array of int8 lables is a list of str corresponting to the bits in flags""" fig = plt.figure(figsize=(10, 5)) flag_dat = flag_dat.flatten() plt.xticks(range(len(labels)), labels, rotation='vertical') plt.ylabel('Percentage Flagged') plt.title('Flag Types (%i samples)' % (np.shape(flag_dat)[0])) plt.plot( np.unpackbits(flag_dat[:, np.newaxis], axis=1).sum(axis=0) / np.float(np.shape(labels)[0]) * 100, '*') plt.grid() plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) return fig
def plot_rolingavg(gain_val,peakmax,peakmin,peak,std,dtrend_std,windowtime=30,phase=True,title=""): pltobj = plt.figure(figsize=[11,20]) sub_title = "Amplitude" unit_str = "" unit = 1.0 if phase : unit = 180./np.pi unit_str = "(deg)" sub_title = "phase" plt.suptitle(title) plt.figtext(0.89, 0.05, git_info(), horizontalalignment='right',fontsize=10) plt.subplots_adjust(bottom=0.15, hspace=0.35, top=0.95) plt.subplot(311) plt.title('%ss for %s'%(sub_title,pol)) (gain_val* unit).plot(label='%s( - rolling mean)'%(sub_title)) (peakmax * unit).plot(label='rolling max') (peakmin * unit).plot(label='rolling min') plt.legend(loc='best') plt.ylabel('Gain %s %s'%(sub_title,unit_str)) ax2 = plt.subplot(312) plt.title('Peak to peak variation of %s, %i Second sliding Window'%(pol,windowtime,)) (peak* unit).plot(color='blue') if phase : ax2.axhline(13,ls='--', color='red') else: ax2.axhline(np.percentile(std*unit,95),ls='--', color='red') #plt.legend(loc='best') plt.ylabel('Variation %s'%(unit_str)) ax3 = plt.subplot(313) plt.title('Detrended Std of %s, %i Second sliding Window'%(pol,windowtime,)) (std* unit).plot(color='blue',label='Std') (dtrend_std* unit).plot(color='green',label='Detrended Std') if phase : ax3.axhline(2.2,ls='--', color='red') else: ax3.axhline(np.percentile(std*unit,95),ls='--', color='red') plt.legend(loc='best') plt.ylabel('Variation %s'%(unit_str)) plt.xlabel('Date/Time') return pltobj
def plot_ts(h5, on_ts=None): import scape fig = plt.figure(figsize=(20, 5)) a = h5.ants[0] nd = scape.gaincal.NoiseDiodeModel(freq=[856, 1712], temp=[20, 20]) d = scape.DataSet(h5, nd_h_model=nd, nd_v_model=nd) scape.plot_xyz(d, 'time', 'amp', label='Average of the data') if on_ts is not None: on = on_ts else: on = h5.sensor['Antennas/' + a.name + '/nd_coupler'] ts = h5.timestamps - h5.timestamps[0] plt.plot(ts, np.array(on).astype(float) * 4000, 'g', label='katdal ND sensor') plt.title("Timeseries for antenna %s - %s" % (a.name, git_info())) plt.legend() return fig
def plot_activity(scan_types, activity_count): """scan_type is a set consisiting of scans :['track', 'stop', 'slew'] and activity_count is an array activities perfomed by each antenna.""" fig = plt.figure(figsize=(12, 2)) for s, st in enumerate(scan_types): plt.plot(np.arange(len(data.ants)), activity_count[s, :], "o", label=st) plt.xticks(np.arange(len(data.ants)), [ant.name for ant in data.ants]) plt.legend(loc=5) plt.grid() plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) plt.xlim(-1, len(data.ants) + 2) plt.title('Number of dumps in each scan state') return fig
def plot_phase_freq(channel_freqs, a123, title=''): """ channel_freqs is an array of channel frequencys in Hz a123 is the closure quantity in radians """ fig = plt.figure(figsize=(20, 10)) plt.title(title) plt.plot(channel_freqs / 1e6, np.degrees(a123)) #plt.ylim(-5,5) plt.ylim(np.degrees(np.nanpercentile(a123, 2)), np.degrees(np.nanpercentile(a123, 98))) plt.grid(True) plt.ylabel('Mean Phase Closure angle(degrees)') plt.xlabel('Frequency (MHz)') plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) return fig
def metrics(model,az,el,measured_delta_az, measured_delta_el ,std_delta_az ,std_delta_el,time_stamps): """Determine new residuals and sky RMS from pointing model.""" model_delta_az, model_delta_el = model.offset(az, el) residual_az = measured_delta_az - model_delta_az residual_el = measured_delta_el - model_delta_el residual_xel = residual_az * np.cos(el) abs_sky_error = rad2deg(np.sqrt(residual_xel ** 2 + residual_el ** 2)) offset_az_ts = pandas.Series(rad2deg(residual_xel), pandas.to_datetime(time_stamps, unit='s'))#.asfreq(freq='1s') offset_el_ts = pandas.Series(rad2deg(residual_el), pandas.to_datetime(time_stamps, unit='s'))#.asfreq(freq='1s') offset_total_ts = pandas.Series( abs_sky_error, pandas.to_datetime(time_stamps, unit='s'))#.asfreq(freq='1s') ###### On the calculation of all-sky RMS ##### # Assume the el and cross-el errors have zero mean, are distributed normally, and are uncorrelated # They are therefore described by a 2-dimensional circular Gaussian pdf with zero mean and *per-component* # standard deviation of sigma # The absolute sky error (== Euclidean length of 2-dim error vector) then has a Rayleigh distribution # The RMS sky error has a mean value of sqrt(2) * sigma, since each squared error term is the sum of # two squared Gaussian random values, each with an expected value of sigma^2. sky_rms = np.sqrt(np.mean(abs_sky_error ** 2)) # A more robust estimate of the RMS sky error is obtained via the median of the Rayleigh distribution, # which is sigma * sqrt(log(4)) -> convert this to the RMS sky error = sqrt(2) * sigma robust_sky_rms = np.median(abs_sky_error) * np.sqrt(2. / np.log(4.)) # The chi^2 value is what is actually optimised by the least-squares fitter (evaluated on the training set) #chi2 = np.sum(((residual_xel / std_delta_az) ** 2 + (residual_el / std_delta_el) ** 2)) text = 'All sky RMS = %.3f\" (robust %.3f\") ' % (sky_rms*3600, robust_sky_rms*3600) fig = plt.figure(figsize=(10,5)) #change_total = np.sqrt(change_el**2 + change_az**2) #(offset_el_ts*3600.).plot(label='Elevation',legend=True,grid=True,style='*') #(offset_az_ts*3600.).plot(label='Azimuth',legend=True,grid=True,style='*') (offset_total_ts*3600.).plot(label='Total pointing Error',legend=True,grid=True,style='*') dataset_str = ' ,'.join(np.unique(offsetdata['dataset']).tolist() ) #target_str = ' ,'.join(np.unique(offsetdata['target']).tolist() ) plt.title("Offset for Antenna:%s Dataset:%s \n %s " %(ant.name,dataset_str ,text),fontsize=10) plt.ylabel('Offset (arc-seconds)') plt.xlabel('Time (UTC)',fontsize=8) plt.figtext(0.89, 0.18,git_info(), horizontalalignment='right',fontsize=10) return fig
def plot_Tsys_eta_A(freq, Tsys, eta_A, TAc, Ku=False, Tsys_std=None, ant='', file_base='.'): fig = plt.figure(figsize=(20, 5)) pols = ['v', 'h'] for p, pol in enumerate(pols): fig.add_subplot(1, 2, p + 1) ax = plt.gca() ax.text(0.95, 0.01, git_info(), horizontalalignment='right', fontsize=10, transform=ax.transAxes) plt.title('%s $T_{sys}/eta_{A}$: %s pol: %s' % (ant, str(pol).upper(), file_base)) plt.ylabel("$T_{sys}/eta_{A}$ [K]") plt.xlabel('f [MHz]') #if p == ant_num * 2 -1: plt.ylabel(ant) if Tsys_std[pol] is not None: plt.errorbar(freq / 1e6, Tsys[pol], Tsys_std[pol], color='b', linestyle='.', label='Measurement') plt.plot(freq / 1e6, Tsys[pol] / eta_A[pol], 'b.', label='Measurement: Y-method') if not (Ku): plt.plot(freq / 1e6, TAc[pol] / eta_A[pol], 'c.', label='Measurement: ND calibration') plt.axhline(np.mean(Tsys[pol] / eta_A[pol]), linewidth=2, color='k', label='Mean: Y-method') if freq.min() < 2090e6: D = 13.5 Ag = np.pi * (D / 2)**2 # Antenna geometric area spec_Tsys_eta = np.zeros_like(freq) plt.ylim(15, 50) #plt.xlim(900,1670) spec_Tsys_eta[freq < 1420e6] = 42 # [R.T.P095] == 220 spec_Tsys_eta[freq >= 1420e6] = 46 # [R.T.P.096] == 200 plt.plot(freq / 1e6, spec_Tsys_eta, 'r', linewidth=2, label='PDR Spec') plt.plot(freq / 1e6, np.interp(freq / 1e6, [900, 1670], [(64 * Ag) / 275.0, (64 * Ag) / 410.0]), 'g', linewidth=2, label="275-410 m^2/K at Receivers CDR") plt.grid() plt.legend(loc=2, fontsize=12) return fig
def plot_waterfall_subsample(visdata, flagdata, freqs=None, times=None, label='', resolution=150, output=None): """ Make a waterfall plot from visdata with flags overplotted. """ from datetime import datetime as dt import matplotlib.dates as mdates from katsdpscripts import git_info repo_info = git_info() fig = plt.figure(figsize=(8.3, 11.7)) ax = plt.subplot(111) ax.set_title(label) ax.text(0.01, 0.02, repo_info, horizontalalignment='left', fontsize=10, transform=ax.transAxes) display_limits = ax.get_window_extent() if freqs is None: freqs = list(range(0, visdata.shape[1])) # 300dpi, and one pixel per desired data-point in pixels at 300dpi display_width = display_limits.width * resolution / 72. display_height = display_limits.height * resolution / 72. x_step = max(int(visdata.shape[1] / display_width), 1) y_step = max(int(visdata.shape[0] / display_height), 1) x_slice = slice(0, -1, x_step) y_slice = slice(0, -1, y_step) data = np.log10(np.abs(visdata[y_slice, x_slice])) flags = flagdata[y_slice, x_slice] plotflags = np.zeros(flags.shape[0:2] + (4, )) plotflags[:, :, 0] = 1.0 plotflags[:, :, 3] = flags if times is None: starttime = 0 endtime = visdata.shape[0] else: starttime = mdates.date2num(dt.fromtimestamp(times[0])) endtime = mdates.date2num(dt.fromtimestamp(times[-1])) kwargs = { 'aspect': 'auto', 'origin': 'lower', 'interpolation': 'none', 'extent': (freqs[0], freqs[-1], starttime, endtime) } image = ax.imshow(data, **kwargs) image.set_cmap('Greys') ax.imshow(plotflags, alpha=0.5, **kwargs) ampsort = np.sort(data[~flags], axis=None) arrayremove = int(len(ampsort) * (1.0 - 0.80) / 2.0) lowcut, highcut = ampsort[arrayremove], ampsort[-(arrayremove + 1)] image.norm.vmin = lowcut image.norm.vmax = highcut plt.xlim((min(freqs), max(freqs))) if times is not None: ax.yaxis_date() plt.gca().yaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) plt.ylabel('Time (SAST)') else: plt.ylabel('Time (Dumps)') # Convert ticks to MHZ ticks = ticker.FuncFormatter(lambda x, pos: '{:4.0f}'.format(x / 1.e6)) ax.xaxis.set_major_formatter(ticks) plt.xlabel('Frequency (MHz)') if output: output.savefig(fig) else: plt.show() plt.close('all')
else: target = opts.target h5data.select(targets=target, scans='track') start_time = Time(h5data.timestamps[0], format='unix') end_time = Time(h5data.timestamps[-1], format='unix') h5name = h5data.name.split('/')[-1] output_filename = 'Gain_flatness_' + baseline + '_' + h5name pdf = PdfPages(output_filename + '.pdf') fig = plt.figure(figsize=[10, 10]) plt.suptitle(h5name + ', ' + start_time.iso + ' - ' + end_time.iso) nplots = len(opts.polarisation.split(',')) for i, pol in enumerate(opts.polarisation.split(',')): visdata, weightdata, h5data = \ sb.read_and_select_file(h5data, bline=baseline, target=target, channels=opts.freq_chans, polarisation=pol, flags_file=opts.flags_file) mean_spec = 10 * np.log10(visdata.mean(axis=0)) mean_level = mean_spec.mean() ax = plt.subplot(nplots, 1, i + 1) plt.title('Gain flatness, ' + baseline + pol) plt.plot(h5data.channel_freqs / 1e6, mean_spec) plt.ylabel('Power (dB)') plt.xlabel('Frequency (MHz)') plt.axhline(mean_level, color='r', alpha=0.5) plt.axhline(mean_level + 5, color='r') plt.axhline(mean_level - 5, color='r') plt.xlim(h5data.channel_freqs[0] / 1e6, h5data.channel_freqs[-1] / 1e6) plt.grid() plt.figtext(0.5, 0.95, git_info(), horizontalalignment='center', fontsize=10) fig.savefig(pdf, format='pdf') pdf.close()
def read_and_plot_data(filename, output_dir='.', pdf=True, Ku=False, verbose=False, error_bars=False, target='off1', write_nd=False, rfi_mask='/var/kat/katsdpscripts/RTS/rfi_mask.pickle', **kwargs): print('inside', kwargs) file_base = filename.split('/')[-1].split('.')[0] nice_filename = file_base + '_T_sys_T_nd' # Set up logging: logging everything (DEBUG & above), both to console and file logger = logging.root logger.setLevel(logging.DEBUG) fh = logging.FileHandler(nice_filename + '.log', 'w') fh.setLevel(logging.DEBUG) fh.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) logger.addHandler(fh) logger.info('Beginning data processing with:\n%s' % git_info('standard')) if Ku: logger.debug("Using Ku band ... unsetting L band RFI flags") h5 = katfile.open(filename, centre_freq=12500.5e6, **kwargs) length = h5.shape[1] # Don't subtract half a channel width as channel 0 is centred on 0 Hz in baseband rfi_static_flags = np.tile(True, length) else: h5 = katfile.open(filename, **kwargs) length = h5.shape[1] pickle_file = open(rfi_mask, mode='rb') rfi_static_flags = pickle.load(pickle_file) pickle_file.close() # Now find the edges of the mask rfi_freqs_width = (856000000.0 / rfi_static_flags.shape[0]) rfi_freqs_min = 856000000.0 - rfi_freqs_width / 2. # True Start of bin rfi_freqs_max = rfi_freqs_min * 2 - rfi_freqs_width / 2. # Middle of Max-1 bin rfi_freqs = np.linspace(rfi_freqs_min, rfi_freqs_max, rfi_static_flags.shape[0]) rfi_function = get_fit( np.r_[rfi_freqs, rfi_freqs + rfi_freqs_width * 0.9999], np.r_[rfi_static_flags, rfi_static_flags]) rfi_static_flags = rfi_function(h5.channel_freqs) #print ( (rfi_static_flags_new-rfi_static_flags)**2).sum() if verbose: logger.debug(h5.__str__()) edge = np.tile(True, length) #edge[slice(211,3896)] = False #Old edge[slice(int(round(length * 0.0515)), int(round(0.9512 * length)))] = False ### static_flags = np.logical_or(edge, rfi_static_flags) ants = h5.ants colour = ['b', 'g', 'r', 'c', 'm', 'y', 'k'] pols = ['v', 'h'] for a in ants: ant = a.name try: rx_sn = h5.receivers[ant] except KeyError: logger.error( 'Receiver serial number for antennna %s not found in the H5 file' % ant) rx_sn = 'l.SN_NOT_FOUND' band, SN = rx_sn.split('.') if pdf: pdf_filename = output_dir + '/' + nice_filename + '.' + rx_sn + '.' + a.name + '.pdf' pp = PdfPages(pdf_filename) logger.debug("Created output PDF file: %s" % pdf_filename) #fig0 = plt.figure(0,figsize=(20,5)) h5.select() h5.select(ants=a.name, channels=~static_flags) observer = h5.ants[0].observer observer.date = time.gmtime( h5.timestamps.mean())[:6] # katdal resets this date to now()! fig0 = plot_ts(h5) Tsys, TAc, Tsys_std = {}, {}, {} eta_A = {} Tdiode = {} nd_temp = {} for pol in pols: logger.debug("Processing: %s%s" % (a.name, pol)) Tsys_std[pol] = None if not (Ku): diode_filename = '/var/kat/katconfig/user/noise-diode-models/mkat/rx.' + rx_sn + '.' + pol + '.csv' logger.info('Loading noise diode file %s from config' % diode_filename) try: nd = scape.gaincal.NoiseDiodeModel(diode_filename) except: logger.error( "Error reading the noise diode file ... using a constant value of 20k" ) logger.error( "Be sure to reprocess the data once the file is in the config" ) nd = scape.gaincal.NoiseDiodeModel(freq=[856, 1712], temp=[20, 20]) #cold data logger.debug('Using off target %s' % target) h5.select(ants=a.name, pol=pol, channels=~static_flags, targets=target, scans='track') freq = h5.channel_freqs cold_data = h5.vis[:].real cold_on, cold_off = get_nd_on_off(h5, log=logger) #hot data h5.select(ants=a.name, pol=pol, channels=~static_flags, targets='Moon', scans='track') hot_on, hot_off = get_nd_on_off(h5, log=logger) hot_data = h5.vis[:].real cold_spec = np.mean(cold_data[cold_off, :, 0], 0) hot_spec = np.mean(hot_data[hot_off, :, 0], 0) cold_nd_spec = np.mean(cold_data[cold_on, :, 0], 0) hot_nd_spec = np.mean(hot_data[hot_on, :, 0], 0) if not (Ku): nd_temp[pol] = nd.temperature(freq / 1e6) # antenna temperature on the moon (from diode calibration) TAh = hot_spec / (hot_nd_spec - hot_spec) * nd_temp[pol] # antenna temperature on cold sky (from diode calibration) (Tsys) TAc[pol] = cold_spec / (cold_nd_spec - cold_spec) * nd_temp[pol] print("Mean TAh = %f mean TAc = %f " % (TAh.mean(), TAc[pol].mean())) Y = hot_spec / cold_spec D = 13.5 # Efficiency tables are defined for 13.5 lam = 299792458. / freq HPBW = 1.18 * (lam / D) Om = 1.133 * HPBW**2 # main beam solid angle for a gaussian beam R = 0.5 * Dmoon(observer) # radius of the moon Os = 2 * np.pi * (1 - np.cos(R)) # disk source solid angle _f_MHz, _eff_pct = np.loadtxt( "/var/kat/katconfig/user/aperture-efficiency/mkat/ant_eff_%s_%s_AsBuilt.csv" % (band.upper(), pol.upper()), skiprows=2, delimiter="\t", unpack=True) eta_A[pol] = np.interp(freq, _f_MHz, _eff_pct) / 100. # EMSS aperture efficiency if Ku: eta_A[pol] = 0.7 Ag = np.pi * (D / 2)**2 # Antenna geometric area Ae = eta_A[pol] * Ag # Effective aperture x = 2 * R / HPBW # ratio of source to beam K = ((x / 1.2)** 2) / (1 - np.exp(-((x / 1.2)**2)) ) # correction factor for disk source from Baars 1973 TA_moon = 225 * (Os / Om) * ( 1 / K ) # contribution from the moon (disk of constant brightness temp) gamma = 1.0 Thot = TA_moon Tcold = 0 Tsys[pol] = gamma * (Thot - Tcold) / ( Y - gamma) # Tsys from y-method ... compare with diode TAc if error_bars: cold_spec_std = np.std(cold_data[cold_off, :, 0], 0) hot_spec_std = np.std(hot_data[hot_off, :, 0], 0) cold_nd_spec_std = np.std(cold_data[cold_on, :, 0], 0) hot_nd_spec_std = np.std(hot_data[hot_on, :, 0], 0) Y_std = Y * np.sqrt((hot_spec_std / hot_spec)**2 + (cold_spec_std / cold_spec)**2) Thot_std = 2.25 gamma_std = 0.01 # This is not definded raise NotImplementedError( "The factor Thot has not been defined ") Tsys_std[pol] = Tsys[pol] * np.sqrt((Thot_std / Thot)**2 + (Y_std / Y)**2 + (gamma_std / gamma)**2) else: Tsys_std[pol] = None if not (Ku): Ydiode = hot_nd_spec / hot_spec Tdiode_h = (Tsys[pol] - Tcold + Thot) * ( Ydiode / gamma - 1 ) # Tsys as computed above includes Tcold Ydiode = cold_nd_spec / cold_spec Tdiode_c = Tsys[pol] * (Ydiode / gamma - 1) Tdiode[pol] = ( Tdiode_h + Tdiode_c ) / 2. # Average two equally valid, independent results if write_nd: outfilename = save_ND(diode_filename, file_base, freq, Tdiode[pol]) logger.info('Noise temp data written to file %s' % outfilename) fig2 = plot_nd(freq, Tdiode, nd_temp, ant=ant, file_base=file_base) fig1 = plot_Tsys_eta_A(freq, Tsys, eta_A, TAc, Tsys_std=Tsys_std, ant=ant, file_base=file_base, Ku=Ku) if pdf: if not (Ku): fig2.savefig(pp, format='pdf') fig1.savefig(pp, format='pdf') fig0.savefig(pp, format='pdf') pp.close() # close the pdf file plt.close("all") logger.info('Processing complete')
fig = T_SysTemp.sky_fig(freq=freq_val.min()) fig.savefig(pp,format='pdf') plt.close(fig) for freq in select_freq : title = "" if np.abs(d.freqs[:]-freq).min() < freq_bw*1.1 : i = (np.abs(d.freqs[:]-freq)).argmin() lineval = None if str.upper(Band) == 'L': if freq > 1420 : lineval = 46 else: lineval = 42 fig = plot_data_el(tsys[0:length,i,:],tant[0:length,i,:],title=r"%s $T_{sys}/\eta_{ap}$ and $T_{ant}$ at %.1f MHz"%(nice_title,d.freqs[i]),units=units,line=lineval,aperture_efficiency=aperture_efficiency,frequency=d.freqs[i]) plt.figtext(0.89, 0.11,git_info(), horizontalalignment='right',fontsize=10) fig.savefig(pp,format='pdf') plt.close(fig) for el in select_el : title = "" i = (np.abs(tsys[0:length,:,2].max(axis=1)-el)).argmin() fig = plot_data_freq(d.freqs[:],tsys[i,:,:],tant[i,:,:],title=r"%s $T_{sys}/\eta_{ap}$ and $T_{ant}$ at %.1f Degrees elevation"%(nice_title,np.abs(tsys[0:length,:,2].max(axis=1))[i]),aperture_efficiency=aperture_efficiency,band=str.upper(Band)) plt.figtext(0.89, 0.11,git_info(), horizontalalignment='right',fontsize=10) fig.savefig(pp,format='pdf') plt.close(fig) #break fig = plt.figure(None,figsize = (8,8)) text =r"""The 'tipping curve' is calculated according to the expression below, with the parameters of $T_{\mathrm{ant}}$ and $\tau_{0}$, the Antenna temperature and the atmospheric opacity respectively. All the variables are also functions of frequency.
#!/usr/bin/python #Script to write out the current versions of installed github repos #Used by the workflow manager which imports katsdpscripts at runtime and doesn't update #__version__ when katsdpscripts is updated on the disk. This can be run by a subprocess #call which will import katsdpscripts from a new environment and correctly report the #installed version. #always import katsdpscripts from katsdpscripts import git_info #Attempt to import packages so that git_info can obtain their installed version for module in ['katholog', 'katpoint', 'katdal', 'scape']: try: __import__(module) except ImportError: pass print git_info('standard')
def calc_stats(timestamps, gain, pol='no polarizarion', windowtime=1200, minsamples=1200): """ calculate the Stats needed to evaluate the observation""" returntext = [] #note gain is in radians #change_el = pandas.rolling_apply(offset_el_ts,window=4*60/6.,min_periods=0,func=calc_change,freq='360s')*3600 gain_ts = pandas.Series(np.angle(gain), pandas.to_datetime(timestamps, unit='s')) #window_occ = pandas.rolling_count(gain_ts,windowtime)/float(windowtime) #full = np.where(window_occ==1) #note std is returned in degrees std = (pandas.rolling_apply(gain_ts, window=windowtime, func=angle_std, min_periods=minsamples)) peakmin = ((pandas.rolling_apply(gain_ts, window=windowtime, func=anglemin, min_periods=minsamples))) peakmax = ((pandas.rolling_apply(gain_ts, window=windowtime, func=anglemax, min_periods=minsamples))) gain_val_corr = ((pandas.rolling_apply(gain_ts, window=windowtime, func=angle_mean, min_periods=minsamples))) #gain_val = pandas.Series(gain_ts-gain_val_corr, pandas.to_datetime(timestamps, unit='s') ) gain_val = pandas.Series( np.angle(np.exp(1j * gain_ts) / np.exp(1j * gain_val_corr)), pandas.to_datetime(timestamps, unit='s')) peak = ((pandas.rolling_apply(gain_ts, window=windowtime, func=peak2peak, min_periods=minsamples))) dtrend_std = (pandas.rolling_apply(gain_ts, window=windowtime, func=detrend, min_periods=minsamples)) #trend_std = pandas.rolling_apply(ts,5,lambda x : np.ma.std(x-(np.arange(x.shape[0])*np.ma.polyfit(np.arange(x.shape[0]),x,1)[0])),1) timeval = timestamps.max() - timestamps.min() #rms = np.sqrt((gain**2).mean()) returntext.append( "Total time of observation : %f (seconds) with %i accumulations." % (timeval, timestamps.shape[0])) #returntext.append("The mean gain of %s is: %.5f"%(pol,gain.mean())) #returntext.append("The Std. dev of the gain of %s is: %.5f"%(pol,gain.std())) #returntext.append("The RMS of the gain of %s is : %.5f"%(pol,rms)) #returntext.append("The Percentage variation of %s is: %.5f"%(pol,gain.std()/gain.mean()*100)) returntext.append( "The mean Peak to Peak range over %i seconds of %s is: %.5f (req < 13 ) " % (windowtime, pol, np.degrees(peak.mean()))) returntext.append( "The Max Peak to Peak range over %i seconds of %s is: %.5f (req < 13 ) " % (windowtime, pol, np.degrees(peak.max()))) returntext.append("The mean variation over %i seconds of %s is: %.5f " % (windowtime, pol, np.degrees(std.mean()))) returntext.append("The Max variation over %i seconds of %s is: %.5f " % (windowtime, pol, np.degrees(std.max()))) returntext.append( "The mean detrended variation over %i seconds of %s is: %.5f (req < 2.3 )" % (windowtime, pol, np.degrees(dtrend_std.mean()))) returntext.append( "The Max detrended variation over %i seconds of %s is: %.5f (req < 2.3 )" % (windowtime, pol, np.degrees(dtrend_std.max()))) pltobj = plt.figure(figsize=[11, 20]) plt.suptitle(h5.name) plt.subplots_adjust(bottom=0.15, hspace=0.35, top=0.95) plt.subplot(311) plt.title('phases for ' + pol) (gain_val * 180. / np.pi).plot(label='phase( - rolling mean)') (peakmax * 180. / np.pi).plot(label='rolling max') (peakmin * 180. / np.pi).plot(label='rolling min') plt.legend(loc='best') plt.ylabel('Gain phase (deg)') ax2 = plt.subplot(312) plt.title('Peak to peak variation of %s, %i Second sliding Window' % ( pol, windowtime, )) (peak * 180. / np.pi).plot(color='blue') ax2.axhline(13, ls='--', color='red') #plt.legend(loc='best') plt.ylabel('Variation (deg)') ax3 = plt.subplot(313) plt.title('Detrended Std of %s, %i Second sliding Window' % ( pol, windowtime, )) (std * 180. / np.pi).plot(color='blue', label='Std') (dtrend_std * 180. / np.pi).plot(color='green', label='Detrended Std') ax3.axhline(2.2, ls='--', color='red') plt.legend(loc='best') plt.ylabel('Variation (deg)') plt.xlabel('Date/Time') pltobj2 = plt.figure(figsize=[11, 11]) plt.suptitle(h5.name) plt.subplots_adjust(bottom=0.15, hspace=0.35, top=0.95) plt.subplot(111) plt.title('Raw phases for ' + pol) (gain_ts * 180. / np.pi).plot(label='Raw phase') plt.legend(loc='best') plt.ylabel('Phase (deg)') plt.xlabel('Date/Time') plt.legend(loc='best') plt.figtext(0.89, 0.05, git_info(), horizontalalignment='right', fontsize=10) return returntext, pltobj, pltobj2 # a plot would be cool
def make_result_report_ku_band(gain, opts, targets, pdf): """ No noise diode present at ku-band. Gains will always have to be normalised. We are interested in the relative gain change between 15 to 90 degrees elevation """ #Separate masks for each target to plot separately targetmask = {} for targ in targets: targetmask[targ] = np.array( [test_targ == targ.strip() for test_targ in data['target']]) #Set up range of elevations for plotting fits fit_elev = np.linspace(5, 90, 85, endpoint=False) obs_details = data['timestamp_ut'][0] + ', ' + data['dataset'][0] + '.h5' #Set up the figure fig = plt.figure(figsize=(8.3, 11.7)) fig.subplots_adjust(hspace=0.0, bottom=0.2) plt.suptitle(obs_details) #Plot the gain vs elevation for each target ax1 = plt.subplot(511) #get ready to collect normalised gains for each target. norm_gain = np.array([]) norm_elev = np.array([]) all_fit_elev = [] all_fit_gain = [] for targ in targets: use_elev = data['elevation'] > opts.min_elevation fit_elev = data['elevation'][good & targetmask[targ] & use_elev] fit_gain = gain[good & targetmask[targ] & use_elev] all_fit_elev.append(fit_elev) all_fit_gain.append(fit_gain) #Fit the parabola with a peak at 60deg. elevation. fit = fit_parabola(all_fit_elev, all_fit_gain, pos=61.)['x'] for targnum, targ in enumerate(targets): plot_gain = gain[good & targetmask[targ]] / fit[targnum + 1] plot_elev = data['elevation'][good & targetmask[targ]] norm_gain = np.append(norm_gain, plot_gain) norm_elev = np.append(norm_elev, plot_elev) plt.plot(plot_elev, plot_gain, 'o', label=targ) plt.ylabel('Normalised gain') plt.xlabel('Elevation (deg)') fit_elev = np.arange(15., 90., 0.1) plt.plot(fit_elev, parabolic_func(fit_elev, fit[0], 61., 1.), label='12.5 GHz fit') #Get a title string if opts.condition_select not in ['ideal', 'optimum', 'normal']: condition = 'all' else: condition = opts.condition_select title = 'Gain Curve, ' title += antenna.name + ',' title += ' ' + opts.polarisation + ' polarisation,' title += ' ' + '%.0f MHz' % (data['frequency'][0]) title += ' ' + '%s conditions' % (condition) plt.title(title) plt.grid() plt.xlim(15., 90.) nu = 14.5e9 nu_0 = 12.5e9 g = lambda x: parabolic_func(x, fit[0], 61., 1.) g14 = lambda x: scale_gain(g, nu_0, nu, x) loss_12 = (g(61.) - g(15.)) / (g(61.)) * 100 loss_14 = (g14(61.) - g14(15.)) / g14(61.) * 100 detrend = norm_gain - g(norm_elev) med_detrend = np.median(detrend) #Get SD of detrended normalised gain data #sd_normgain = np.std(detrend) sd_normgain = 1.4826 * np.median(np.abs(med_detrend - detrend)) plt.fill_between(fit_elev, parabolic_func(fit_elev, fit[0], 60., 1 - sd_normgain), parabolic_func(fit_elev, fit[0], 60., 1 + sd_normgain), alpha=0.5, color='lightcoral') plt.axhline(0.95, linestyle='--', color='r') plt.plot(fit_elev, g14(fit_elev), label='14.5 GHz fit') legend = plt.legend(bbox_to_anchor=(1, -0.1)) plt.setp(legend.get_texts(), fontsize='small') outputtext = 'Relative loss in gain at 12.5 GHz is %.2f %%\n' % loss_12 outputtext += 'Relative loss in gain at 14.5 GHz is %.2f %%\n' % loss_14 outputtext += 'Standard deviation of normalised gain is %.2f %%' % ( sd_normgain * 100., ) plt.figtext(0.1, 0.55, outputtext, fontsize=11) plt.figtext(0.89, 0.5, git_info(), horizontalalignment='right', fontsize=10) fig.savefig(pdf, format='pdf') plt.close(fig)
var_theta, c=50), getper(var_theta, c=50. - 34.13), getper(var_theta, c=50. + 34.13))) #print (theta/fwhm).mean(),returntext[-1] mean = np.array(mean) lower = np.array(lower) upper = np.array(upper) fig = plt.figure(None) plt.title( 'File:%s Calculated Antenna short timescale jitter for %s' % (args[0].split('/')[-1], blvalue[0])) plt.xlabel("Angle offset from Boresight (degrees)") plt.ylabel("Standard Devation of Telescope pointing (arcseconds)") #plt.plot(thetav,mean,'go') #plt.plot(thetav,lower,'ro') #plt.plot(thetav,upper,'bo') plt.errorbar(thetav, mean, yerr=(mean - lower, upper - mean)) # the formulate is valid in these ranges 0.25 -> 0.55 plt.figtext(0.89, 0.11, git_info(), horizontalalignment='right', fontsize=10) fig.savefig(pp, format='pdf') plt.close(fig) fig = plt.figure(None, figsize=(10, 16)) plt.figtext(0.1, 0.1, '\n'.join(returntext), fontsize=10) fig.savefig(pp, format='pdf') pp.close() plt.close(fig)
def plot_std_results(corr_visdata_std, mean_visdata, freqdata, flagdata, baseline, pol, freqav, timeav, obs_details, pdf): #Frequency Range in MHz start_freq = freqdata[0] end_freq = freqdata[-1] #Get flag frequencies #print visdata.shape,np.sum(visdata.mask, axis=0) channel_width = freqdata[1] - freqdata[0] flagged_chans = np.sum(flagdata, axis=0, dtype=np.float) # Show where 50% of data is flagged flagged_chans = flagged_chans / flagdata.shape[0] > 0.5 flag_freqs = freqdata[flagged_chans] #Set up the figure fig = plt.figure(figsize=(8.3, 8.3)) fig.subplots_adjust(hspace=0.0) #Plot the gain vs elevation for each target ax1 = plt.subplot(211) ax1.axhline(0.005, ls='--', color='red') ax1.plot(freqdata, corr_visdata_std / mean_visdata * 100.0) ax1.set_yscale('log') plt.ylabel('Standard Deviation (% of mean)') tstring = 'Spectral Baseline, %s' % baseline if pol == 'I': tstring += ', Stokes I' else: tstring += ', %s pol' % pol # Add some pertinent information. pstring = 'Time average: %4.1f min.\n' % (timeav) pstring += 'Frequency average: %4.1f MHz.\n' % (freqav) pstring += 'Median standard deviation: %6.4f%%' % np.ma.median( corr_visdata_std / mean_visdata * 100.0) plt.figtext(0.5, 0.83, pstring) plt.grid() #plot title plt.title(tstring) plt.suptitle(obs_details) #Plot the spectrum with standard deviations around it ax2 = plt.subplot(212, sharex=ax1) ax2.plot(freqdata, mean_visdata) plt.figtext(0.6, 0.47, 'Average spectrum') plt.ylabel('Amplitude') plt.xlabel('Frequency (MHz)') #Overlay rfi rfilib.plot_RFI_mask(ax1, main=False, extra=flag_freqs, channelwidth=channel_width) rfilib.plot_RFI_mask(ax2, main=False, extra=flag_freqs, channelwidth=channel_width) if end_freq < start_freq: plt.xlim((end_freq, start_freq)) else: plt.xlim((start_freq, end_freq)) #Convert ticks to MHZ ticks = ticker.FuncFormatter(lambda x, pos: '{:4.0f}'.format(x / 1.e6)) ax2.xaxis.set_major_formatter(ticks) plt.grid() plt.figtext(0.89, 0.13, git_info(), horizontalalignment='right', fontsize=10) pdf.savefig(fig) plt.close(fig)
"target sky rms = %.3f' (robust %.3f')" % (old.sky_rms, old.robust_sky_rms), ha='center', va='baseline', fontdict=dict(color=(0.25, 0, 0, 1))) old.metrics(keep) # Create buttons to toggle parameter selection param_button_color = ['0.65', '0.0'] param_button_weight = ['normal', 'bold'] param_buttons = [setup_param_button(p) for p in range(len(display_params))] # Add old pointing model and labels list_o_names = 'Ant:%s , Datasets:' % (antenna.name) + ' ,'.join( np.unique(data['dataset']).tolist()) fig.text(0.405, 0.98, git_info(), horizontalalignment='right', fontsize=10) fig.text(0.905, 0.98, list_o_names, horizontalalignment='right', fontsize=10) fig.text(0.053, 0.95, 'OLD', ha='center', va='bottom', size='large') fig.text(0.105, 0.95, 'MODEL', ha='center', va='bottom', size='large') fig.text(0.16, 0.95, 'NEW', ha='center', va='bottom', size='large') fig.text(0.225, 0.95, 'STD', ha='center', va='bottom', size='large') for p, param in enumerate(display_params): param_str = param_to_str(old_model, param) if list( old_model.values())[param] else '' fig.text(0.085, 0.94 - (0.5 * 0.85 + p * 0.9) / len(display_params), param_str,
text, output_data_tmp = referencemetrics(ant, offsetdata, np.float(opts.num_samples_limit), np.float(opts.power_sample_limit)) #print text#,output_data_tmp if not output_data_tmp is None: if output_data is None: output_data = output_data_tmp.copy() #print "First time" else: #print "Next time",output_data.shape[0]+1 output_data.resize(output_data.shape[0] + 1) output_data[-1] = output_data_tmp.copy()[0] textString += text textString.append("") textString.append(git_info()) #for line in textString: print line if not opts.no_plot: nice_filename = args[0].split('/')[-1] + '_residual_pointing_offset' if len(args) > 1: nice_filename = args[0].split( '/')[-1] + '_Multiple_files' + '_residual_pointing_offset' pp = PdfPages(nice_filename + '.pdf') # plot diagnostic plots if len(output_data['condition']) > 0: suptitle = '%s offset-pointing accuracy vs environmental conditions' % ant.name.upper( ) fig = plot_diagnostics(output_data, suptitle) fig.savefig(pp, format='pdf') plt.close(fig)
def make_result_report_L_band(data, good, opts, pdf, gain, e, Tsys=None, SEFD=None): """ Generate a pdf report containing relevant results and a txt file with the plotting data. """ #Set up list of separate targets for plotting if opts.targets: targets = opts.targets.split(',') else: #Plot all targets targets = list(set(data['target'])) #Separate masks for each target to plot separately targetmask = {} for targ in targets: targetmask[targ] = np.array( [test_targ == targ.strip() for test_targ in data['target'][good]]) #Set up range of elevations for plotting fits fit_elev = np.linspace(5, 90, 85, endpoint=False) obs_details = data['timestamp_ut'][0] + ', ' + data['dataset'][0] + '.h5' #Set up the figure fig = plt.figure(figsize=(8.3, 11.7)) fig.subplots_adjust(hspace=0.0, bottom=0.2, right=0.8) plt.suptitle(obs_details) #Plot the gain vs elevation for each target ax1 = plt.subplot(511) for targ in targets: # Normalise the data by fit of line to it if not opts.no_normalise_gain: use_elev = data['elevation'] > opts.min_elevation fit_elev = data['elevation'][good & targetmask[targ] & use_elev] fit_gain = gain[good & targetmask[targ] & use_elev] fit = np.polyfit(fit_elev, fit_gain, 1) g90 = fit[0] * 90.0 + fit[1] #if fit[0]<0.0: # print "WARNING: Fit to gain on %s has negative slope, normalising to maximum of data"%(targ) # g90=max(fit_gain) plot_gain = gain[good & targetmask[targ]] / g90 plot_elevation = data['elevation'][good & targetmask[targ]] plt.plot(plot_elevation, plot_gain, 'o', label=targ) # Plot a pass fail line plt.axhline(0.95, 0.0, 90.0, ls='--', color='red') plt.axhline(1.05, 0.0, 90.0, ls='--', color='red') plt.ylabel('Normalised gain') else: plt.plot(plot_elevation, plot_gain, 'o', label=targ) plt.ylabel('Gain (%s/Jy)' % opts.units) #Get a title string if opts.condition_select not in ['ideal', 'optimum', 'normal']: condition = 'all' else: condition = opts.condition_select title = 'Gain Curve, ' title += antenna.name + ',' title += ' ' + opts.polarisation + ' polarisation,' title += ' ' + '%.0f MHz' % (data['frequency'][0]) title += ' ' + '%s conditions' % (condition) plt.title(title) legend = plt.legend(bbox_to_anchor=(1.3, 0.7)) plt.setp(legend.get_texts(), fontsize='small') plt.grid() # Only do derived plots if units were in Kelvin if opts.units != "counts": #Plot the aperture efficiency vs elevation for each target ax2 = plt.subplot(512, sharex=ax1) for targ in targets: plt.plot(data['elevation'][good & targetmask[targ]], e[good & targetmask[targ]], 'o', label=targ) plt.ylim((opts.eff_min, opts.eff_max)) plt.ylabel('Ae %') plt.grid() #Plot Tsys vs elevation for each target and the fit of the atmosphere ax3 = plt.subplot(513, sharex=ax1) for targ in targets: plt.plot(data['elevation'][good & targetmask[targ]], Tsys[good & targetmask[targ]], 'o', label=targ) #Plot the model curve for Tsys #fit_Tsys=T_rec + T_atm*(1 - np.exp(-tau/np.sin(np.radians(fit_elev)))) #plt.plot(fit_elev, fit_Tsys, 'k-') plt.ylabel('Tsys (K)') plt.grid() #Plot SEFD vs elevation for each target ax4 = plt.subplot(514, sharex=ax1) for targ in targets: plt.plot(data['elevation'][good & targetmask[targ]], SEFD[good & targetmask[targ]], 'o', label=targ) plt.ylabel('SEFD (Jy)') xticklabels = ax1.get_xticklabels() + ax2.get_xticklabels( ) + ax3.get_xticklabels() plt.setp(xticklabels, visible=False) plt.grid() plt.xlabel('Elevation (deg)') #Make some blank space for text ax5 = plt.subplot(515, sharex=ax1) plt.setp(ax5, visible=False) #Construct output text. outputtext = 'Median Gain (%s/Jy): %1.4f std: %.4f (el. > %2.0f deg.)\n' % ( opts.units, np.median(gain[good]), np.std( gain[good]), opts.min_elevation) if opts.units != "counts": outputtext += 'Median Ae (%%): %2.2f std: %.2f (el. > %2.0f deg.)\n' % ( np.median(e[good]), np.std(e[good]), opts.min_elevation) if Tsys is not None: outputtext += 'Median T_sys (K): %1.2f std: %1.2f (el. > %2.0f deg.)\n' % ( np.median(Tsys[good]), np.std(Tsys[good]), opts.min_elevation) if SEFD is not None: outputtext += 'Median SEFD (Jy): %4.1f std: %4.1f (el. > %2.0f deg.)\n' % ( np.median(SEFD[good]), np.std(SEFD[good]), opts.min_elevation) plt.figtext(0.1, 0.1, outputtext, fontsize=11) plt.figtext(0.89, 0.09, git_info(), horizontalalignment='right', fontsize=10) fig.savefig(pdf, format='pdf') plt.close(fig)