def get_xy_corr(out, doplot=True): ''' Analyze a pair of parallel and cross polarization calibration scans and return the X vs. Y delay phase corrections on all antennas 1-14. Required keyword: out a 2-element array of dicts representing two scans, the first being the parallel-feed scan, and the second being the crossed-feed scan. Optional keyword: doplot True => plot the final result, False => no plot ''' if doplot: import matplotlib.pylab as plt tstr = Time(out[0]['time'][0],format='jd').iso[:19].replace('-','').replace(' ','').replace(':','') ph0 = np.angle(np.sum(out[0]['x'][ri.bl2ord[:13,13]],3)) ph1 = np.angle(np.sum(out[1]['x'][ri.bl2ord[:13,13]],3)) ph0[:,2:] = ph1[:,2:] # Insert crossed-feed phases from ph1 into ph0 fghz = out[0]['fghz'] nf = len(fghz) dph = np.zeros((14,nf),np.float) # Determine xi_rot xi2 = ph0[:,2] - ph0[:,0] + ph0[:,3] - ph0[:,1] # This is 2 * xi, measured separately on each of 13 antennas xi_rot = np.unwrap(np.angle(np.sum(np.exp(1j*xi2),0)))/2. # Very clever average does not suffer from wrapping issues # Form differential delay phase from channels, and average them # dph14 = XY - XX and YY - YX + pi #dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] + np.pi/2),lobe(ph0[:,1] - ph0[:,3] - np.pi/2))) # 26 values for Ant 14 #dph[13] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX and XY - YY + pi #dphi = np.array((lobe(ph0[:,0] - ph0[:,3] - np.pi/2),lobe(ph0[:,2] - ph0[:,1] + np.pi/2))) # 2 values for Ant 14 #dph[:13] = np.angle(np.sum(np.exp(1j*dphi),0)) # dph14 = XY - XX - xi_rot and YY - YX + xi_rot dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] - xi_rot),lobe(ph0[:,1] - ph0[:,3] + xi_rot))) # 26 values for Ant 14 dph[13] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX + xi_rot and XY - YY - xi_rot dphi = np.array((lobe(ph0[:,0] - ph0[:,3] + xi_rot),lobe(ph0[:,2] - ph0[:,1] - xi_rot))) # 2 values for Ant 14 dph[:13] = np.angle(np.sum(np.exp(1j*dphi),0)) if doplot: f, ax = plt.subplots(4, 4, num='XY_Phase') ax.shape = (16,) for i in range(13): ax[i].plot(fghz,dphi[0,i],'.') ax[i].plot(fghz,dphi[1,i],'.') ax[i].plot(fghz,dph[i],'k.') for i in range(26): ax[13].plot(fghz,dph14[i],'.') ax[13].plot(fghz,dph[13],'k.') for i in range(14): ax[i].set_ylim(-4,4) f.suptitle('Multicolor: Measurements, Black: Final Results') np.savez('/common/tmp/Feed_rotation/'+tstr+'_delay_phase.npz',fghz=fghz,dph=dph,xi_rot=xi_rot) xy_phase = {'timestamp':Time(out[0]['time'][0],format='jd').lv,'fghz':fghz,'xyphase':dph,'xi_rot':xi_rot, 'dphi':dphi, 'dph14':dph14} return xy_phase
def get_xy_corr(npzlist=None, doplot=True): ''' Analyze a pair of parallel and cross polarization calibration scans and return the X vs. Y delay phase corrections on all antennas 1-14. Required keyword: npzlist a list of 2 NPZ filenames, the first being the parallel-feed scan, and the second being the crossed-feed scan. Optional keyword: doplot True => plot the final result, False => no plot ''' if npzlist is None: print 'Must provide a list of 2 NPZ files.' return None, None import read_idb as ri import numpy as np from util import lobe if doplot: import matplotlib.pylab as plt out0 = ri.read_npz([npzlist[0]]) # Parallel scan out1 = ri.read_npz([npzlist[1]]) # Perpendicular scan ph0 = np.angle(np.sum(out0['x'][ri.bl2ord[:13,13]],3)) ph1 = np.angle(np.sum(out1['x'][ri.bl2ord[:13,13]],3)) ph0[:,2:] = ph1[:,2:] # Insert crossed-feed phases from ph1 into ph0 fghz = out0['fghz'] nf = len(fghz) dph = np.zeros((14,nf),np.float) # Form differential delay phase from channels, and average them # dph14 = XY - XX and YY - YX + pi dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] + np.pi/2),lobe(ph0[:,1] - ph0[:,3] - np.pi/2))) # 26 values for Ant 14 dph[13] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX and XY - YY + pi dphi = np.array((lobe(ph0[:,0] - ph0[:,3] - np.pi/2),lobe(ph0[:,2] - ph0[:,1] + np.pi/2))) # 2 values for Ant 14 dph[:13] = np.angle(np.sum(np.exp(1j*dphi),0)) if doplot: f, ax = plt.subplots(4,4) ax.shape = (16,) for i in range(13): ax[i].plot(fghz,dphi[0,i],'.') ax[i].plot(fghz,dphi[1,i],'.') ax[i].plot(fghz,dph[i],'k.') for i in range(26): ax[13].plot(fghz,dph14[i],'.') ax[13].plot(fghz,dph[13],'k.') for i in range(14): ax[i].set_ylim(-4,4) f.suptitle('Multicolor: Measurements, Black: Final Results') np.savez('/common/tmp/Feed_rotation/'+npzlist[0].split('/')[-1][:14]+'_delay_phase.npz',fghz=fghz,dph=dph) return dph
def unrot_refcal(refcal_in): ''' Apply feed-rotation correction to data read with rd_refcal(), returning updated data in the same format for further processing. ''' import dbutil as db import copy import chan_util_bc as cu import cal_header as ch from stateframe import extract refcal = copy.deepcopy(refcal_in) xml, buf = ch.read_cal(11, Time(refcal['times'][0][0], format='jd')) dph = extract(buf, xml['XYphase']) xi_rot = extract(buf, xml['Xi_Rot']) freq = extract(buf, xml['FGHz']) freq = freq[np.where(freq != 0)] band = [] for f in freq: band.append(cu.freq2bdname(f)) bds, sidx = np.unique(band, return_index=True) nbd = len(bds) eidx = np.append(sidx[1:], len(band)) dxy = np.zeros((14, 34), dtype=np.float) xi = np.zeros(34, dtype=np.float) fghz = np.zeros(34) # average dph and xi_rot frequencies within each band, to convert to 34-band representation for b, bd in enumerate(bds): fghz[bd - 1] = np.nanmean(freq[sidx[b]:eidx[b]]) xi[bd - 1] = np.nanmean(xi_rot[sidx[b]:eidx[b]]) for a in range(14): dxy[a, bd - 1] = np.angle(np.sum(np.exp(1j * dph[a, sidx[b]:eidx[b]]))) nscans = len(refcal['scanlist']) for i in range(nscans): # Read parallactic angles for this scan trange = Time([refcal['tstlist'][i].iso, refcal['tedlist'][i].iso]) times, chi = db.get_chi(trange) tchi = times.jd t = refcal['times'][i] if len(t) > 0: vis = copy.deepcopy(refcal['vis'][i]) idx = nearest_val_idx(t, tchi) pa = chi[idx] # Parallactic angle for the times of this refcal. pa[:, [8, 9, 10, 12]] = 0.0 nt = len(idx) # Number of times in this refcal # Apply X-Y delay phase correction for a in range(13): a1 = lobe(dxy[a] - dxy[13]) a2 = -dxy[13] - xi a3 = dxy[a] - xi + np.pi for j in range(nt): vis[a, 1, :, j] *= np.exp(1j * a1) vis[a, 2, :, j] *= np.exp(1j * a2) vis[a, 3, :, j] *= np.exp(1j * a3) for j in range(nt): for a in range(13): refcal['vis'][i][a, 0, :, j] = vis[a, 0, :, j] * np.cos(pa[j, a]) + vis[a, 3, :, j] * np.sin(pa[j, a]) refcal['vis'][i][a, 2, :, j] = vis[a, 2, :, j] * np.cos(pa[j, a]) + vis[a, 1, :, j] * np.sin(pa[j, a]) refcal['vis'][i][a, 3, :, j] = vis[a, 3, :, j] * np.cos(pa[j, a]) - vis[a, 0, :, j] * np.sin(pa[j, a]) refcal['vis'][i][a, 1, :, j] = vis[a, 1, :, j] * np.cos(pa[j, a]) - vis[a, 2, :, j] * np.sin(pa[j, a]) return refcal
def coarse_delay(fghz,phz): # Do a coarse search of delays corresponding to phase errors ranging from -0.1 to 0.1 # Returns the delay value with the minimum sigma tvals = np.arange(-0.1,0.1,0.01) sigma = [] for t in tvals: sigma.append(np.std(lobe(phz - 2*np.pi*t*fghz))) return tvals[np.argmin(np.array(sigma))]
def apply_xy_corr(out,dph): ''' Does not actually change the data, only calculates and displays it ''' import copy import matplotlib.pylab as plt ph0 = np.angle(np.sum(out['x'][ri.bl2ord[:13,13]],3)) ph1 = copy.deepcopy(ph0) for i in range(13): ph1[i,1] += dph[i] - dph[13] ph1[i,2] += -dph[13] + np.pi/2 ph1[i,3] += dph[i] + np.pi/2 f, ax = plt.subplots(4,13) for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz,lobe(ph0[i,j]),'.',color='lightgreen') ax[j,i].plot(fghz,lobe(ph1[i,j]),'.',color='black') ax[j,i].set_ylim(-4,4)
def apply_xy_corr(out, dph): ''' Does not actually change the data, only calculates and displays it ''' import copy import matplotlib.pylab as plt ph0 = np.angle(np.sum(out['x'][ri.bl2ord[:13, 13]], 3)) ph1 = copy.deepcopy(ph0) for i in range(13): ph1[i, 1] += dph[i] - dph[13] ph1[i, 2] += -dph[13] + np.pi / 2 ph1[i, 3] += dph[i] + np.pi / 2 f, ax = plt.subplots(4, 13) for i in range(13): for j in range(4): ax[j, i].cla() ax[j, i].plot(fghz, lobe(ph0[i, j]), '.', color='lightgreen') ax[j, i].plot(fghz, lobe(ph1[i, j]), '.', color='black') ax[j, i].set_ylim(-4, 4)
def phase_diff(phacal, refcal): ''' Finds the delay slope (phase slope is 2*pi*fghz) of the difference between the input phase calibration and the input reference calibration. Adds some keywords to the phacal dict. This does NOT fit for a phase offset, but still returns offsets of zero, in case this is needed in the future. The sflags keyword is different from flags, because sflags can be set for either missing phase calibrations or missing reference calibrations. The slope values are zero for entries flagged in sflags. 2018-02-14 DG Added brute-force coarse delay calculation ''' def mbdfunc0(fghz, mbd): # fghz: frequency in GHz # ph0 = 0: phase offset identically set to zero (not fitted) # mbd: multi-band delay associated with the phase_phacal - phase_refcal in ns return 2. * np.pi * fghz * mbd def coarse_delay(fghz,phz): # Do a coarse search of delays corresponding to phase errors ranging from -0.1 to 0.1 # Returns the delay value with the minimum sigma tvals = np.arange(-0.1,0.1,0.01) sigma = [] for t in tvals: sigma.append(np.std(lobe(phz - 2*np.pi*t*fghz))) return tvals[np.argmin(np.array(sigma))] from scipy.optimize import curve_fit fghz = phacal['fghz'] if len(fghz) != len(refcal['fghz']): self.status.config(text = 'Status: Phase and Reference calibrations have different frequencies. No action taken.') return phacal dpha = np.angle(phacal['x'][:,:2]) - np.angle(refcal['x'][:,:2]) flags = np.logical_or(phacal['flags'][:,:2],refcal['flags'][:,:2]).astype(np.int) amp_pc = np.abs(phacal['x'][:,:2]) amp_rc = np.abs(refcal['x'][:,:2]) sigma = ((phacal['sigma'][:,:2]/amp_pc)**2. + (refcal['sigma'][:,:2]/amp_rc)**2)**0.5 slopes = np.zeros((15,2),np.float) offsets = np.zeros((15,2),np.float) flag = np.zeros((15,2),np.float) for ant in range(13): for pol in range(2): good, = np.where(flags[ant,pol] == 0) if len(good) > 3: x = fghz[good] t = coarse_delay(x,dpha[ant,pol,good]) # Get coarse delay y = np.unwrap(lobe(dpha[ant,pol,good] - 2*np.pi*t*x)) # Correct for coarse delay p, pcov = curve_fit(mbdfunc0, x, y, p0=[0.], sigma=sigma[ant,pol,good], absolute_sigma=False) slopes[ant,pol] = p + t # Add back coarse delay flag[ant,pol] = 0 else: flag[ant,pol] = 1 phacal.update({'mbd':slopes, 'mbd_flag':flag, 'flags': flags, 'offsets':offsets, 'pdiff':dpha}) return phacal
def apply_unrot_new(filename): import read_idb as ri import dbutil as db import copy from util import lobe, Time import matplotlib.pylab as plt import numpy as np blah = np.load('/common/tmp/Feed_rotation/20171223001448_delay_phase.npz') dph = blah['dph'] fghz = blah['fghz'] xi_rot = blah['xi_rot'] out = ri.read_npz([filename]) nbl, npol, nfrq, nt = out['x'].shape # Correct data for phase #n = [0,0,0,1,1,0,1,0,1,1,0,0,0] for i in range(13): a1 = lobe(dph[i] - dph[13]) a2 = -dph[13] - xi_rot a3 = dph[i] - xi_rot + np.pi for j in range(nt): out['x'][ri.bl2ord[i,13],1,:,j] *= np.exp(1j*a1) out['x'][ri.bl2ord[i,13],2,:,j] *= np.exp(1j*a2) out['x'][ri.bl2ord[i,13],3,:,j] *= np.exp(1j*a3) trange = Time(out['time'][[0,-1]],format='jd') times, chi = db.get_chi(trange) nskip = len(times)/nt chi = np.transpose(chi[::nskip+1]) chi[[8,9,10,12]] = 0.0 outp = copy.deepcopy(out) for i in range(nt): for k in range(13): outp['x'][ri.bl2ord[k,13],0,:,i] = out['x'][ri.bl2ord[k,13],0,:,i]*np.cos(chi[k,i]) + out['x'][ri.bl2ord[k,13],3,:,i]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],2,:,i] = out['x'][ri.bl2ord[k,13],2,:,i]*np.cos(chi[k,i]) + out['x'][ri.bl2ord[k,13],1,:,i]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],3,:,i] = out['x'][ri.bl2ord[k,13],3,:,i]*np.cos(chi[k,i]) - out['x'][ri.bl2ord[k,13],0,:,i]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],1,:,i] = out['x'][ri.bl2ord[k,13],1,:,i]*np.cos(chi[k,i]) - out['x'][ri.bl2ord[k,13],2,:,i]*np.sin(chi[k,i]) amp0 = np.abs(np.sum(out['x'][ri.bl2ord[:13,13]],3)) amp2 = np.abs(np.sum(outp['x'][ri.bl2ord[:13,13]],3)) f, ax = plt.subplots(4,13) for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz, amp0[i,j],'.',color='lightgreen') ax[j,i].plot(fghz, amp2[i,j],'k.') ax[j,i].set_ylim(0,10) ph0 = np.angle(np.sum(out['x'][ri.bl2ord[:13,13]],3)) ph2 = np.angle(np.sum(outp['x'][ri.bl2ord[:13,13]],3)) f, ax = plt.subplots(4,13) for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz, ph0[i,j],'.',color='lightgreen') ax[j,i].plot(fghz, ph2[i,j],'k.')
def apply_unrot(filename): import read_idb as ri import dbutil as db import copy from util import lobe, Time import matplotlib.pylab as plt import numpy as np blah = np.load('/common/tmp/Feed_rotation/20170702121949_delay_phase.npz') dph = blah['dph'] fghz = blah['fghz'] out = ri.read_npz([filename]) nbl, npol, nfrq, nt = out['x'].shape # Correct data for phase #n = [0,0,0,1,1,0,1,0,1,1,0,0,0] for i in range(13): a1 = lobe(dph[i] - dph[13]) a2 = -dph[13] + np.pi/2 a3 = dph[i] - np.pi/2 for j in range(nt): out['x'][ri.bl2ord[i,13],1,:,j] *= np.exp(1j*a1) out['x'][ri.bl2ord[i,13],2,:,j] *= np.exp(1j*a2) out['x'][ri.bl2ord[i,13],3,:,j] *= np.exp(1j*a3) trange = Time(out['time'][[0,-1]],format='jd') times, chi = db.get_chi(trange) nskip = len(times)/nt chi = np.transpose(chi[::nskip+1]) chi[[8,9,10,12]] = 0.0 outp = copy.deepcopy(out) for i in range(nt): for k in range(13): outp['x'][ri.bl2ord[k,13],0] = out['x'][ri.bl2ord[k,13],0]*np.cos(chi[k,i]) + out['x'][ri.bl2ord[k,13],3]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],2] = out['x'][ri.bl2ord[k,13],2]*np.cos(chi[k,i]) + out['x'][ri.bl2ord[k,13],1]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],3] = out['x'][ri.bl2ord[k,13],3]*np.cos(chi[k,i]) - out['x'][ri.bl2ord[k,13],0]*np.sin(chi[k,i]) outp['x'][ri.bl2ord[k,13],1] = out['x'][ri.bl2ord[k,13],1]*np.cos(chi[k,i]) - out['x'][ri.bl2ord[k,13],2]*np.sin(chi[k,i]) amp0 = np.abs(np.sum(out['x'][ri.bl2ord[:,13]],3)) amp2 = np.abs(np.sum(outp['x'][ri.bl2ord[:,13]],3)) f, ax = plt.subplots(4,13) for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz, amp0[i,j],'.',color='lightgreen') ax[j,i].plot(fghz, amp2[i,j],'k.') ph0 = np.angle(np.sum(out['x'][ri.bl2ord[:,13]],3)) ph2 = np.angle(np.sum(outp['x'][ri.bl2ord[:,13]],3)) f, ax = plt.subplots(4,13) for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz, ph0[i,j],'.',color='lightgreen') ax[j,i].plot(fghz, ph2[i,j],'k.')
def xydla(filename, ant_str='ant1-14', apply=False): ''' Determine X vs. Y delay based on packet capture with Noise Diode on filename Name and path of a PRT (packet capture) file taken with ND-ON, using a fixed band, e.g. band15.fsq. Returns xy 14-element list of delays to apply, for use in second argument of cal_header.dla_update2sql() Optional argument: ant_str If supplied, is the standard specification of antennas to include. Antennas not included are updated with 0 (i.e. no change) apply If True, calls cal_header.dla_update2sql() and cal_header.dla_censql2table() to update X vs. Y delays ''' import matplotlib.pylab as plt from util import lobe f, ax = plt.subplots(4, 4) ants = p.ant_str2list(ant_str) ax.shape = (16) if type(filename) is dict: # Interpret input as an already read dictionary, rather than a filename, and skip # the slow process of reading it again. out = filename else: out = p.rd_jspec(filename) xy = [] chrange = np.arange( 2100, 3900) # Portion of 4096 sub-channel range to use for the fit npts = len(chrange) x = np.linspace( 0, np.pi * npts / 4096., 1800) # This makes phase slope come out in units of delay steps for i in range(14): if i in ants: ax[i].plot(np.angle(out['a'][i, 2, :, 30]), 'y.', label='Ant ' + str(i + 1)) res = np.polyfit(x, np.unwrap(np.angle(out['a'][i, 2, chrange, 30])), 1) ax[i].plot(chrange, lobe(np.polyval(res, x)), 'r.') ax[i].set_ylim(-4, 4) ax[i].legend(fontsize=9, loc='best', fancybox=True, framealpha=0.5) else: res = [0., 0.] xy.append(res[0]) if apply: import cal_header as ch ch.dla_update2sql(np.zeros(14, np.float), np.array(xy)) ch.dla_censql2table() return np.array(xy)
def unrot_refcal(refcal_in): ''' Apply feed-rotation correction to data read with rd_refcal(), returning updated data in the same format for further processing. ''' import dbutil as db import copy import chan_util_bc as cu refcal = copy.deepcopy(refcal_in) blah = np.load('/common/tmp/Feed_rotation/20170702121949_delay_phase.npz') dph = blah['dph'] band=[] for f in blah['fghz']: band.append(cu.freq2bdname(f)) bds, sidx = np.unique(band, return_index=True) nbd = len(bds) eidx = np.append(sidx[1:], len(band)) dxy = np.zeros((14, 34), dtype=np.float) fghz = np.zeros(34) # average dph frequencies within each band, to convert to 34-band representation for b, bd in enumerate(bds): fghz[bd - 1] = np.nanmean(blah['fghz'][sidx[b]:eidx[b]]) for a in range(14): dxy[a,bd-1] = np.angle(np.sum(np.exp(1j*dph[a, sidx[b]:eidx[b]]))) # Read parallactic angles for entire time range trange = Time([refcal['tstlist'][0].iso,refcal['tedlist'][-1].iso]) times, chi = db.get_chi(trange) tchi = times.jd nscans = len(refcal['scanlist']) for i in range(nscans): t = refcal['times'][i] vis = copy.deepcopy(refcal['vis'][i]) idx = nearest_val_idx(t,tchi) pa = chi[idx] # Parallactic angle for the times of this refcal. pa[:,[8,9,10,12]] = 0.0 nt = len(idx) # Number of times in this refcal # Apply X-Y delay phase correction for a in range(13): a1 = lobe(dxy[a] - dxy[13]) a2 = -dxy[13] + np.pi/2 a3 = dxy[a] - np.pi/2 for j in range(nt): vis[a,1,:,j] *= np.exp(1j*a1) vis[a,2,:,j] *= np.exp(1j*a2) vis[a,3,:,j] *= np.exp(1j*a3) for j in range(nt): for a in range(13): refcal['vis'][i][a,0,:,j] = vis[a,0,:,j]*np.cos(pa[j,a]) + vis[a,3,:,j]*np.sin(pa[j,a]) refcal['vis'][i][a,2,:,j] = vis[a,2,:,j]*np.cos(pa[j,a]) + vis[a,1,:,j]*np.sin(pa[j,a]) refcal['vis'][i][a,3,:,j] = vis[a,3,:,j]*np.cos(pa[j,a]) - vis[a,0,:,j]*np.sin(pa[j,a]) refcal['vis'][i][a,1,:,j] = vis[a,1,:,j]*np.cos(pa[j,a]) - vis[a,2,:,j]*np.sin(pa[j,a]) return refcal
def doplot(self, ant=1): dla = self.delays[ant - 1] ydla = self.xydelays[ant - 1] # Also need delay settings for ant 1 dla1 = self.delays[0] ydla1 = self.xydelays[0] ydla14 = np.float(self.dla14.get()) for i, ax in enumerate(self.ax): if self.pol[i] == 0: # XX => use only the ant X delay tau = dla tau1 = dla1 elif self.pol[i] == 1: # YY => use the ant (X delay + Y-X delay) + Ant 14 Y-X delay tau = dla + ydla + ydla14 tau1 = dla1 + ydla1 + ydla14 elif self.pol[i] == 2: # XY => use the ant X delay + Ant 14 Y-X delay tau = dla + ydla14 tau1 = dla1 + ydla14 else: #YX => use the ant (X delay + Y-X delay) tau = dla + ydla tau1 = dla1 + ydla1 ax.cla() if ant == 1: ax.plot( self.fghz, lobe(self.ph[ant - 1, self.pol[i]] - 2 * np.pi * self.fghz * tau), '.') else: ax.plot( self.fghz, lobe(self.ph[ant - 1, self.pol[i]] - self.ph[0, self.pol[i]] - 2 * np.pi * self.fghz * (tau - tau1)), '.') ax.set_ylim(-4, 4) self.canvas.draw()
def apply_xy_corr(out,dph,dphnew=None): ''' Does not actually change the data, only calculates and displays it ''' import copy import matplotlib.pylab as plt from util import lobe fghz = out['fghz'] ph0 = np.angle(np.sum(out['x'][ri.bl2ord[:13,13]],3)) ph1 = copy.deepcopy(ph0) for i in range(13): ph1[i,1] += dph[i] - dph[13] ph1[i,2] += -dph[13] + np.pi/2 ph1[i,3] += dph[i] - np.pi/2 if not dphnew is None: ph2 = copy.deepcopy(ph0) dph, xi_rot = dphnew for i in range(13): ph2[i,1] += dph[i] - dph[13] ph2[i,2] += -dph[13] - xi_rot[i] ph2[i,3] += dph[i] - xi_rot[i] + np.pi f, ax = plt.subplots(4,13) f.suptitle('Old Correction Applied') for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz,lobe(ph0[i,j]),'.',color='lightgreen') ax[j,i].plot(fghz,lobe(ph1[i,j]),'.',color='black') ax[j,i].set_ylim(-4,4) f, ax = plt.subplots(4,13) f.suptitle('New Correction Applied') for i in range(13): for j in range(4): ax[j,i].cla() ax[j,i].plot(fghz,lobe(ph0[i,j]),'.',color='lightgreen') ax[j,i].plot(fghz,lobe(ph2[i,j]),'.',color='black') ax[j,i].set_ylim(-4,4)
def shift_time(told, target): ''' This is for calculating a time shift for a given schedule, to bring it to the current date. When editing a .scd file with calibrators, enter the first time as a Time() object told, and enter the current date/time as a Time() object target. Returns a new Time() object with the date/time that the schedule should be adjusted to. There is a potential for the time to be off by four minutes (one day's sidereal lag). ''' from astropy.time import TimeDelta dt = TimeDelta((util.lobe(eovsa_lst(target) - eovsa_lst(told)) * 0.158720), format='jd') print dt return target - dt
def sat_unrot(data,xyphase,band=0): from util import bl2ord xi_rot = xyphase['xi_rot'] dph = xyphase['xyphase'] nf = 4096 fidx2 = np.arange(nf) for i in range(13): for j in range(i + 1, 14): k = bl2ord[i, j] if j == 13: # xi_rot was applied for all antennas, but this xi = xi_rot[fidx2] # is wrong. Now it is only done for ant14. else: xi = 0.0 # xi_rot for other antennas is just zero. a1 = lobe(dph[i, fidx2] - dph[j, fidx2]) a2 = -dph[j, fidx2] - xi a3 = dph[i, fidx2] - xi + np.pi data['x'][k, 1, fidx2, 10*band:10*(band+1)] *= np.repeat(np.exp(1j * a1), 10).reshape(nf, 10) data['x'][k, 2, fidx2, 10*band:10*(band+1)] *= np.repeat(np.exp(1j * a2), 10).reshape(nf, 10) data['x'][k, 3, fidx2, 10*band:10*(band+1)] *= np.repeat(np.exp(1j * a3), 10).reshape(nf, 10) return data
def doplot(self,ant=1): dla = self.delays[ant-1] ydla = self.xydelays[ant-1] ydla14 = np.float(self.dla14.get()) for i,ax in enumerate(self.ax): if self.pol[i] == 0: # XX => use only the ant X delay tau = dla elif self.pol[i] == 1: # YY => use the ant (X delay + Y-X delay) + Ant 14 Y-X delay tau = dla + ydla + ydla14 elif self.pol[i] == 2: # XY => use the ant X delay + Ant 14 Y-X delay tau = dla + ydla14 else: #YX => use the ant (X delay + Y-X delay) tau = dla + ydla ax.cla() ax.plot(self.fghz,lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau),'.') ax.set_ylim(-4,4) self.canvas.draw()
def doplot(self, ant=1): dla = self.delays[ant - 1] ydla = self.xydelays[ant - 1] ydla14 = np.float(self.dla14.get()) for i, ax in enumerate(self.ax): if self.pol[i] == 0: # XX => use only the ant X delay tau = dla elif self.pol[i] == 1: # YY => use the ant (X delay + Y-X delay) + Ant 14 Y-X delay tau = dla + ydla + ydla14 elif self.pol[i] == 2: # XY => use the ant X delay + Ant 14 Y-X delay tau = dla + ydla14 else: #YX => use the ant (X delay + Y-X delay) tau = dla + ydla ax.cla() ax.plot( self.fghz, lobe(self.ph[ant - 1, self.pol[i]] - 2 * np.pi * self.fghz * tau), '.') ax.set_ylim(-4, 4) self.canvas.draw()
def unrot(data, azeldict=None): ''' Apply the correction to differential feed rotation to data, and return the corrected data. Inputs: data A dictionary returned by udb_util.py's readXdata(). azeldict The dictionary returned from get_sql_info(), or if None, the appropriate get_sql_info() call is done internally. Output: cdata A dictionary with the phase-corrected data. Only the key x is updated. ''' import copy from util import lobe trange = Time(data['time'][[0,-1]],format='jd') if azeldict is None: azeldict = get_sql_info(trange) chi = azeldict['ParallacticAngle'] # (nt, nant) # Correct parallactic angle for equatorial mounts, relative to Ant14 for i in [8,9,10,12,13]: chi[:,i] -= chi[:,13] # Ensure that nearest valid parallactic angle is used for times in the data good, = np.where(azeldict['ActualAzimuth'][0] != 0) tidx = nearest_val_idx(data['time'],azeldict['Time'][good].jd) # Read X-Y Delay phase from SQL database and get common frequencies xml, buf = ch.read_cal(11,t=trange[0]) fghz = stateframe.extract(buf,xml['FGHz']) good, = np.where(fghz != 0.) fghz = fghz[good] dph = stateframe.extract(buf,xml['XYphase']) dph = dph[:,good] fidx1, fidx2 = common_val_idx(data['fghz'],fghz,precision=4) missing = np.setdiff1d(np.arange(len(data['fghz'])),fidx1) nf, nbl, npol, nt = data['x'].shape nf = len(fidx1) # Correct data for X-Y delay phase for k,bl in enumerate(get_bl_order()): i, j = bl if i < 14 and j < 14 and i != j: a1 = lobe(dph[i,fidx2] - dph[j,fidx2]) a2 = -dph[j,fidx2] + np.pi/2 a3 = dph[i,fidx2] - np.pi/2 data['x'][fidx1,k,1] *= np.repeat(np.exp(1j*a1),nt).reshape(nf,nt) data['x'][fidx1,k,2] *= np.repeat(np.exp(1j*a2),nt).reshape(nf,nt) data['x'][fidx1,k,3] *= np.repeat(np.exp(1j*a3),nt).reshape(nf,nt) # Correct data for differential feed rotation cdata = copy.deepcopy(data) for n in range(nt): for k,bl in enumerate(get_bl_order()): i, j = bl if i < 14 and j < 14 and i != j: dchi = chi[n,i] - chi[n,j] cchi = np.cos(dchi) schi = np.sin(dchi) cdata['x'][:,k,0,n] = data['x'][:,k,0,n]*cchi + data['x'][:,k,3,n]*schi cdata['x'][:,k,2,n] = data['x'][:,k,2,n]*cchi + data['x'][:,k,1,n]*schi cdata['x'][:,k,3,n] = data['x'][:,k,3,n]*cchi - data['x'][:,k,0,n]*schi cdata['x'][:,k,1,n] = data['x'][:,k,1,n]*cchi - data['x'][:,k,2,n]*schi # Set flags for any missing frequencies (hopefully this also works when missing is np.array([])) cdata[missing] = np.ma.masked return cdata
def unrot(data, azeldict=None): ''' Apply the correction to differential feed rotation to data, and return the corrected data. Inputs: data A dictionary returned by udb_util.py's readXdata(). azeldict The dictionary returned from get_sql_info(), or if None, the appropriate get_sql_info() call is done internally. Output: cdata A dictionary with the phase-corrected data. Only the key x is updated. ''' import copy from util import lobe trange = Time(data['time'][[0, -1]], format='jd') if azeldict is None: azeldict = get_sql_info(trange) chi = azeldict['ParallacticAngle'] # (nt, nant) # Correct parallactic angle for equatorial mounts, relative to Ant14 for i in [8, 9, 10, 12, 13]: chi[:, i] -= chi[:, 13] # Ensure that nearest valid parallactic angle is used for times in the data good, = np.where(azeldict['ActualAzimuth'][0] != 0) tidx = nearest_val_idx(data['time'], azeldict['Time'][good].jd) # Read X-Y Delay phase from SQL database and get common frequencies xml, buf = ch.read_cal(11, t=trange[0]) fghz = stateframe.extract(buf, xml['FGHz']) good, = np.where(fghz != 0.) fghz = fghz[good] dph = stateframe.extract(buf, xml['XYphase']) dph = dph[:, good] fidx1, fidx2 = common_val_idx(data['fghz'], fghz, precision=4) missing = np.setdiff1d(np.arange(len(data['fghz'])), fidx1) nf, nbl, npol, nt = data['x'].shape nf = len(fidx1) # Correct data for X-Y delay phase for k, bl in enumerate(get_bl_order()): i, j = bl if i < 14 and j < 14 and i != j: a1 = lobe(dph[i, fidx2] - dph[j, fidx2]) a2 = -dph[j, fidx2] + np.pi / 2 a3 = dph[i, fidx2] - np.pi / 2 data['x'][fidx1, k, 1] *= np.repeat(np.exp(1j * a1), nt).reshape(nf, nt) data['x'][fidx1, k, 2] *= np.repeat(np.exp(1j * a2), nt).reshape(nf, nt) data['x'][fidx1, k, 3] *= np.repeat(np.exp(1j * a3), nt).reshape(nf, nt) # Correct data for differential feed rotation cdata = copy.deepcopy(data) for n in range(nt): for k, bl in enumerate(get_bl_order()): i, j = bl if i < 14 and j < 14 and i != j: dchi = chi[n, i] - chi[n, j] cchi = np.cos(dchi) schi = np.sin(dchi) cdata['x'][:, k, 0, n] = data['x'][:, k, 0, n] * cchi + data['x'][:, k, 3, n] * schi cdata['x'][:, k, 2, n] = data['x'][:, k, 2, n] * cchi + data['x'][:, k, 1, n] * schi cdata['x'][:, k, 3, n] = data['x'][:, k, 3, n] * cchi - data['x'][:, k, 0, n] * schi cdata['x'][:, k, 1, n] = data['x'][:, k, 1, n] * cchi - data['x'][:, k, 2, n] * schi # Set flags for any missing frequencies (hopefully this also works when missing is np.array([])) cdata[missing] = np.ma.masked return cdata
def unrot(data, azeldict=None): ''' Apply the correction to differential feed rotation to data, and return the corrected data. This also applies flags to data whose antennas are not tracking. Inputs: data A dictionary returned by udb_util.py's readXdata(). azeldict The dictionary returned from get_sql_info(), or if None, the appropriate get_sql_info() call is done internally. Output: cdata A dictionary with the phase-corrected data. Only the key x is updated. ''' import copy from util import lobe, bl2ord trange = Time(data['time'][[0, -1]], format='jd') if azeldict is None: azeldict = get_sql_info(trange) chi = azeldict['ParallacticAngle'] * np.pi / 180. # (nt, nant) # Correct parallactic angle for equatorial mounts, relative to Ant14 chi[:, [8, 9, 10, 12, 13]] = 0 # Currently 0, but can be measured and updated # Which antennas are tracking track = azeldict['TrackFlag'] # True if tracking # Ensure that nearest valid parallactic angle is used for times in the data good = np.where(azeldict['ActualAzimuth'] != 0) tidx = [] # List of arrays of indexes for each antenna for i in range(14): gd = good[0][np.where(good[1] == i)] tidx.append(nearest_val_idx(data['time'], azeldict['Time'][gd].jd)) # Read X-Y Delay phase from SQL database and get common frequencies xml, buf = ch.read_cal(11, t=trange[0]) fghz = stateframe.extract(buf, xml['FGHz']) good, = np.where(fghz != 0.) fghz = fghz[good] dph = stateframe.extract(buf, xml['XYphase']) dph = dph[:, good] xi_rot = stateframe.extract(buf, xml['Xi_Rot']) xi_rot = xi_rot[good] fidx1, fidx2 = common_val_idx(data['fghz'], fghz, precision=4) missing = np.setdiff1d(np.arange(len(data['fghz'])), fidx1) nf, nbl, npol, nt = data['x'].shape nf = len(fidx1) # Correct data for X-Y delay phase for i in range(13): for j in range(i + 1, 14): k = bl2ord[i, j] a1 = lobe(dph[i, fidx2] - dph[j, fidx2]) a2 = -dph[j, fidx2] - xi_rot[fidx2] a3 = dph[i, fidx2] - xi_rot[fidx2] + np.pi data['x'][fidx1, k, 1] *= np.repeat(np.exp(1j * a1), nt).reshape(nf, nt) data['x'][fidx1, k, 2] *= np.repeat(np.exp(1j * a2), nt).reshape(nf, nt) data['x'][fidx1, k, 3] *= np.repeat(np.exp(1j * a3), nt).reshape(nf, nt) # Correct data for differential feed rotation cdata = copy.deepcopy(data) for n in range(nt): for i in range(13): for j in range(i + 1, 14): k = bl2ord[i, j] ti = tidx[i][n] tj = tidx[j][n] if track[ti, i] and track[tj, j]: dchi = chi[ti, i] - chi[tj, j] cchi = np.cos(dchi) schi = np.sin(dchi) cdata['x'][:, k, 0, n] = data['x'][:, k, 0, n] * cchi + data['x'][:, k, 3, n] * schi cdata['x'][:, k, 2, n] = data['x'][:, k, 2, n] * cchi + data['x'][:, k, 1, n] * schi cdata['x'][:, k, 3, n] = data['x'][:, k, 3, n] * cchi - data['x'][:, k, 0, n] * schi cdata['x'][:, k, 1, n] = data['x'][:, k, 1, n] * cchi - data['x'][:, k, 2, n] * schi else: cdata['x'][:, k, :, n] = np.ma.masked # Set flags for any missing frequencies (hopefully this also works when "missing" is np.array([])) cdata['x'][missing] = np.ma.masked return cdata
def fit_diskmodel(out, bidx, rstn_flux, uvfitrange=[1, 150], angle_tolerance=np.pi / 2, doplot=True): ''' Given the result returned by read_ms(), plots the amplitude vs. uvdistance separately for polar and equatorial directions rotated for P-angle, then overplots a disk model for a disk enlarged by eqfac in the equatorial direction, and polfac in the polar direction. Also requires the RSTN flux spectrum for the date of the ms, determined from (example for 2019-09-01): import rstn frq, flux = rstn.rd_rstnflux(t=Time('2019-09-01')) rstn_flux = rstn.rstn2ant(frq, flux, out['fghz']*1000, t=Time('2019-09-01')) ''' from util import bl2ord, lobe import matplotlib.pylab as plt import sun_pos from scipy.special import j1 import scipy.constants mperns = scipy.constants.c / 1e9 # speed of light in m/ns # Rotate uv angle for P-angle pa, b0, r = sun_pos.get_pb0r(out['mjd'][0], arcsec=True) uvangle = lobe(out['uvangle'] - pa * np.pi / 180.) a = 2 * r * np.pi ** 2 / (180. * 3600.) # Initial scale for z, uses photospheric radius of the Sun if doplot: f, ax = plt.subplots(3, 1) uvmin, uvmax = uvfitrange uvdeq = [] uvdpol = [] ampeq = [] amppol = [] zeq = [] zpol = [] # Loop over antennas 1-4 antmax = 7 at = angle_tolerance for i in range(4): fidx, = np.where(out['band'] == bidx) # Array of frequency indexes for channels in this band for j, fi in enumerate(fidx): amp = out['amp'][0, fi, bl2ord[i, i + 1:antmax]].flatten() / 10000. # Convert to sfu # Use only non-zero amplitudes good, = np.where(amp != 0) amp = amp[good] uva = uvangle[bl2ord[i, i + 1:antmax]].flatten()[good] # Equatorial points are within +/- pi/8 of solar equator eq, = np.where(np.logical_or(np.abs(uva) < at / 2, np.abs(uva) >= np.pi - at / 2)) # Polar points are within +/- pi/8 of solar pole pol, = np.where(np.logical_and(np.abs(uva) >= np.pi / 2 - at / 2, np.abs(uva) < np.pi / 2 + at / 2)) uvd = out['uvdist'][bl2ord[i, i + 1:antmax]].flatten()[good] * out['fghz'][fi] / mperns # Wavelengths # Add data for this set of baselines to global arrays uvdeq.append(uvd[eq]) uvdpol.append(uvd[pol]) ampeq.append(amp[eq]) amppol.append(amp[pol]) zeq.append(uvd[eq]) zpol.append(uvd[pol]) uvdeq = np.concatenate(uvdeq) uvdpol = np.concatenate(uvdpol) uvdall = np.concatenate((uvdeq, uvdpol)) ampeq = np.concatenate(ampeq) amppol = np.concatenate(amppol) ampall = np.concatenate((ampeq, amppol)) zeq = np.concatenate(zeq) zpol = np.concatenate(zpol) zall = np.concatenate((zeq, zpol)) # These indexes are for a restricted uv-range to be fitted ieq, = np.where(np.logical_and(uvdeq > uvmin, uvdeq <= uvmax)) ipol, = np.where(np.logical_and(uvdpol > uvmin, uvdpol <= uvmax)) iall, = np.where(np.logical_and(uvdall > uvmin, uvdall <= uvmax)) if doplot: # Plot all of the data points ax[0].plot(uvdeq, ampeq, 'k+') ax[1].plot(uvdpol, amppol, 'k+') ax[2].plot(uvdall, ampall, 'k+') # Overplot the fitted data points in a different color ax[0].plot(uvdeq[ieq], ampeq[ieq], 'b+') ax[1].plot(uvdpol[ipol], amppol[ipol], 'b+') ax[2].plot(uvdall[iall], ampall[iall], 'b+') # Minimize ratio of points to model ntries = 300 solfac = np.linspace(1.0, 1.3, ntries) d2m_eq = np.zeros(ntries, np.float) d2m_pol = np.zeros(ntries, np.float) d2m_all = np.zeros(ntries, np.float) sfac = np.zeros(ntries, np.float) sfacall = np.zeros(ntries, np.float) # Loop over ntries (300) models of solar disk size factor ranging from 1.0 to 1.3 r_Sun for k, sizfac in enumerate(solfac): eqpts = rstn_flux[fidx][0] * 2 * np.abs(j1(a * sizfac * zeq[ieq]) / (a * sizfac * zeq[ieq])) polpts = rstn_flux[fidx[0]] * 2 * np.abs(j1(a * sizfac * zpol[ipol]) / (a * sizfac * zpol[ipol])) sfac[k] = (np.nanmedian(ampeq[ieq] / eqpts) + np.nanmedian(amppol[ipol] / polpts)) / 2 eqpts = rstn_flux[fidx[0]] * (2 * sfac[k]) * np.abs(j1(a * sizfac * zeq[ieq]) / (a * sizfac * zeq[ieq])) polpts = rstn_flux[fidx[0]] * (2 * sfac[k]) * np.abs(j1(a * sizfac * zpol[ipol]) / (a * sizfac * zpol[ipol])) allpts = rstn_flux[fidx[0]] * (2 * sfac[k]) * np.abs(j1(a * sizfac * zall[iall]) / (a * sizfac * zall[iall])) sfacall[k] = np.nanmedian(ampall[iall] / allpts) d2m_eq[k] = np.nanmedian(abs(ampeq[ieq] / eqpts - 1)) d2m_pol[k] = np.nanmedian(abs(amppol[ipol] / polpts - 1)) d2m_all[k] = np.nanmedian(abs(ampall[iall] / allpts - 1)) keq = np.argmin(d2m_eq) kpol = np.argmin(d2m_pol) kall = np.argmin(d2m_all) eqradius = solfac[keq] * r polradius = solfac[kpol] * r allradius = solfac[kall] * r sfactor = sfac[keq] sfall = sfacall[kall] sflux = sfall * rstn_flux[fidx[0]] if doplot: z = np.linspace(1.0, 1000.0, 10000) # Overplot the best fit ax[0].plot(z, rstn_flux[fidx[0]] * (2 * sfactor) * np.abs(j1(a * solfac[keq] * z) / (a * solfac[keq] * z))) ax[1].plot(z, rstn_flux[fidx[0]] * (2 * sfactor) * np.abs(j1(a * solfac[kpol] * z) / (a * solfac[kpol] * z))) ax[2].plot(z, rstn_flux[fidx[0]] * (2 * sfall) * np.abs(j1(a * solfac[kall] * z) / (a * solfac[kall] * z))) # ax[1].plot(zpol,polpts,'y.') ax[0].set_title( str(out['fghz'][fidx][0])[:4] + 'GHz. R_eq:' + str(eqradius)[:6] + '". R_pol' + str(polradius)[:6] + '". R_all' + str(allradius)[:6] + '". Flux scl fac:' + str(sfall)[:4]) # ax[0].plot(uvdeq,ampeq/eqpts,'k+') # ax[0].plot([0,1000],np.array([1,1])*np.nanmedian(ampeq/eqpts)) # ax[1].plot(uvdpol,amppol/polpts,'k+') # ax[1].plot([0,1000],np.array([1,1])*np.nanmedian(amppol/polpts)) for i in range(3): ax[i].set_xlim(0, 1000) ax[i].set_ylim(0.01, rstn_flux[fidx[0]] * 2 * sfactor) ax[i].set_yscale('log') ax[2].set_xlabel('UV Distance (wavelengths)') ax[i].set_ylabel('Amplitude (sfu)') ax[i].text(850, 125, ['Equator', 'Pole', 'All'][i]) return bidx, out['fghz'][fidx[0]], eqradius, polradius, allradius, sfall, sflux
def get_xy_corr(npzlist=None, doplot=True, npzlist2=None): ''' Analyze a pair of parallel and cross polarization calibration scans and return the X vs. Y delay phase corrections on all antennas 1-14. Required keyword: npzlist a list of 2 NPZ filenames, the first being the parallel-feed scan, and the second being the crossed-feed scan. Optional keyword: doplot True => plot the final result, False => no plot ''' if npzlist is None: print 'Must provide a list of 2 NPZ files.' return None, None import read_idb as ri from util import lobe, Time if doplot: import matplotlib.pylab as plt out0 = ri.read_npz([npzlist[0]]) # Parallel scan out1 = ri.read_npz([npzlist[1]]) # Perpendicular scan if npzlist2 is None: pass else: # Interpret second list as a set of additional files to be concatenated to the first for file in npzlist2: outx = ri.read_npz([file]) out0['x'] = np.concatenate((out0['x'],outx['x']),3) out1['x'] = np.concatenate((out1['x'],outx['x']),3) ph0 = np.angle(np.sum(out0['x'][ri.bl2ord[:13,13]],3)) ph1 = np.angle(np.sum(out1['x'][ri.bl2ord[:13,13]],3)) ph0[:,2:] = ph1[:,2:] # Insert crossed-feed phases from ph1 into ph0 fghz = out0['fghz'] nf = len(fghz) dph = np.zeros((14,nf),np.float) # Determine xi_rot xi2 = ph0[:,2] - ph0[:,0] + ph0[:,3] - ph0[:,1] # This is 2 * xi, measured separately on each of 13 antennas xi_rot = np.unwrap(np.angle(np.sum(np.exp(1j*xi2),0)))/2. # Very clever average does not suffer from wrapping issues # Form differential delay phase from channels, and average them # dph14 = XY - XX and YY - YX + pi #dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] + np.pi/2),lobe(ph0[:,1] - ph0[:,3] - np.pi/2))) # 26 values for Ant 14 #dph[13] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX and XY - YY + pi #dphi = np.array((lobe(ph0[:,0] - ph0[:,3] - np.pi/2),lobe(ph0[:,2] - ph0[:,1] + np.pi/2))) # 2 values for Ant 14 #dph[:13] = np.angle(np.sum(np.exp(1j*dphi),0)) # dph14 = XY - XX - xi_rot and YY - YX + xi_rot dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] - xi_rot),lobe(ph0[:,1] - ph0[:,3] + xi_rot))) # 26 values for Ant 14 dph[13] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX + xi_rot and XY - YY - xi_rot dphi = np.array((lobe(ph0[:,0] - ph0[:,3] + xi_rot),lobe(ph0[:,2] - ph0[:,1] - xi_rot))) # 2 values for Ant 14 dph[:13] = np.angle(np.sum(np.exp(1j*dphi),0)) if doplot: f, ax = plt.subplots(4, 4, num='XY_Phase') ax.shape = (16,) for i in range(13): ax[i].plot(fghz,dphi[0,i],'.') ax[i].plot(fghz,dphi[1,i],'.') ax[i].plot(fghz,dph[i],'k.') for i in range(26): ax[13].plot(fghz,dph14[i],'.') ax[13].plot(fghz,dph[13],'k.') for i in range(14): ax[i].set_ylim(-4,4) f.suptitle('Multicolor: Measurements, Black: Final Results') np.savez('/common/tmp/Feed_rotation/'+npzlist[0].split('/')[-1][:14]+'_delay_phase.npz',fghz=fghz,dph=dph,xi_rot=xi_rot) xy_phase = {'timestamp':Time(out0['time'][0],format='jd').lv,'fghz':fghz,'xyphase':dph,'xi_rot':xi_rot} return xy_phase
def calpntanal(t,ant_str='ant1-13',do_plot=True,ax=None): ''' Does a complete analysis of CALPNTCAL, reading information from the SQL database, finding the corresponding Miriad IDB data, and doing the gaussian fit to the beam, to return the beam and offset parameters. t Time object with a time near the start of the desired scan (finds the scan that starts closest time to the given time) ax If specified as a two-element array of axes, the plots will be placed in an already existing window, allowing reuse of the same window. Otherwise, a new figure is created (if do_plot is True). Returns a dictionary containing Source name, Time, HA, Dec, RA offset and Dec offset ''' import matplotlib.pyplot as plt from matplotlib.transforms import Bbox import read_idb import dbutil as db bl2ord = read_idb.bl2ord tdate = t.iso.replace('-','')[:8] fdir = '/data1/eovsa/fits/IDB/'+tdate+'/' fdb = dump_tsys.rd_fdb(t) scanidx, = np.where(fdb['PROJECTID'] == 'CALPNTCAL') # Set offset coordinates appropriate to the type of PROJECTID found if scanidx.size == 0: # No CALPNTCAL scans, so search for CALPNT2M scanidx, = np.where(fdb['PROJECTID'] == 'CALPNT2M') if scanidx.size == 0: print 'No CALPNTCAL or CALPNT2M project IDs found for date'+t.iso[:10] return {} else: # Found CALPNT2M, so set offset coordinates to match calpnt2m.trj rao = np.array([-5.00, -2.0, -1.0, -0.5, 0.00, 0.5, 1.0, 2.0]) deco = np.array([-2.0, -1.0, -0.5, 0.00, 0.5, 1.0, 2.0, 5.00]) pltfac = 10. else: # Found CALPNTCAL, so set offset coordinates to match calpnt.trj rao = np.array([-1.00, -0.20, -0.10, -0.05, 0.00, 0.05, 0.10, 0.20]) deco = np.array([-0.20, -0.10, -0.05, 0.00, 0.05, 0.10, 0.20, 1.00]) pltfac = 1. scans,sidx = np.unique(fdb['SCANID'][scanidx],return_index=True) tlist = Time(fdb['ST_TS'][scanidx[sidx]].astype(float).astype(int),format='lv') idx, = nearest_val_idx([t.jd],tlist.jd) filelist = [fdir+f for f in fdb['FILE'][np.where(fdb['SCANID'] == scans[idx])]] # Read pointing data (timerange t must be accurate) out = read_idb.read_idb(filelist, navg=30) # Determine wanted baselines with ant 14 from ant_str idx = read_idb.p.ant_str2list(ant_str) idx1 = idx[idx>7] # Ants > 8 idx2 = idx[idx<8] # Ants <= 8 # Determine parallactic angle for azel antennas (they are all the same, so find median). # If 0 < abs(chi) < 30, use channel XX # If 30 < abs(chi) < 60, use sum of channel XX and XY # If 60 < abs(chi) < 90, use channel XY midtime = Time((out['time'][0] + out['time'][-1])/2.,format='jd') times, chi = db.get_chi(Time([midtime.lv+1,midtime.lv + 10],format='lv')) abschi = abs(lobe(np.median(chi[0,0:8]))) if pltfac == 1.: # Case of 27m antenna pointing # Do appropriate sums over frequency, polarization and baseline if abschi >= 0 and abschi < np.pi/6: pntdata = np.sum(np.abs(np.sum(out['x'][bl2ord[idx,13],0,:,:48],1)),0) # Use only XX elif abschi >= np.pi/6 and abschi < np.pi/3: pntdata1 = np.sum(np.abs(np.sum(out['x'][bl2ord[idx1,13],0,:,:48],1)),0) # Use XX only for ants > 8 pntdata2 = np.sum(np.abs(np.sum(out['x'][bl2ord[idx2,13],:,:,:48],2)),0) # Use sum of XX and XY for ants <= 8 pntdata = pntdata1 + np.sum(pntdata2[np.array([0,2])],0) else: pntdata1 = np.sum(np.abs(np.sum(out['x'][bl2ord[idx1,13],0,:,:48],1)),0) # Use XX only for ants > 8 pntdata2 = np.sum(np.abs(np.sum(out['x'][bl2ord[idx2,13],2,:,:48],1)),0) # Use sum of XY for ants <= 8 pntdata = pntdata1 + pntdata2 # Measurements are 90 s long, hence 3 consecutive 30 s points, so do final # sum over these pntdata.shape = (16,3) stdev = np.std(pntdata,1) pntdata = np.sum(pntdata,1) radat = pntdata[:8] decdat = pntdata[8:] plsqr, xr, yr = solpnt.gausfit(rao, radat) plsqd, xd, yd = solpnt.gausfit(deco, decdat) midtime = Time((out['time'][0] + out['time'][-1])/2.,format='jd') if (do_plot): if ax is None: f, ax = plt.subplots(1,2) f.set_size_inches(2.5,1.5,forward=True) ax[0].errorbar(rao,radat,yerr=stdev[:8],fmt='.') ax[0].plot(xr,yr) ax[0].axvline(x=0,color='k') ax[0].axvline(x=plsqr[1],linestyle='--') ax[1].errorbar(deco,decdat,yerr=stdev[8:],fmt='.') ax[1].plot(xd,yd) ax[1].axvline(x=0,color='k') ax[1].axvline(x=plsqd[1],linestyle='--') for j in range(2): ax[j].set_xlim(-0.3, 0.3) ax[j].grid() ax[0].text(0.05,0.9,'RAO :'+str(plsqr[1])[:5],transform=ax[0].transAxes) ax[0].text(0.55,0.9,'FWHM:'+str(plsqr[2])[:5],transform=ax[0].transAxes) ax[0].set_xlabel('RA Offset [deg]') ax[1].text(0.05,0.9,'DECO:'+str(plsqd[1])[:5],transform=ax[1].transAxes) ax[1].text(0.55,0.9,'FWHM:'+str(plsqd[2])[:5],transform=ax[1].transAxes) ax[1].set_xlabel('Dec Offset [deg]') if ax is None: f.suptitle('Pointing on '+out['source']+' at '+midtime.iso) else: ax[0].set_title(out['source']+' at') ax[1].set_title(midtime.iso[:16]) plt.pause(0.5) return {'source':out['source'],'ha':out['ha'][24]*180./np.pi,'dec':out['dec']*180/np.pi, 'rao':plsqr[1],'deco':plsqd[1],'time':midtime,'antidx':13} else: # Case of 2m antenna pointing # Do appropriate sum over frequency and polarization but not baseline if abschi >= 0 and abschi < np.pi/6: pntdata = np.abs(np.sum(out['x'][bl2ord[idx,13],0,:,:48],1)) # Use only XX elif abschi >= np.pi/6 and abschi < np.pi/3: pntdata1 = np.abs(np.sum(out['x'][bl2ord[idx1,13],0,:,:48],1)) # Use XX only for ants > 8 pntdata2 = np.abs(np.sum(out['x'][bl2ord[idx2,13],:,:,:48],2)) # Use sum of XX and XY for ants <= 8 pntdata = np.concatenate((pntdata1,np.sum(pntdata2[:,np.array([0,2])],1)),0) else: pntdata1 = np.abs(np.sum(out['x'][bl2ord[idx1,13],0,:,:48],1)) # Use XX only for ants > 8 pntdata2 = np.abs(np.sum(out['x'][bl2ord[idx2,13],2,:,:48],1)) # Use sum of XY for ants <= 8 pntdata = np.concatenate((pntdata1,pntdata2),0) # Measurements are 90 s long, hence 3 consecutive 30 s points, so do final # sum over these pntdata.shape = (idx.size,16,3) stdev = np.std(pntdata,2) pntdata = np.sum(pntdata,2) rao_fit = np.zeros(idx.size,float) deco_fit = np.zeros(idx.size,float) if (do_plot): if ax is None: f, ax = plt.subplots(1,2*idx.size) f.set_size_inches(2.5*idx.size,1.5,forward=True) plt.subplots_adjust(left=0.03, right=0.97, top=0.89, bottom=0.3, wspace=0.1, hspace=0.1) for k in range(idx.size): radat = pntdata[k,:8] decdat = pntdata[k,8:] plsqr, xr, yr = solpnt.gausfit(rao, radat) plsqd, xd, yd = solpnt.gausfit(deco, decdat) midtime = Time((out['time'][0] + out['time'][-1])/2.,format='jd') if (do_plot): ax[k*2+0].errorbar(rao,radat,yerr=stdev[k,:8],fmt='.') ax[k*2+0].plot(xr,yr) ax[k*2+0].axvline(x=0,color='k') ax[k*2+0].axvline(x=plsqr[1],linestyle='--') ax[k*2+1].errorbar(deco,decdat,yerr=stdev[k,8:],fmt='.') ax[k*2+1].plot(xd,yd) ax[k*2+1].axvline(x=0,color='k') ax[k*2+1].axvline(x=plsqd[1],linestyle='--') for j in range(2): ax[k*2+j].set_xlim(-3.0, 3.0) ax[k*2+j].grid() ax[k*2+0].text(0.05,0.7,'RAO :'+str(plsqr[1])[:5],transform=ax[k*2+0].transAxes,fontsize=9) ax[k*2+0].text(0.05,0.5,'FWHM:'+str(plsqr[2])[:5],transform=ax[k*2+0].transAxes,fontsize=9) ax[k*2+0].set_xlabel('RAO [deg]',fontsize=9) ax[k*2+1].text(-0.2,0.85,'Ant :'+str(idx[k]+1),transform=ax[k*2+1].transAxes,fontsize=9) ax[k*2+1].text(0.05,0.7,'DECO:'+str(plsqd[1])[:5],transform=ax[k*2+1].transAxes,fontsize=9) ax[k*2+1].text(0.05,0.5,'FWHM:'+str(plsqd[2])[:5],transform=ax[k*2+1].transAxes,fontsize=9) ax[k*2+1].set_yticklabels([]) ax[k*2+1].set_xlabel('DECO [deg]',fontsize=9) if k == idx.size-1: ax[k+0].set_title(out['source']+' at',fontsize=9) ax[k+1].set_title(midtime.iso[:16],fontsize=9) # Adjust the pairs of subplots to group RA/Dec for each antenna together ax[k*2+0].set_position(Bbox(ax[k*2+0].get_position().get_points() + np.array([[0.0025,0],[0.0025,0]]))) ax[k*2+1].set_position(Bbox(ax[k*2+1].get_position().get_points() + np.array([[-0.0025,0],[-0.0025,0]]))) # Set to the same amplitude scale ymin = min(ax[k*2+0].get_ylim()[0],ax[k*2+1].get_ylim()[0]) ymax = max(ax[k*2+0].get_ylim()[1],ax[k*2+1].get_ylim()[1]) ax[k*2+0].set_ylim(ymin,ymax) ax[k*2+1].set_ylim(ymin,ymax) plt.pause(0.5) rao_fit[k] = plsqr[1] deco_fit[k] = plsqd[1] return {'source':out['source'],'ha':out['ha'][24]*180./np.pi,'dec':out['dec']*180/np.pi, 'rao':rao_fit,'deco':deco_fit,'time':midtime,'antidx':idx}
def rd_refcal(file, quackint=120., navg=3): ''' Reads a single UDB file representing a calibrator scan, and averages over the bands in the file ''' from read_idb import read_idb, bl2ord from copy import deepcopy import chan_util_bc as cu import dbutil as db out = read_idb([file], navg=navg, quackint=quackint) bds = np.unique(out['band']) nt = len(out['time']) nbd = len(bds) vis = np.zeros((15, 4, 34, nt), dtype=complex) fghz = np.zeros(34) # average over channels within each band o = out['x'][bl2ord[13,:13]] for bd in bds: idx = np.where(out['band'] == bd)[0] fghz[bd-1] = np.nanmean(out['fghz'][idx]) vis[:13,:,bd-1] = np.mean(o[:, :, idx], axis=2) # Need to apply unrot to correct for feed rotation, before returning xml, buf = ch.read_cal(11, Time(out['time'][0],format='jd')) dph = extract(buf,xml['XYphase']) xi_rot = extract(buf,xml['Xi_Rot']) freq = extract(buf,xml['FGHz']) freq = freq[np.where(freq != 0)] band = [] for f in freq: band.append(cu.freq2bdname(f)) bds, sidx = np.unique(band, return_index=True) nbd = len(bds) eidx = np.append(sidx[1:], len(band)) dxy = np.zeros((14, 34), dtype=np.float) xi = np.zeros(34, dtype=np.float) fghz = np.zeros(34) # average dph and xi_rot frequencies within each band, to convert to 34-band representation for b, bd in enumerate(bds): fghz[bd - 1] = np.nanmean(freq[sidx[b]:eidx[b]]) xi[bd - 1] = np.nanmean(xi_rot[sidx[b]:eidx[b]]) for a in range(14): dxy[a, bd - 1] = np.angle(np.sum(np.exp(1j * dph[a, sidx[b]:eidx[b]]))) # Read parallactic angles for this scan trange = Time(out['time'][[0,-1]],format='jd') times, chi = db.get_chi(trange) tchi = times.jd t = out['time'] if len(t) > 0: vis2 = deepcopy(vis) idx = nearest_val_idx(t, tchi) pa = chi[idx] # Parallactic angle for the times of this refcal. pa[:, [8, 9, 10, 12]] = 0.0 nt = len(idx) # Number of times in this refcal # Apply X-Y delay phase correction for a in range(13): a1 = lobe(dxy[a] - dxy[13]) a2 = -dxy[13] - xi a3 = dxy[a] - xi + np.pi for j in range(nt): vis2[a, 1, :, j] *= np.exp(1j * a1) vis2[a, 2, :, j] *= np.exp(1j * a2) vis2[a, 3, :, j] *= np.exp(1j * a3) for j in range(nt): for a in range(13): vis[a, 0, :, j] = vis2[a, 0, :, j] * np.cos(pa[j, a]) + vis2[a, 3, :, j] * np.sin(pa[j, a]) vis[a, 2, :, j] = vis2[a, 2, :, j] * np.cos(pa[j, a]) + vis2[a, 1, :, j] * np.sin(pa[j, a]) vis[a, 3, :, j] = vis2[a, 3, :, j] * np.cos(pa[j, a]) - vis2[a, 0, :, j] * np.sin(pa[j, a]) vis[a, 1, :, j] = vis2[a, 1, :, j] * np.cos(pa[j, a]) - vis2[a, 2, :, j] * np.sin(pa[j, a]) # ******* return {'file': file, 'source': out['source'], 'vis': vis, 'bands': bds, 'fghz': fghz, 'times': out['time'], 'ha': out['ha'], 'dec': out['dec'], 'flag': np.zeros_like(vis, dtype=np.int)}
def doplot(self,ant=1): polstr = ['XX','XY','YX','YY'] dla = self.delays[ant-1] ydla = self.xydelays[ant-1] # Also need delay settings for ant 1 dla1 = self.delays[0] ydla1 = self.xydelays[0] ydla14 = np.float(self.dla14.get()) for i,ax in enumerate(self.ax[:4]): if self.pol[i] == 0: # XX => use only the ant X delay tau = dla tau1 = dla1 elif self.pol[i] == 1: # YY => use the ant (X delay + Y-X delay) + Ant 14 Y-X delay tau = dla + ydla + ydla14 tau1 = dla1 + ydla1 + ydla14 elif self.pol[i] == 2: # XY => use the ant X delay + Ant 14 Y-X delay tau = dla + ydla14 tau1 = dla1 + ydla14 else: #YX => use the ant (X delay + Y-X delay) tau = dla + ydla tau1 = dla1 + ydla1 ax.cla() if ant == 1: ax.plot(self.fghz,lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau),'.',label=polstr[i]) if self.pol[i] == 0: pxx = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 1: pyy = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 2: pxy = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 3: pyx = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) else: ax.plot(self.fghz,lobe(self.ph[ant-1,self.pol[i]] - self.ph[0,self.pol[i]] - 2*np.pi*self.fghz*(tau-tau1)),'.',label=polstr[i]) if self.pol[i] == 0: pxx = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 1: pyy = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 2: pxy = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) if self.pol[i] == 3: pyx = lobe(self.ph[ant-1,self.pol[i]] - 2*np.pi*self.fghz*tau) ax.set_ylim(-4,4) ax.legend(fontsize=9,loc='lower right') self.ax[4].cla() self.ax[4].plot(self.fghz,lobe(pyy - pxx),'.') self.ax[4].set_title('YY - XX Phase') #self.ax[4].plot(self.fghz,lobe(pxy - pyx),'.') self.ax[4].set_ylim(-4,4) self.canvas.draw()
def refcal_anal(out, timerange=None, scanidx=None, minsnr=0.7, bandplt=[5, 11, 17, 23], doplot=True, lohi=False): '''Analyze the visibility data from rd_refcal and return time averaged visibility values and flags. ***Optional Keywords*** timerange: time range to obtain the average. E.g., timerange=Time(['2017-04-08T05:00','2017-04-08T07:00']) scanidx: index numbers of scans to select. Useful when other (undesired) types of observations exist. !!!! The selected scans should have exactly the same frequency/band setup !!!! minsnr: minimum signal to noise to consider. Data with smaller SNRs will be flagged (as 1 in the flag array) bandplt: bands to show in the figure doplot: if True, display plots of results (default) ***Outputs*** refcal: complex array of shape (15, 2, 34) (nant, npol, nband) as the result of the reference calibration flag: int array of shape (15, 2, 34). 0 is unflagged and 1 is flagged. timestamp: midpoint of the time range used for averaging to obtain the refcal values ''' if scanidx: scanlist = [out['scanlist'][i] for i in scanidx] srclist = [out['srclist'][i] for i in scanidx] tstlist = [out['tstlist'][i] for i in scanidx] tedlist = [out['tedlist'][i] for i in scanidx] bandnames = [out['bandnames'][i] for i in scanidx] vis = [out['vis'][i] for i in scanidx] times_ = [out['times'][i] for i in scanidx] fghzs = [out['fghzs'][i] for i in scanidx] else: scanlist = out['scanlist'] srclist = out['srclist'] tstlist = out['tstlist'] tedlist = out['tedlist'] bandnames = out['bandnames'] vis = out['vis'] times_ = out['times'] fghzs = out['fghzs'] scanidx = range(len(scanlist)) if len(set(np.array(srclist)[scanidx])) > 1: prompt = '' while not (prompt.lower() in ['y', 'n']): prompt = raw_input('Multiple sources are selected. Are you sure to continue? [y/n]') if prompt.lower() == 'n': print 'Abort...' return None fghz = fghzs[0] times = np.concatenate(times_) vis = np.concatenate(vis, axis=3) # only keep the first 2 polarizations vis = vis[:, :2] if timerange: tidx, = np.where((times > timerange[0].jd) & (times < timerange[1].jd)) if len(tidx) == 0: print 'no records within the selected timerange. Abort...' for i in range(len(scanlist)): sidx, = np.where((times_[i] > timerange[0].jd) & (times_[i] < timerange[1].jd)) if len(sidx) > 0: src = srclist[i] break vis = vis[:, :, :, tidx] timeavg = times[tidx] else: timeavg = times src = srclist[0] # vismean = np.nanmean(np.angle(vis),axis=3) vis_ = np.zeros(vis.shape[:3], dtype=complex) flag = np.zeros(vis.shape[:3], dtype=int) sigma = np.zeros(vis.shape[:3]) + 1e10 # compute standard deviation of the visibilities sigma_ = np.nanstd(vis, axis=3) # sigma = np.nanstd(np.abs(vis),axis=3) # mask out records with phases > 3 sigma for bd in range(34): # print 'band: ',bd for ant in range(13): # print 'ant: ',ant for pol in range(2): # print 'pol: ',pol amp = np.abs(vis[ant, pol, bd]) amp_median = np.median(np.abs(vis[ant, pol, bd])) ind, = np.where(np.abs(amp - amp_median) < sigma_[ant, pol, bd]) snr = amp_median / sigma_[ant, pol, bd] # pdb.set_trace() if snr < minsnr or np.isnan(snr): flag[ant, pol, bd] = 1 else: if len(ind) > len(timeavg) / 2: vis_[ant, pol, bd] = np.nanmean(vis[ant, pol, bd, ind]) sigma[ant, pol, bd] = np.nanstd(vis[ant, pol, bd, ind]) else: vis_[ant, pol, bd] = np.nanmean(vis[ant, pol, bd]) flag[ant, pol, bd] = 1 # print '# of valid datapoints: ',len(ind) # count how many datapoints are flagged in a given band nflag = np.count_nonzero(flag[:13, :, bd]) print '{0:d} of 26 measurements are flagged due to SNR < {1:.1f} in Band {2:d}'.format(nflag, minsnr, bd + 1) # flag zero values (e.g., antennas or bands not observed) zeroind = np.where(np.abs(vis_ == 0)) flag[zeroind] = 1 # timestamps timestamp = Time(np.mean(timeavg), format='jd') timestamp_gcal = Time((tstlist[0].jd + tedlist[0].jd) / 2., format='jd') #Calculate band 4 phases refcal_lohi = sql2refcal(timestamp, lohi=True) #obtain from SQL database dph = np.zeros((15,2,30)) for i in range(13): for j in range(2): dph[i,j,:] = np.unwrap(lobe(np.angle(vis_)[i,j,4:] - refcal_lohi['pha'][i,j,4:])) dphfitxxyy = np.zeros((13,2,2)) w = np.ones(31) w[0] += 99 for i in range(13): for j in range(2): dphfitxxyy[i,j,:] = np.polyfit(np.append([0.],fghz[4:]),np.append([0.],[dph[i,j]]),1,w=w) fghz[3] = refcal_lohi['fghz'][3] dph4 = np.zeros((13,2)) for i in range(13): for j in range(2): dph4[i,j] = np.polyval(dphfitxxyy[i,j,:],fghz[3]) dph4 = dph4 for i in range(13): #Insert band 4 phase in high frequency receiver for j in range(2): vis_[i,j,3] = np.complex(np.cos(refcal_lohi['pha'][i,j,3] + dph4[i,j]), np.sin(refcal_lohi['pha'][i,j,3] + dph4[i,j])) #Amp = 1.0 if doplot: visavg = {'pha': np.angle(vis_), 'amp': np.abs(vis_), 'timestamp': timestamp, 't_bg': timeavg[0], 't_ed': timeavg[-1], 'flag': flag} graph(out, visavg, scanidx=scanidx, bandplt=bandplt) graph(out, visavg, scanidx=scanidx, bandplt=bandplt, pol=1) f2, ax2 = plt.subplots(2, 13, figsize=(12, 5)) if lohi == 0: f2.suptitle('Orange = Estimated band 4 phases based on lohi scans on ' + str(refcal_lohi['timestamp'].iso)) plt.title('source: {}'.format(srclist[scanidx[0]])) f3, ax3 = plt.subplots(2, 13, figsize=(12, 5)) plt.title('source: {}'.format(srclist[scanidx[0]])) allbands = np.arange(34) + 1 for ant in range(13): for pol in range(2): ind, = np.where(flag[ant, pol, :] == 0) ax2[pol, ant].plot(allbands[ind], np.unwrap(visavg['pha'][ant, pol, ind]), '.', markersize=5) if lohi == 0: ax2[pol, ant].plot(4., np.unwrap(visavg['pha'])[ant, pol, 3], '.', markersize=5) #Add band 4 ax2[pol, ant].set_ylim([-20, 20]) ax2[pol, ant].set_xlim([1, 34]) ax3[pol, ant].plot(allbands[ind], visavg['amp'][ant, pol, ind], '.', markersize=5) ax3[pol, ant].set_xlim([1, 34]) ax3[pol, ant].set_ylim([0, 1.]) if ant == 0: ax2[pol, ant].set_ylabel('Phase (radian)') ax3[pol, ant].set_ylabel('Amplitude') else: ax2[pol, ant].set_yticks([]) ax3[pol, ant].set_yticks([]) if pol == 1 and ant == 0: ax2[pol, ant].set_xlabel('Band #') ax3[pol, ant].set_xlabel('Band #') if pol == 0: ax2[pol, ant].set_title('Ant ' + str(ant + 1)) ax2[pol, ant].set_xticks([]) ax3[pol, ant].set_title('Ant ' + str(ant + 1)) ax3[pol, ant].set_xticks([]) if lohi == 0: f, ax = plt.subplots(2,13,figsize=(12,5)) #Plot delay curve used for band 4 phase estimation f.suptitle('Phase differences with respect to ' + str(refcal_lohi['timestamp'].iso)) for i in range(13): ax[0,i].set_title('Ant ' + str(i+1)) for j in range(2): ax[j,i].plot(fghz[4:],dph[i,j,:],'.') ax[j,i].plot(np.append([0.],fghz[4:]),np.polyval(dphfitxxyy[i,j,:],np.append([0.],fghz[4:]))) ax[j,i].plot(fghz[3],np.polyval(dphfitxxyy[i,j,:],fghz[3]),'.') ax[j,i].set_xlim([0.,18.]) ax[j,i].set_ylim([-np.pi,np.pi]) ax[0,0].set_ylabel('XX') ax[1,0].set_ylabel('YY') ax[1,0].set_xlabel('fghz') if lohi: fghz = out['fghzs'][0] fghz[3] = out['fghzs'][1][3] bandnames = np.append(4,out['bandnames'][0]) com_idx = [4,5,6,7,8,9,10,11] #because common_val_idx somehow failed phlo = np.angle(np.sum(out['vis'][1],3))[:,:2,:] phhi = np.angle(vis_) #Caliculate band 4 phase dphlohi = np.unwrap(lobe(phlo[:,:,com_idx] - phhi[:,:,com_idx])) #dph over bands 5-12 dphfitlohi = np.zeros((13,2,3)) for i in range(13): #Poly-fit dph curve for j in range(2): dphfitlohi[i,j,:] = np.polyfit(out['fghzs'][0][com_idx],dphlohi[i,j,:],2) dph4 = np.zeros((13,2)) #Extrapolate the fit to band 4 for i in range(13): for j in range(2): dph4[i,j] = np.polyval(dphfitlohi[i,j,:],out['fghzs'][1][3]) for i in range(13): #Insert band 4 phase in high frequency receiver for j in range(2): vis_[i,j,3] = np.complex(np.cos(phlo[i,j,3] - dph4[i,j]), np.sin(phlo[i,j,3] - dph4[i,j])) #Amp = 1.0 phhi_new = np.angle(vis_) #Plot band 4 phase f, ax = plt.subplots(2,13,figsize=(12,5)) f.suptitle('Calculated band 4 phases (orange)') plt.title('source: {}'.format(srclist[scanidx[0]])) for ant in range(13): ax[0,ant].set_title('Ant ' + str(ant+1)) for pol in range(2): ax[pol,ant].plot(bandnames[1:], np.unwrap(phhi_new)[ant,pol,4:],'.') ax[pol,ant].plot(bandnames[0], np.unwrap(phhi_new)[ant,pol,3],'.') ax[pol,ant].set_ylim([-20,20]) ax[pol,ant].set_xlim([1,34]) if pol == 0: ax[pol,ant].set_xticks([]) if ant >= 1: ax[pol,ant].set_yticks([]) ax[1,0].set_xlabel('Band #') ax[0,0].set_ylabel('XX Phase (radian)') ax[1,0].set_ylabel('YY Phase (radian)') return {'src': src, 'vis': vis_, 'pha': np.angle(vis_), 'amp': np.abs(vis_), 'fghz': fghz, 'flag': flag, 'sigma': sigma, 'timestamp': timestamp, 't_gcal': timestamp_gcal, 't_bg': Time(timeavg[0], format='jd'), 't_ed': Time(timeavg[-1], format='jd')} return {'src': src, 'vis': vis_, 'pha': np.angle(vis_), 'amp': np.abs(vis_), 'fghz': fghz, 'flag': flag, 'sigma': sigma, 'timestamp': timestamp, 't_gcal': timestamp_gcal, 't_bg': Time(timeavg[0], format='jd'), 't_ed': Time(timeavg[-1], format='jd')}
def sat_xy_corr(out0, out1, band=0, ant_str='ant1-13', doplot=True): ''' Analyze a pair of parallel and cross polarization calibration packet captures on a Geosat in K-band (bands 33, 34, 35, 36, 37) and return the X vs. Y delay phase corrections on all antennas 1-14. Required keyword: prtlist a list of 2 PRT filenames, the first being the parallel-feed scan, and the second being the crossed-feed scan. band the band index (0-4) corresponding to the above 5 bands Optional keyword: doplot True => plot the final result, False => no plot ''' import pcapture2 as p from util import bl2ord, ant_str2list if doplot: import matplotlib.pylab as plt antlist = ant_str2list(ant_str) nant = len(antlist) # out0 = p.rd_jspec(prtlist[0]) # Parallel scan # out1 = p.rd_jspec(prtlist[1]) # Perpendicular scan # Integrate over (10) repeated records for the desired band ph0 = np.angle(np.sum(out0['x'][:,:,:,10*band:10*(band+1)],3)) ph1 = np.angle(np.sum(out1['x'][:,:,:,10*band:10*(band+1)],3)) # Determine secular change in phase at the two times, relative to ant 1 for i in antlist: if i == antlist[0]: dp = np.zeros_like(ph0[0,0]) else: dp = lobe(ph1[bl2ord[antlist[0],i],0] - ph0[bl2ord[antlist[0],i],0]) ph0[bl2ord[i,13],2:] = lobe(ph1[bl2ord[i,13],2:]-dp) # Insert crossed-feed phases from ph1 into ph0, corrected for secular change ph0 = ph0[bl2ord[antlist,13]] # Now restrict to only baselines with ant 14 fstart = (band+32)*0.325 + 1.1 - 0.025 fghz = np.linspace(fstart,fstart+0.400,4096) nf = len(fghz) dph = np.zeros((nant+1,nf),np.float) # Determine xi_rot xi2 = ph0[:,2] - ph0[:,0] + ph0[:,3] - ph0[:,1] # This is 2 * xi, measured separately on each of 13 antennas xi_rot = lobe(np.unwrap(np.angle(np.sum(np.exp(1j*xi2),0)))/2.) # Very clever average does not suffer from wrapping issues #xi_rot = np.zeros_like(xi_rot) + np.pi/2. # *********** Zero out xi_rot for now **************** # Form differential delay phase from channels, and average them # dph14 = XY - XX - xi_rot and YY - YX + xi_rot dph14 = np.concatenate((lobe(ph0[:,2] - ph0[:,0] - xi_rot),lobe(ph0[:,1] - ph0[:,3] + xi_rot))) # 26 values for Ant 14 dph[nant] = np.angle(np.sum(np.exp(1j*dph14),0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX + xi_rot and XY - YY - xi_rot dphi = np.array((lobe(ph0[:,0] - ph0[:,3] + xi_rot),lobe(ph0[:,2] - ph0[:,1] - xi_rot))) # 2 values for Ant 14 dph[:nant] = np.angle(np.sum(np.exp(1j*dphi),0)) if doplot: figlabel = 'XY_Phase_'+str(band+33) if figlabel in plt.get_figlabels(): f = plt.figure(figlabel) ax = f.get_axes() else: f, ax = plt.subplots(4, 4, num=figlabel) ax.shape = (16,) for i in range(nant): ax[antlist[i]].plot(fghz,dphi[0,i],',') ax[antlist[i]].plot(fghz,dphi[1,i],',') ax[antlist[i]].plot(fghz,dph[i],'k,') ax[antlist[i]].set_title('Ant '+str(antlist[i]+1),fontsize=9) for i in range(2*nant): ax[13].plot(fghz,dph14[i],',') ax[13].set_title('Ant 14',fontsize=9) ax[13].plot(fghz,dph[nant],'k,') for i in range(14): ax[i].set_ylim(-4,4) f.suptitle('Multicolor: Measurements, Black: Final Results') ax[14].plot(fghz,xi_rot) for i in range(nant): ax[15].plot(fghz,xi2[i],',') # np.savez('/common/tmp/Feed_rotation/' + npzlist[0].split('/')[-1][:14] + '_delay_phase.npz', fghz=fghz, dph=dph, xi_rot=xi_rot) time = Time.now().lv xy_phase = {'antlist':antlist, 'timestamp':time, 'fghz':fghz, 'xyphase':dph, 'xi_rot':xi_rot, 'dphi':dphi, 'dph14':dph14} return xy_phase
import read_idb as ri from util import lobe files = glob.glob('/data1/eovsa/fits/UDB/2018/UDB20180826*') files.sort() files = files[:20] out1o = ri.read_idb(files, navg=20) out2o = ri.read_idb(files[1:], navg=20) ph1 = np.angle(out1o['x'][ri.bl2ord[13, :13]]) ph2 = np.angle(out2o['x'][ri.bl2ord[13, :13]]) ph_1 = np.zeros((13, 2, 12, 13), np.float) ph_2 = np.zeros((13, 2, 12, 10), np.float) for i in range(13): for j in range(2): for k in range(12): if i == 0: ph_1[i, 0, k] = lobe(ph1[i, j, k] - ph1[i, j, k, 0] ) * out1o['fghz'][0] / out1o['fghz'][k] ph_2[i, 1, k] = lobe(ph2[i, j, k] - ph2[i, j, k, 0] ) * out2o['fghz'][0] / out2o['fghz'][k] else: ph_1[i, 0, k] = -lobe(ph1[i, j, k] - ph1[0, j, k] - ph1[i, j, k, 0] + ph1[0, j, k, 0]) * out1o['fghz'][0] / out1o['fghz'][k] ph_2[i, 1, k] = -lobe(ph2[i, j, k] - ph2[0, j, k] - ph2[i, j, k, 0] + ph2[0, j, k, 0]) * out2o['fghz'][0] / out2o['fghz'][k] # Mean over frequencies and polarizations, scaled by cos(dec) ph_1t = np.mean(np.mean(ph_1, 1), 1) / cos(out1o['dec']) ph_2t = np.mean(np.mean(ph_2, 1), 1) / cos(out2o['dec']) f, ax = plt.subplots(4, 4)
def graph(f, navg=None, path=None): import matplotlib.pyplot as plt from matplotlib.ticker import FormatStrFormatter import struct, time, glob, sys, socket import read_idb as ri import dbutil as db if navg is None: navg = 60 if path is None: path = '' out = ri.read_idb(f, navg=navg) if len(out['fghz']) == 0: # This file is no good, so skip it return fig, ax = plt.subplots(4, 13, sharex=True, sharey=True) trange = Time( [fname2mjd(f[0]), fname2mjd(f[-1]) + ten_minutes], format='mjd') # ************ This block commented out due to loss of SQL ************** # times, wscram, avgwind = db.a14_wscram(trange) # nwind = len(wscram) # nbad = np.sum(wscram) nbad = 0 # Skip Windscram check if nbad != 0: warn = ' --> Windscram! (' + str(nbad) + ' of ' + str(nwind) + ')' color = '#d62728' # Plot points with "warning" Red color else: warn = '' color = '#1f77b4' # Plot points with "normal" Blue color fig.set_size_inches(18, 6) nf = len(out['fghz']) fstr = str(out['fghz'][nf / 2] * 1000)[:5] + ' MHz ' for k in range(13): for j in range(4): ax[j, k].cla() ax[j, k].plot(out['ha'], np.angle(out['x'][ri.bl2ord[k, 13], j, nf / 2]), '.', color=color) ax[j, k].set_ylim(-4, 4) ax[j, k].xaxis.set_major_formatter(FormatStrFormatter('%.2f')) if k in range(1, 13): ax[j, k].yaxis.set_visible(False) if j in range(3): ax[j, k].xaxis.set_visible(False) if j == 0: ax[0, k].title.set_text('antenna %d' % (k + 1)) fig.suptitle(out['source'] + ' ' + Time(out['time'][0], format='jd').iso[:19] + ' UT ' + fstr + warn) ax[0, 0].set_ylabel('XX Phase') ax[1, 0].set_ylabel('YY Phase') ax[2, 0].set_ylabel('XY Phase') ax[3, 0].set_ylabel('YX Phase') fig.text(0.5, 0.04, 'Hour Angle', ha='center') t = Time(out['time'][0], format='jd').iso[:19].replace('-', '').replace(':', '').replace(' ', '') s = out['source'] ofile = path + t[:14] + '_' + s + '.npz' np.savez(open(ofile, 'wb'), out=out) plt.savefig(path + 'pcT' + t + '_' + s + '.png', bbox_inches='tight') plt.close(fig) ph = np.angle(np.sum(out['x'], 3)) fig, ax = plt.subplots(4, 13) fig.set_size_inches(18, 6) for k in range(13): for j in range(4): ax[j, k].cla() if k == 0: ax[j, k].plot(out['fghz'], ph[ri.bl2ord[k, 13], j], '.', color=color) else: ax[j, k].plot(out['fghz'], lobe(ph[ri.bl2ord[k, 13], j] - ph[ri.bl2ord[0, 13], j]), '.', color=color) ax[j, k].set_ylim(-4, 4) ax[j, k].xaxis.set_major_formatter(FormatStrFormatter('%.2f')) if k in range(1, 13): ax[j, k].yaxis.set_visible(False) if j in range(3): ax[j, k].xaxis.set_visible(False) if j == 0: ax[0, k].title.set_text('antenna %d' % (k + 1)) fig.suptitle(out['source'] + ' ' + Time(out['time'][0], format='jd').iso[:19] + ' UT' + warn) ax[0, 0].set_ylabel('XX Phase') ax[1, 0].set_ylabel('YY Phase') ax[2, 0].set_ylabel('XY Phase') ax[3, 0].set_ylabel('YX Phase') fig.text(0.5, 0.04, 'Frequency[GHz]', ha='center') t = Time(out['time'][0], format='jd').iso[:19].replace('-', '').replace(':', '').replace(' ', '') plt.savefig(path + 'pcF' + t + '_' + s + '.png', bbox_inches='tight') plt.close(fig)
def get_xy_corr(npzlist=None, doplot=True): ''' Analyze a pair of parallel and cross polarization calibration scans and return the X vs. Y delay phase corrections on all antennas 1-14. Required keyword: npzlist a list of 2 NPZ filenames, the first being the parallel-feed scan, and the second being the crossed-feed scan. Optional keyword: doplot True => plot the final result, False => no plot ''' if npzlist is None: print 'Must provide a list of 2 NPZ files.' return None, None import read_idb as ri import numpy as np from util import lobe if doplot: import matplotlib.pylab as plt out0 = ri.read_npz([npzlist[0]]) # Parallel scan out1 = ri.read_npz([npzlist[1]]) # Perpendicular scan ph0 = np.angle(np.sum(out0['x'][ri.bl2ord[:13, 13]], 3)) ph1 = np.angle(np.sum(out1['x'][ri.bl2ord[:13, 13]], 3)) ph0[:, 2:] = ph1[:, 2:] # Insert crossed-feed phases from ph1 into ph0 fghz = out0['fghz'] nf = len(fghz) dph = np.zeros((14, nf), np.float) # Form differential delay phase from channels, and average them # dph14 = XY - XX and YY - YX + pi dph14 = np.concatenate( (lobe(ph0[:, 2] - ph0[:, 0] + np.pi / 2), lobe(ph0[:, 1] - ph0[:, 3] - np.pi / 2))) # 26 values for Ant 14 dph[13] = np.angle( np.sum(np.exp(1j * dph14), 0)) # Very clever average does not suffer from wrapping issues # dphi = XX - YX and XY - YY + pi dphi = np.array( (lobe(ph0[:, 0] - ph0[:, 3] - np.pi / 2), lobe(ph0[:, 2] - ph0[:, 1] + np.pi / 2))) # 2 values for Ant 14 dph[:13] = np.angle(np.sum(np.exp(1j * dphi), 0)) if doplot: f, ax = plt.subplots(4, 4) ax.shape = (16, ) for i in range(13): ax[i].plot(fghz, dphi[0, i], '.') ax[i].plot(fghz, dphi[1, i], '.') ax[i].plot(fghz, dph[i], 'k.') for i in range(26): ax[13].plot(fghz, dph14[i], '.') ax[13].plot(fghz, dph[13], 'k.') for i in range(14): ax[i].set_ylim(-4, 4) f.suptitle('Multicolor: Measurements, Black: Final Results') np.savez('/common/tmp/Feed_rotation/' + npzlist[0].split('/')[-1][:14] + '_delay_phase.npz', fghz=fghz, dph=dph) return dph
def xydelay_anal(npzfiles, fix_tau_lo=None): ''' Analyze a "standard" X vs. Y delay calibration, consisting of four observations on a strong calibrator near 0 HA, in the order: 90-degree Low-frequency receiver, 90-degree High-frequency receiver, 0-degree High-frequency receiver, 0-degree Low-frequency receiver It has happened that the low-frequency receiver delays were not set for one of the observation sets. This can be fixed by reading the delay-center table for the relevant date and applying a phase correction according to the delay difference. Setting fix_tau_lo to "first" means correct first scan, and to "last" means correct last scan. ''' import matplotlib.pylab as plt from util import common_val_idx npzfiles = np.array(npzfiles) out = [] for file in npzfiles: out.append(ri.read_npz([file])) out = np.array(out) if fix_tau_lo != None: # Correct for low-frequency delay error, if requested import cal_header as ch from stateframe import extract if fix_tau_lo == 'first': icorr=0 elif fix_tau_lo == 'last': icorr=3 else: print 'Invalid value for fix_tau_lo. Must be "first" or "last"' return xml, buf = ch.read_cal(4,t=Time(out[icorr]['time'][0],format='jd')) dlatbl = extract(buf,xml['Delaycen_ns']) dtau_x, dtau_y = dlatbl[14] - dlatbl[13] dp_x = out[icorr]['fghz']*2*np.pi*dtau_x dp_y = out[icorr]['fghz']*2*np.pi*dtau_y nt, = out[icorr]['time'].shape for i in range(nt): for iant in range(13): out[icorr]['x'][ri.bl2ord[iant,13],0,:,i] *= np.exp(1j*dp_x) out[icorr]['x'][ri.bl2ord[iant,13],1,:,i] *= np.exp(1j*dp_y) out[icorr]['x'][ri.bl2ord[iant,13],2,:,i] *= np.exp(1j*dp_y) out[icorr]['x'][ri.bl2ord[iant,13],3,:,i] *= np.exp(1j*dp_x) dph_lo = get_xy_corr(out[[3,0]], doplot=False) dph_hi = get_xy_corr(out[[2,1]]) fghz = np.union1d(dph_lo['fghz'],dph_hi['fghz']) # Check for LO and HI being off by pi due to pi-ambiguity in xi_rot lo_com, hi_com = common_val_idx(dph_lo['fghz'],dph_hi['fghz']) # Average xi_rot angle difference over common frequencies a = lobe(dph_hi['xi_rot'][hi_com]-dph_lo['xi_rot'][lo_com]) # angle difference xi_rot_diff = np.angle(np.sum(np.exp(1j*a))) # Average angle difference if np.abs(xi_rot_diff) > np.pi/2: # Looks like shifting by pi will get us closer, so shift both xyphase and xi_rot # This does not actually change any phase relationships, it only makes the plots # and HI/LO data comparison more consistent. dph_lo['xyphase'] += np.pi dph_lo['xi_rot'] += np.pi dph_lo['dphi'] += np.pi dph_lo['dph14'] += np.pi ax = plt.figure('XY_Phase').get_axes() for i in range(13): ax[i].plot(dph_lo['fghz'], lobe(dph_lo['dphi'][0,i]), '.',color='C0') ax[i].plot(dph_lo['fghz'], lobe(dph_lo['dphi'][1,i]), '.',color='C1') ax[i].plot(dph_lo['fghz'],lobe(dph_lo['xyphase'][i]),'r.') ax[i].set_xlim(0,20) for i in range(26): ax[13].plot(dph_lo['fghz'],lobe(dph_lo['dph14'][i]),'.') ax[13].plot(dph_lo['fghz'],lobe(dph_lo['xyphase'][13]),'r.') nf, = fghz.shape flo_uniq = np.setdiff1d(dph_lo['fghz'],dph_hi['fghz']) # List of frequencies in LO not in HI idx_lo_not_hi, idx2 = common_val_idx(fghz, flo_uniq) # List of indexes of unique LO frequencies # Make empty arrays with enough frequencies xyphase = np.zeros((14,nf),dtype=float) xi_rot = np.zeros((nf),dtype=float) idx_hi, idx2 = common_val_idx(fghz,dph_hi['fghz']) # List of indexes of HI receiver frequencies xyphase[:14,idx_hi] = dph_hi['xyphase'] # Insert all high-receiver xyphases xyphase[:14,idx_lo_not_hi] = dph_lo['xyphase'][:14,idx_lo_not_hi] # For unique low-receiver frequencies, insert LO xyphases xi_rot[idx_hi] = dph_hi['xi_rot'] # Insert all high-receiver xi_rot xi_rot[idx_lo_not_hi] = lobe(dph_lo['xi_rot'][idx_lo_not_hi]) # For unique low-receiver frequencies, insert LO xi_rot ax[14].plot(fghz,xi_rot) dph_hi.update({'xi_rot':xi_rot, 'xyphase':xyphase, 'fghz':fghz}) print 'Referring to the output of this routine as "xyphase,"' print 'run cal_header.xy_phasecal2sql(xyphase) to write the SQL record.' return dph_hi