def compute(self, freq, time, ra=None, dec=None): """ Compute UVW for a given time with an array phased towards (ra, dec). If ra and dec are None, local zenith is assumed. If uvw has already been calculated, the new values are stacked to the previous ones, as for a tracking. Parameters ---------- ra : float Right Ascension in degrees dec : float Declination in degrees time : `astropy.time.Time` Time at which observation happened freq : float Frequency in MHz Returns ------- self.uvw : `np.ndarray` Shape (time, freq, nant*nant, 3) """ if (ra is None) and (dec is None): ra, dec = eq_zenith(time=time, location=self.instru.array_position) self.wavel = wavelength(freq) uvw = np.zeros((1, self.wavel.size, self.bsl.shape[0], 3)) # Prepare the transformation matrices rot_cel = self._celestial() rot_uvw = self._uvwplane(time=time, ra=ra, dec=dec) # Initialize the UVW array uvw = np.zeros((1, self.instru.n_ant, self.instru.n_ant, 3)) # Perform UVW computation for each baseline and frequency xi = self.instru.tri_x yi = self.instru.tri_y dpos = self.positions[xi] - self.positions[yi] xyz = rot_cel * np.matrix(dpos).T uvw_k = rot_uvw * xyz uvw[0, xi, yi, :] = uvw_k.T uvw[0, yi, xi, :] = -uvw_k.T # uvw = uvw[:, np.newaxis, ...] /\ # self.wavel[:, np.newaxis, np.newaxis, np.newaxis] uvw = uvw[:, np.newaxis, ...] # Stack the UVW coordinates by time if self.uvw is None: self.uvw = uvw else: self.uvw = np.vstack((self.uvw, uvw)) return
def freq(self, f): if f is not None: if isinstance(f, np.ndarray): pass elif isinstance(f, list): f = np.array(f) else: f = np.array([f]) self._freq = f self.wavel = wavelength(f) else: self._freq = None return
def _uvplot(self, freq=None): """ Return UV for plotting purposes """ if freq is None: fid = 0 else: fid = np.argmin(np.abs(self.wavel - wavelength(freq))) uvw_f = self.uvw[:, fid, ...] # dont show autocorrelations mask = (uvw_f[..., 0] != 0.) & (uvw_f[..., 1] != 0.) u = uvw_f[..., 0][mask] v = uvw_f[..., 1][mask] #u = np.hstack((-u, u)) #v = np.hstack((-v, v)) return u, v
def uvw_lambda(self, frequencies): """ """ if self.uvw is None: raise Exception('Run compute() before.') if self._uvw.shape[1] != 1: warnings.warn('uvw_lambda() has previously been computed.') return if isinstance(frequencies, (int, np.integer))\ or isinstance(frequencies, (float, np.inexact)): frequencies = [frequencies] if not isinstance(frequencies, np.ndarray): frequencies = np.array(frequencies) self.wavel = wavelength(frequencies) self._uvw = self._uvw / self.wavel[np.newaxis, :, np.newaxis, np.newaxis, np.newaxis] return
def wave_vector(self, frequency): """ Compute the wave vector k = 2 pi / lambda """ k = 2. * np.pi / wavelength(frequency) return k
def make_nearfield(self, filename='', radius=300, npix=128): """ :param radius: Radius of nearfield in meters :type radius: float, optional :param npix: Size in pixels of the image :type npix: int, optional """ import matplotlib.pyplot as plt from matplotlib.colorbar import ColorbarBase from matplotlib.ticker import LinearLocator from matplotlib.colors import Normalize from matplotlib.cm import get_cmap from mpl_toolkits.axes_grid1.inset_locator import inset_axes antpos = self.array.ma_enu.copy() # Compute the delays at the ground z = np.average(antpos[:, 2]) + 1 x = np.linspace(-radius, radius, npix) y = np.linspace(-radius, radius, npix) posx, posy = np.meshgrid(x, y) delay = (antpos[:, 0][:, None, None] - posx[None]) ** 2 \ + (antpos[:, 1][:, None, None] - posy[None]) ** 2 \ + (antpos[:, 2][:, None, None] - z) ** 2 delay = np.sqrt(delay) # Compute delays between antenna pairs k = 0 nant = self.array.antennas.size dd = np.zeros((int(nant * (nant - 1) / 2 + nant), npix, npix)) for i in range(nant): for j in range(i, nant): dd[k] = np.array([delay[j] - delay[i]]) k += 1 # Multi-frequencies vis = np.mean(self.vis, axis=0) # mean in time vis_i = 0.5 * (vis[..., 0] + vis[..., 3]) j2pi = 2.j * np.pi nearfield = 0 n = vis_i.shape[0] for j in range(n): v = vis_i[j, self.array.tri_x, self.array.tri_y][:, None, None] d = dd[None, ...] lamb = wavelength(self.freq[j]) # Multiproc h = v * mp_expo_nf(npix, self.ncpus, d, lamb) # Slow # h = ne.evaluate('v*exp(j2pi*d/lamb)') nearfield += h nearfield = np.sum(np.abs(nearfield[0] / len(self.freq)), axis=0) # /mean # Plot loc_buildings_enu = np.array([[27.75451691, -51.40993459, 7.99973228], [20.5648047, -59.79299576, 7.99968629], [167.86485612, 177.89170175, 7.99531119]]) fig, ax = plt.subplots(figsize=(10, 10)) nearfield_db = 10 * np.log10(nearfield) ax.imshow(np.fliplr(nearfield_db), cmap='YlGnBu_r', extent=[-radius, radius, -radius, radius]) # Colorbar cax = inset_axes( ax, width='5%', height='100%', loc='lower left', bbox_to_anchor=(1.05, 0., 1, 1), bbox_transform=ax.transAxes, borderpad=0, ) cb = ColorbarBase(cax, cmap=get_cmap(name='YlGnBu_r'), orientation='vertical', norm=Normalize(vmin=np.min(nearfield_db), vmax=np.max(nearfield_db)), ticks=LinearLocator()) cb.solids.set_edgecolor('face') cb.set_label('dB') cb.formatter.set_powerlimits((0, 0)) # Array info ax.scatter(self.array.ma_enu[:, 0], self.array.ma_enu[:, 1], color='tab:red') ax.scatter(loc_buildings_enu[:, 0], loc_buildings_enu[:, 1], color='tab:orange') for i in range(self.array.ant_names.size): ax.text(self.array.ma_enu[i, 0], self.array.ma_enu[i, 1], ' ' + self.array.ant_names[i].replace('MR', '')) ax.set_xlabel(r'$\Delta x$ (m)') ax.set_ylabel(r'$\Delta y$ (m)') obstime = self.cross.time[0] + (self.cross.time[-1] - self.cross.time[0]) / 2. ax.set_title('{}'.format(obstime.isot)) plt.savefig(filename, dpi=300, bbox_inches='tight') return
def make_psf(self, npix=None, fi=None): """ Make the PSF regarding the UV distribution :param npix: Size in pixels of the image :type npix: int, optional :param fi: Frequency index :type fi: int, optional """ from astropy.modeling.models import Gaussian2D from astropy.modeling.fitting import LevMarLSQFitter print('\tMaking PSF') if fi is None: fi = self._fi if npix is None: npix = self.npix * 2 na = np.newaxis # Average over time uvw = np.mean(self.uvw, axis=0) # Flatten the array uvw = uvw[:, self.array.tri_y, self.array.tri_x, :] # Transform UVW in lambdas units, take fi frequency uvw = uvw[fi, ...] / wavelength(self.freq[fi, na, na]) # Prepare l, m grid lm_psf = np.cos(np.radians(90 - self.fov)) l = np.linspace(-lm_psf, lm_psf, npix) m = np.linspace(lm_psf, -lm_psf, npix) lg, mg = np.meshgrid(l, m) # Make the TF of UV distribution u = uvw[..., 0][:, na, na] v = uvw[..., 1][:, na, na] # Slow # expo = np.exp( # 2j * np.pi * (u * lg[na, ...] + v * mg[na, ...]) # ) # Faster # lg = lg[na, ...] # mg = mg[na, ...] # pi = np.pi # expo = ne.evaluate('exp(2j*pi*(u*lg+v*mg))') # psf = np.real( # np.mean( # expo, # axis=0 # ) # ) # Mutliprocessing expo = mp_expo(npix, self.ncpus, lg, mg, u, v) psf = np.real(np.mean(expo, axis=0)) self.psf = psf / psf.max() # Get clean beam print('\tComputing clean beam') # Put most of the PSF to 0 to help the fit simple_psf = self.psf.copy() simple_psf[self.psf <= np.std(self.psf)] = 0 nsize = int(simple_psf.shape[0]) fit_init = Gaussian2D(amplitude=1, x_mean=npix / 2, y_mean=npix / 2, x_stddev=0.2, y_stddev=0.2) fit_algo = LevMarLSQFitter() yi, xi = np.indices(simple_psf.shape) gaussian = fit_algo(fit_init, xi, yi, simple_psf) clean_beam = gaussian(xi, yi) clean_beam /= clean_beam.max() self.clean_beam = clean_beam[int(npix / 2 / 2):int(npix / 2 * 3 / 2), int(npix / 2 / 2):int(npix / 2 * 3 / 2)] return
def plot_dirty(self, output='plot', altazdir=None, **kwargs): """ """ obstime = self.cross.time[0] + (self.cross.time[-1] - self.cross.time[0]) / 2. if altazdir is not None: # Find pointing information to plot the beam from glob import glob from astropy.time import Time # Get hold of the pointing files pointings = sorted(glob(join(altazdir, '*.altazA'))) # Parse the pointings time = [] azimuth = [] elevation = [] for pointing in pointings: try: t, b, az, el, az1, el1, f, el2 = np.loadtxt(fname=pointing, dtype=str, comments=';', unpack=True) except: # TESTANT-type files... t, b, az, el, f, el2 = np.loadtxt(fname=pointing, dtype=str, comments=';', unpack=True) time += t.tolist() azimuth += az.tolist() elevation += el.tolist() time = Time(np.array(time)) azimuth = np.array(azimuth).astype(float) elevation = np.array(elevation).astype(float) # Remove unused files and keed the last one for pointing in sorted(pointings)[:-1]: remove(pointing) # Mini-Array beam width primarybeam = np.degrees(wavelength(np.mean(self.freq)) / 25) # Where NenuFAR was aiming ti = np.max(np.where((time - obstime).sec <= 0)[0]) ra_point, dec_point = to_radec(alt=elevation[ti], az=azimuth[ti], time=obstime) circle = (ra_point, dec_point, primarybeam) else: circle = None astroplt = AstroPlot(image=np.real(np.flipud(self.dirty)), center=self.phase_center, resol=self.fov / self.npix) if output.lower() == 'plot': astroplt.plot(title='{}'.format(obstime.isot), cblabel='Amplitude', sources=True, time=obstime, circle=circle, **kwargs) elif output.endswith('.png'): output = abspath(output) if isdir(dirname(output)): astroplt.plot(title='{}'.format(obstime.isot), cblabel='Amplitude', sources=True, time=obstime, filename=output, circle=circle, **kwargs) else: pass return
def make_dirty(self, npix=256, fi=0, data='data', **kwargs): """ Make a dirty image from the visibilities :param npix: Size in pixels of the image :type npix: int, optional :param fi: Frequency index :type fi: int, optional :param data: Data type to be imaged, choose between `'data'`, `'corrected'` and `'predicted'`. :type data: str, optional :param output: Type of output `'plot'`, `'array'` and `'savefile'` (in .png). :type output: str, optional """ self._selection(**kwargs) if fi >= self.freq.size: raise IndexError('Frequency index exceeds {}'.format( self.freq.size)) self.npix = npix self._fi = fi na = np.newaxis # Average over time if data.lower == 'corrected': if not hasattr(self, 'corrected'): self.calibrate(fi=fi) vis = np.mean(self.corrected, axis=0) elif data.lower() == 'predicted': if not hasattr(self, 'predicted'): self.predict() vis = np.mean(self.predicted, axis=0) else: vis = np.mean(self.vis, axis=0) uvw = np.mean(self.uvw, axis=0) # Flatten the arrays vis = vis[:, self.array.tri_y, self.array.tri_x, :] uvw = uvw[:, self.array.tri_y, self.array.tri_x, :] # Get Stokes I vis = 0.5 * (vis[..., 0] + vis[..., 3]) # # Transform UVW in lambdas units, take fi frequency # uvw = uvw[fi, ...] / wavelength(self.freq[fi, na, na]) # # Prepare l, m grid # l = np.linspace(-self.lmax, self.lmax, npix) # m = np.linspace(self.mmax, -self.mmax, npix) # lg, mg = np.meshgrid(l, m) # # Make the TF of Visbilities # print('\tMaking dirty image') # u = uvw[:, 0][:, na, na] # v = uvw[:, 1][:, na, na] # # Slow: # # self.dirty = np.zeros([npix, npix], dtype=np.complex64) # # expo = np.exp( # # 2j * np.pi * (u * lg[na, ...] + v * mg[na, ...]) # # ) # # Faster : # # self.dirty = np.zeros([npix, npix], dtype=np.complex64) # # lg = lg[na, ...] # # mg = mg[na, ...] # # pi = np.pi # # expo = ne.evaluate('exp(2j*pi*(u*lg+v*mg))') # # self.dirty += np.mean( # # vis[fi, :, na, na] * expo, # # axis=0 # # ) # # Mutliprocessing # expo = mp_expo(self.npix, self.ncpus, lg, mg, u, v) # self.dirty = np.mean( # vis[fi, :, na, na] * expo, # axis=0 # ) # Transform UVW in lambdas units, take fi frequency uvw = uvw / wavelength(self.freq[:, na, na]) # Prepare l, m grid l = np.linspace(-self.lmax, self.lmax, npix) m = np.linspace(self.mmax, -self.mmax, npix) lg, mg = np.meshgrid(l, m) # Make the TF of Visbilities print('\tMaking dirty image') self.dirty = 0 for fi in range(self.freq.size): u = uvw[fi, :, 0][:, na, na] v = uvw[fi, :, 1][:, na, na] # Slow: # self.dirty = np.zeros([npix, npix], dtype=np.complex64) # expo = np.exp( # 2j * np.pi * (u * lg[na, ...] + v * mg[na, ...]) # ) # Faster : # self.dirty = np.zeros([npix, npix], dtype=np.complex64) # lg = lg[na, ...] # mg = mg[na, ...] # pi = np.pi # expo = ne.evaluate('exp(2j*pi*(u*lg+v*mg))') # self.dirty += np.mean( # vis[fi, :, na, na] * expo, # axis=0 # ) # Mutliprocessing expo = mp_expo(self.npix, self.ncpus, lg, mg, u, v) self.dirty += np.mean(vis[fi, :, na, na] * expo, axis=0) / self.freq.size break # only compute image on first frequency return