def test_hocoord(): # Float and string inputs altaz = ho_coord(alt=90, az=180, time='2020-04-01 12:00:00') # Quantity and Time inputs altaz = ho_coord(alt=90 * u.deg, az=180 * u.deg, time=Time('2020-04-01 12:00:00')) assert isinstance(altaz, AltAz) assert altaz.az.deg == 180.0 assert altaz.alt.deg == 90.0
def beam_values(self, coords, time): """ :param coords: :type coords: :class:`~astropy.coordinates.ICRS` or :class:`~astropy.coordinates.SkyCoord` :param time: :type time: :class:`~astropy.time.Time` """ if not isinstance(coords, (ICRS, SkyCoord)): raise TypeError('coords should be ICRS or SkyCoord object') if not isinstance(time, Time): raise TypeError('time should be Time object') # Real pointing if self.beamsquint: el = desquint_elevation(elevation=self.elana, opt_freq=self.squint_freq) else: el = self.elana.copy() az, el = analog_pointing(self.azana, el) log.info('Effective analog pointing=({}, {})'.format(az, el)) # Array factor phase_center = ho_coord(az=az, alt=el, time=time) altazcoords = toAltaz(skycoord=coords, time=time) arrfac = self.array_factor(phase_center=phase_center, coords=altazcoords, antpos=ma_antpos(rot=self._rot)) # Antenna Gain antgain = self.ant_gain(coords=coords, time=time) anagain = arrfac * antgain log.info('Anabeam (rot {}) computed for {} pixels.'.format( self._rot % 60, anagain.size)) return anagain
def test_toradec(): with pytest.raises(TypeError): radec = to_radec(1.) altaz = ho_coord(alt=90, az=180, time='2020-04-01 12:00:00') radec = to_radec(altaz) assert isinstance(radec, ICRS) assert radec.ra.deg == pytest.approx(12.22, 1e-2) assert radec.dec.deg == pytest.approx(47.27, 1e-2)
def beam_values(self, coords, time): """ :param coords: :type coords: :class:`~astropy.coordinates.ICRS` or :class:`~astropy.coordinates.SkyCoord` :param time: :type time: :class:`~astropy.time.Time` """ if not isinstance(coords, (ICRS, SkyCoord)): raise TypeError('coords should be ICRS or SkyCoord object') if not isinstance(time, Time): raise TypeError('time should be Time object') # Build the Mini-Array 'summed' response abeams = {} for ma in self.ma: rot = ma_info['rot'][ma_info['ma'] == ma][0] if str(rot % 60) not in abeams.keys(): ana = ABeam(freq=self.freq, polar=self.polar, azana=self.azana, elana=self.elana, ma=ma) ana.beamsquint = self.beamsquint anavals = ana.beam_values(coords=coords, time=time) abeams[str(rot % 60)] = anavals.copy() if not 'summa' in locals(): summa = abeams[str(rot % 60)] else: summa += abeams[str(rot % 60)] # Array factor phase_center = ho_coord(az=self.azdig, alt=self.eldig, time=time) altazcoords = toAltaz(skycoord=coords, time=time) arrfac = self.array_factor(phase_center=phase_center, coords=altazcoords, antpos=ma_pos[np.isin( ma_info['ma'], self.ma)]) # Arrayfactor * summed MA response digigain = arrfac * summa log.info('Digibeam computed for {} pixels.'.format(digigain.size)) return digigain
def array_factor(self, az, el, antpos, freq): r""" Computes the array factor :math:`\mathcal{A}` (i.e. the far-field radiation pattern obtained for an array of :math:`n_{\rm ant}` radiators). .. math:: \mathcal{A} = \left| \sum_{n_{\scriptscriptstyle \rm ant}} e^{2 \pi i \frac{\nu}{c} (\varphi_0 - \varphi)} \right|^2 .. math:: \varphi = \underset{\scriptstyle n_{\scriptscriptstyle \rm ant}\, \times\, 3}{\mathbf{P}_{\rm ant}} \cdot \pmatrix{ \cos(\phi)\cos(\theta)\\ \sin(\phi)\cos(\theta)\\ \sin(\theta) } .. math:: \varphi_0 = \underset{\scriptstyle n_{\scriptscriptstyle \rm ant}\, \times\, 3}{\mathbf{P}_{\rm ant}} \cdot \pmatrix{ \cos(\phi_0)\cos(\theta_0)\\ \sin(\phi_0)\cos(\theta_0)\\ \sin(\theta_0) } :math:`\mathbf{P}_{\rm ant}` is the antenna position matrix, :math:`\phi` and :math:`\theta` are the sky local coordinates (azimuth and elevation respectively) gridded on a HEALPix representation, whereas :math:`\phi_0` and :math:`\theta_0` are the pointing direction in local coordinates. :param az: Pointing azimuth (in degrees if `float`) :type az: `float` or :class:`~astropy.units.Quantity` :param el: Pointing elevation (in degrees if `float`) :type el: `float` or :class:`~astropy.units.Quantity` :param antpos: Antenna positions shaped as (n_ant, 3) :type antpos: :class:`~numpy.ndarray` :param freq: Frequency (in MHz if `float`) :type freq: `float` or :class:`~astropy.units.Quantity` :returns: Array factor :rtype: :class:`~numpy.ndarray` """ def get_phi(az, el, antpos): """ az, el in radians """ xyz_proj = np.array( [np.cos(az) * np.cos(el), np.sin(az) * np.cos(el), np.sin(el)]) antennas = np.array(antpos) phi = np.dot(antennas, xyz_proj) return phi if not isinstance(az, u.Quantity): az *= u.deg if not isinstance(el, u.Quantity): el *= u.deg self.phase_center = to_radec(ho_coord(az=az, alt=el, time=self.time)) phi0 = get_phi(az=[az.to(u.rad).value], el=[el.to(u.rad).value], antpos=antpos) phi_grid = get_phi(az=self.ho_coords.az.rad, el=self.ho_coords.alt.rad, antpos=antpos) nt = ne.set_num_threads(ne._init_num_threads()) delay = ne.evaluate('phi_grid-phi0') coeff = 2j * np.pi / wavelength(freq).value if self.ncpus == 1: # Normal af = ne.evaluate('sum(exp(coeff*delay),axis=0)') # elif self.ncpus == 'numba': # af = perfcompute(coeff * delay) else: # Multiproc af = np.sum(mp_expo(self.ncpus, coeff, delay), axis=0) #return np.abs(af * af.conjugate()) return np.real(af * af.conjugate())
def plotPointing(altaza=None, altazb=None, sourceName=None): """ """ fig, axs = plt.subplots(2 if sourceName is None else 3, 1, sharex=True, figsize=(15, 10)) fig.subplots_adjust(hspace=0) if altaza is not None: if not altaza.endswith('.altazA'): raise TypeError('Wrong file format for altaza.') title = basename(altaza).replace('.altazA', '') aza = readPointing(altaza) azaTime = Time(aza['time']) tmin = azaTime[0] tmax = azaTime[-1] for abeam in np.unique(aza['anabeam']): mask = aza['anabeam'] == abeam axs[0].plot( azaTime[mask] [:-1].datetime, # Do not show last pointing / back to zenith aza['az'][mask][:-1], linewidth='5.5', label=f'Analog requested (#{abeam})') axs[0].plot(azaTime[mask][:-1].datetime, aza['az_cor'][mask][:-1], linewidth='3', label=f'Analog corrected (#{abeam})') axs[1].plot(azaTime[mask][:-1].datetime, aza['el'][mask][:-1], linewidth='5.5', label=f'Analog requested (#{abeam})') axs[1].plot(azaTime[mask][:-1].datetime, aza['el_cor'][mask][:-1], linewidth='3', label=f'Analog corrected (#{abeam})') axs[1].plot(azaTime[mask][:-1].datetime, aza['el_eff'][mask][:-1], linewidth='2', label=f'Analog beamsquint corrected (#{abeam})') if altazb is not None: if not altazb.endswith('.altazB'): raise TypeError('Wrong file format for altazb.') title = basename(altazb).replace('.altazB', '') azb = readPointing(altazb) azbTime = Time(azb['time']) tmin = azbTime[0] tmax = azbTime[-1] for dbeam in np.unique(azb['digibeam']): mask = azb['digibeam'] == dbeam axs[0].plot(azbTime[mask].datetime, azb['az'][mask], linewidth='1', label=f'Numeric (#{dbeam})') axs[1].plot(azbTime[mask].datetime, azb['el'][mask], linewidth='1', label=f'Numeric (#{dbeam})') if (altaza is None) and (altazb is None): raise ValueError('At least one pointing file should be given.') if sourceName is not None: srcTime, srcAz, srcEl = altazProfile(sourceName=sourceName, tMin=tmin, tMax=tmax, dt=(tmax - tmin) / 20. - TimeDelta(0.5, format='sec')) axs[0].plot(srcTime.datetime, srcAz, color='black', linestyle='--', alpha=0.8, label=f'{sourceName}') axs[1].plot(srcTime.datetime, srcEl, color='black', linestyle='--', alpha=0.8, label=f'{sourceName}') if altaza is not None: for abeam in np.unique(aza['anabeam']): mask = aza['anabeam'] == abeam analog = ho_coord(alt=aza['el'][mask][:-1], az=aza['az'][mask][:-1], time=azaTime[mask][:-1]) analog_cor = ho_coord(alt=aza['el_cor'][mask][:-1], az=aza['az_cor'][mask][:-1], time=azaTime[mask][:-1]) analog_bscor = ho_coord(alt=aza['el_eff'][mask][:-1], az=aza['az_cor'][mask][:-1], time=azaTime[mask][:-1]) source = toAltaz(skycoord=getSource(name=sourceName, time=azaTime[mask][:-1]), time=azaTime[mask][:-1]) axs[2].plot(azaTime[mask][:-1].datetime, source.separation(analog).deg, label=f'Analog (#{abeam})') axs[2].plot(azaTime[mask][:-1].datetime, source.separation(analog_cor).deg, label=f'Analog corrected (#{abeam})') axs[2].plot(azaTime[mask][:-1].datetime, source.separation(analog_bscor).deg, label=f'Analog beamsquint corrected (#{abeam})') if altazb is not None: for dbeam in np.unique(azb['digibeam']): mask = azb['digibeam'] == dbeam numeric = ho_coord(alt=azb['el'][mask], az=azb['az'][mask], time=azbTime[mask]) source = toAltaz(skycoord=getSource(name=sourceName, time=azbTime[mask]), time=azbTime[mask]) axs[2].plot(azbTime[mask].datetime, source.separation(numeric).deg, label=f'Numeric (#{dbeam})') axs[2].set_ylabel(f'Separation from {sourceName} (deg)') axs[2].legend() axs[2].set_xlabel(f'UTC Time (since {tmin.iso})') else: axs[1].set_xlabel(f'UTC Time (since {tmin.iso})') axs[0].set_title('Pointing - ' + title) axs[0].set_ylabel('Azimuth (deg)') axs[0].legend() axs[1].set_ylabel('Elevation (deg)') axs[1].legend() plt.show()