def test_altaz_to_radec(): radec_fast = altaz_to_radec(altaz=SkyCoord( 300, 45, unit="deg", frame=AltAz(obstime=Time("2022-01-01T12:00:00"), location=nenufar_position)), fast_compute=True) assert isinstance(radec_fast, SkyCoord) assert radec_fast.ra.deg == pytest.approx(212.967, 1e-3) assert radec_fast.dec.deg == pytest.approx(49.440, 1e-3) radec_slow = altaz_to_radec(altaz=SkyCoord( 300, 45, unit="deg", frame=AltAz(obstime=Time("2022-01-01T12:00:00"), location=nenufar_position)), fast_compute=False) assert isinstance(radec_slow, SkyCoord) assert radec_slow.ra.deg == pytest.approx(212.764, 1e-3) assert radec_slow.dec.deg == pytest.approx(49.546, 1e-3) radec_array = altaz_to_radec(altaz=SkyCoord( [100, 300], [45, 45], unit="deg", frame=AltAz(obstime=Time("2022-01-01T12:00:00"), location=nenufar_position)), fast_compute=True) assert radec_array.shape == (2, )
def compute_nenufar_tv(self, analog_pointing_file: str = None, **kwargs): """ """ obs_time = self.time[0] + (self.time[-1] - self.time[0])/2 fov_radius = kwargs.get("fov_radius", 27*u.deg) resolution = kwargs.get("resolution", 0.5*u.deg) stokes = kwargs.get("stokes", "I") if analog_pointing_file is None: phase_center_altaz = SkyCoord( 0, 90, unit="deg", frame=AltAz( obstime=obs_time, location=nenufar_position ) ) else: pointing = Pointing.from_file( analog_pointing_file, include_corrections=False )[obs_time.reshape((1,))] phase_center_altaz = pointing.custom_ho_coordinates[0] data = self.get_stokes(stokes) sky_image = data.make_image( resolution=resolution, fov_radius=fov_radius, phase_center=altaz_to_radec(phase_center_altaz) ) return TV_Image( tv_image=sky_image, analog_pointing=phase_center_altaz, fov_radius=fov_radius, )
def compute_uvw(interferometer: Interferometer, phase_center: SkyCoord = None, time: Time = Time.now(), observer: EarthLocation = nenufar_position): """ """ # Get the baselines in ITRF coordinates baselines_itrf = interferometer.baselines.bsl xyz = baselines_itrf[np.tril_indices(interferometer.size)].T #xyz = np.array(baselines_itrf).T # Select zenith phase center if nothing is provided if phase_center is None: log.debug("Default zenith phase center selected.") zenith = SkyCoord(np.zeros(time.size), np.ones(time.size) * 90, unit="deg", frame=AltAz(obstime=time, location=observer)) phase_center = altaz_to_radec(zenith) center_dec_rad = phase_center.dec.rad if np.isscalar(center_dec_rad): center_dec_rad = np.repeat(center_dec_rad, time.size) # Compute the hour angle of the phase center lha = hour_angle(radec=phase_center, time=time, observer=observer, fast_compute=True) lha_rad = lha.rad # Compute UVW projection sr = np.sin(lha_rad) cr = np.cos(lha_rad) sd = np.sin(center_dec_rad) cd = np.cos(center_dec_rad) rot_uvw = np.array([[sr, cr, np.zeros(time.size)], [-sd * cr, sd * sr, cd], [cd * cr, -cd * sr, sd]]) # Project the baselines in the UVW frame uvw = -np.dot(np.moveaxis(rot_uvw, -1, 0), xyz) return np.moveaxis(uvw, -1, 1) # ============================================================= # # ============================================================= #
def save_fits(self, file_name: str, partial: bool = True): """ """ phase_center_eq = altaz_to_radec(self.analog_pointing) header = [ ("software", 'nenupy'), ("version", nenupy.__version__), ("contact", nenupy.__email__), ("azana", self.analog_pointing.az.deg), ("elana", self.analog_pointing.alt.deg), ("freq", self.tv_image.frequency[0].to(u.MHz).value), ("obstime", self.tv_image.time[0].isot), ("fov", self.fov_radius.to(u.deg).value * 2), ("pc_ra", phase_center_eq.ra.deg), ("pc_dec", phase_center_eq.dec.deg), ("stokes", self.tv_image.polarization[0]) ] map2write = self.tv_image.value[0, 0, 0].copy() write_map( filename=file_name, m=map2write, nest=False, coord='C', overwrite=True, dtype=self.tv_image.value.dtype, extra_header=header, partial=partial ) log.info( 'HEALPix image of {} cells (nside={}) saved in `{}`.'.format( map2write.size, self.tv_image.nside, file_name ) )
def save_png(self, figname: str, beam_contours: bool = True, show_sources: bool = True, **kwargs): """ """ image_center = altaz_to_radec( SkyCoord( self.analog_pointing.az, self.analog_pointing.alt, frame=AltAz( obstime=self.tv_image.time[0], location=nenufar_position ) ) ) #kwargs = {} if show_sources: src_names = [] src_position = [] with open(join(dirname(__file__), "nenufar_tv_sources.json")) as src_file: sources = json.load(src_file) for name in sources["FixedSources"]: src = FixedTarget.from_name(name, time=self.tv_image.time[0]) if src.coordinates.separation(image_center) <= 0.8*self.fov_radius: src_names.append(name) src_position.append(src.coordinates) for name in sources["SolarSystemSources"]: src = SolarSystemTarget.from_name(name, time=self.tv_image.time[0]) if src.coordinates.separation(image_center) <= 0.8*self.fov_radius: src_names.append(name) src_position.append(src.coordinates) if len(src_position) != 0: kwargs["text"] = (SkyCoord(src_position), src_names, "white") if beam_contours: # Simulate the array factor ma = MiniArray() af_sky = ma.array_factor( sky=HpxSky( resolution=0.2*u.deg, time=self.tv_image.time[0], frequency=self.tv_image.frequency[0] ), pointing=Pointing( coordinates=image_center, time=self.tv_image.time[0] ) ) # Normalize the array factor af = af_sky[0, 0, 0].compute() af_normalized = af/af.max() kwargs["contour"] = (af_normalized, np.arange(0.5, 1, 0.2), "copper") # Plot self.tv_image[0, 0, 0].plot( center=image_center, radius=self.fov_radius - 2.5*u.deg, figname=figname, colorbar_label=f"Stokes {self.tv_image.polarization[0]}", **kwargs ) return
def rephase_visibilities(self, phase_center, uvw): """ """ # Compute the zenith original phase center zenith = SkyCoord( np.zeros(self.time.size), np.ones(self.time.size)*90, unit="deg", frame=AltAz( obstime=self.time, location=nenufar_position ) ) zenith_phase_center = altaz_to_radec(zenith) # Define the rotation matrix def rotation_matrix(skycoord): """ """ ra_rad = skycoord.ra.rad dec_rad = skycoord.dec.rad if np.isscalar(ra_rad): ra_rad = np.array([ra_rad]) dec_rad = np.array([dec_rad]) cos_ra = np.cos(ra_rad) sin_ra = np.sin(ra_rad) cos_dec = np.cos(dec_rad) sin_dec = np.sin(dec_rad) return np.array([ [cos_ra, -sin_ra, np.zeros(ra_rad.size)], [-sin_ra*sin_dec, -cos_ra*sin_dec, cos_dec], [sin_ra*cos_dec, cos_ra*cos_dec, sin_dec], ]) # Transformation matrices to_origin = rotation_matrix(zenith_phase_center) # (3, 3, ntimes) to_new_center = rotation_matrix(phase_center) # (3, 3, 1) total_transformation = np.matmul( np.transpose( to_new_center, (2, 0, 1) ), to_origin ) # (3, 3, ntimes) rotUVW = np.matmul( np.expand_dims( (to_origin[2, :] - to_new_center[2, :]).T, axis=1 ), np.transpose( to_origin, (2, 1, 0) ) ) # (ntimes, 1, 3) phase = np.matmul( rotUVW, np.transpose(uvw, (0, 2, 1)) ) # (ntimes, 1, nvis) rotate_visibilities = np.exp( 2.j*np.pi*phase/wavelength(self.frequency).to(u.m).value[None, :, None] ) # (ntimes, nfreqs, nvis) new_uvw = np.matmul( uvw, # (ntimes, nvis, 3) np.transpose(total_transformation, (2, 0, 1)) ) return rotate_visibilities, new_uvw
def zenith_tracking(cls, time: Time, duration: TimeDelta = TimeDelta(1, format="sec"), observer: EarthLocation = nenufar_position ): """ Instantiates a :class:`~nenupy.astro.pointing.Pointing` object at the local zenith. :param time: Start times of the zenith pointing. :type t_min: :class:`~astropy.time.Time` :param duration: Duration of each individual pointing. If this argument is a scalar, then it will be applied to every start time (defined in ``time``). Default is one hour. :type duration: :class:`~astropy.time.TimeDelta` :param observer: Earth location from where the target is observed. Default is NenuFAR's location. :type observer: :class:`~astropy.coordinates.EarthLocation` :return: Pointing fixed at the local zenith. :rtype: :class:`~nenupy.astro.pointing.Pointing` :Example: >>> from nenupy.astro.pointing import Pointing >>> from astropy.time import Time, TimeDelta >>> pointing = Pointing.target_transit( target=cyg_a, time=Time("2021-01-01 00:00:00"), duration=TimeDelta(7200, format="sec"), azimuth=180*u.deg ) """ az = 0 el = 90 if not time.isscalar: az = np.repeat(az, time.size) el = np.repeat(el, time.size) altaz = SkyCoord( az, el, unit="deg", frame=AltAz( obstime=time, location=observer ) ) pointing = cls( coordinates=altaz_to_radec( altaz=altaz, fast_compute=False ), time=time, duration=duration, observer=observer ) pointing.custom_ho_coordinates = altaz.reshape((1,)) if altaz.isscalar else altaz return pointing
def from_file(cls, file_name, beam_index: int = 0, include_corrections: bool = True ): """ Instantiates a :class:`~nenupy.astro.pointing.Pointing` object from a NenuFAR pointing file. Several beam pointings (analog and/or numerical) could be described in ``file_name``. The argument ``beam_index`` allows for the selection of one of them. :param file_name: NenuFAR pointing file, either analog (ending with ``.altazA``) or numerical (ending with ``.altazB``). :type file_name: `str` :param beam_index: Beam number to take into account. :type beam_index: `int` :param include_corrections: Include or not the pointing corrections (default is ``True``). Only has an effect on analog beam corrections. :type include_corrections: `bool` :return: Pointing derived from a NenuFAR pointing file. :rtype: :class:`~nenupy.astro.pointing.Pointing` :Example: >>> from nenupy.astro.pointing import Pointing >>> pointing = Pointing.from_file( file_name=".../20211104_170000_20211104_200000_JUPITER_TRACKING.altazA", beam_index=1 ) """ if file_name.endswith('.altazA'): try: pointing = np.loadtxt( file_name, skiprows=3, comments=";", dtype={ 'names': ('time', 'anabeam', 'az', 'el', 'az_cor', 'el_cor', 'freq', 'el_eff'), 'formats': ('U20', 'i4', 'f4', 'f4', 'f4', 'f4', 'U5', 'f4') } ) available_beams = pointing["anabeam"] pointing = pointing[available_beams == beam_index] azimuths = pointing["az_cor"] if include_corrections else pointing["az"] elevations = pointing["el_eff"] if include_corrections else pointing["el"] except ValueError: # No correction pointing = np.loadtxt( file_name, skiprows=3, comments=";", dtype={ 'names': ('time', 'anabeam', 'az', 'el', 'freq', 'el_eff'), 'formats': ('U20', 'i4', 'f4', 'f4', 'U5', 'f4') } ) available_beams = pointing["anabeam"] pointing = pointing[available_beams == beam_index] azimuths = pointing["az"] elevations = pointing["el_eff"] if include_corrections else pointing["el"] except IndexError: # No beamsquint pointing = np.loadtxt( file_name, skiprows=3, comments=";", dtype={ 'names': ('time', 'anabeam', 'az', 'el', 'az_cor', 'el_cor'), 'formats': ('U20', 'i4', 'f4', 'f4', 'f4', 'f4') } ) available_beams = pointing["anabeam"] pointing = pointing[available_beams == beam_index] azimuths = pointing["az_cor"] if include_corrections else pointing["az"] elevations = pointing["el_cor"] if include_corrections else pointing["el"] if pointing.size == 0: raise ValueError( f"Empty pointing, check the beam_index={beam_index} value " f"(avalaible beam indices: {np.unique(available_beams)})." ) times = Time(pointing["time"]) azimuths *= u.deg elevations *= u.deg if times.size == 1: # for a transit with multiple ABeams azimuths = np.append(azimuths, [0]*u.deg) elevations = np.append(elevations, [90]*u.deg) times = times.insert(1, times[-1] + TimeDelta(1, format="sec")) duration = times[1:] - times[:-1] times = times[:-1] altaz_coords = SkyCoord( azimuths[:-1], elevations[:-1], frame=AltAz( obstime=times, location=nenufar_position ) ) elif file_name.endswith('.altazB'): pointing = np.loadtxt( file_name, skiprows=2, comments=";", dtype={ 'names': ('time', 'anabeam', 'digibeam', 'az', 'el', 'l', 'm', 'n'), 'formats': ('U20', 'i4', 'i4', 'f4', 'f4', 'f4', 'f4', 'f4') } ) available_beams = pointing["digibeam"] pointing = pointing[available_beams == beam_index] if pointing.size == 0: raise ValueError( f"Empty pointing, check the beam_index={beam_index} value " f"(avalaible beam indices: {np.unique(available_beams)})." ) times = Time(pointing["time"]) duration = times[1:] - times[:-1] # Add the last duration at the end (supposed to be 10 seconds) duration = duration.insert(-1, TimeDelta(10, format="sec", scale=duration.scale)) altaz_coords = SkyCoord( pointing['az'], pointing["el"], unit="deg", frame=AltAz( obstime=times, location=nenufar_position ) ) pointing = cls( coordinates=altaz_to_radec(altaz=altaz_coords), time=times, duration=duration, observer=nenufar_position ) pointing.custom_ho_coordinates = altaz_coords return pointing
def from_bst(cls, bst, beam: int = 0, analog: bool = True, max_points: int = 100 ): """ Instantiates a class:`~nenupy.astro.pointing.Pointing` object from a :class:`~nenupy.io.bst.BST` object. :param bst: :type bst: :param beam: :type beam: `int` :param analog: :type analog: `bool` :param max_points: :type max_points: :returns: Pointing derived from a NenuFAR BST file. :rtype: :class:`~nenupy.astro.pointing.Pointing` """ bst.beam = beam if analog: time, az, el = bst.analog_pointing else: time, az, el = bst.digital_pointing if time.size == 1: # for a transit with multiple ABeams az = np.append(az, [0]*u.deg) el = np.append(el, [90]*u.deg) time = time.insert(1, bst.time[-1]) if time.size > max_points: julian_days = time.jd jd_rebin = np.linspace(julian_days[0], julian_days[-1], max_points) az = np.interp(jd_rebin, julian_days, az) el = np.interp(jd_rebin, julian_days, el) time = Time(jd_rebin, format='jd') altaz_coords = SkyCoord( az[:-1], el[:-1], frame=AltAz( obstime=time[:-1], location=nenufar_position ) ) pointing = cls( coordinates=altaz_to_radec(altaz=altaz_coords), time=time[:-1], duration=time[1:] - time[:-1], observer=nenufar_position ) pointing.custom_ho_coordinates = altaz_coords return pointing