Example #1
0
    def test_analog_pointing(self):
        analog_pointing = self.ma.analog_pointing(
            pointing=Pointing.target_tracking(
                target=FixedTarget.from_name("Vir A"),
                time=Time(["2022-01-01T11:00:00", "2022-01-01T14:00:00"]),
                duration=TimeDelta(7200, format="sec")),
            configuration=NenuFAR_Configuration(beamsquint_correction=False,
                                                beamsquint_frequency=20 *
                                                u.MHz))
        assert analog_pointing._custom_ho_coordinates.shape == (2, )
        assert analog_pointing._custom_ho_coordinates[
            0].alt.deg == pytest.approx(21.55, 1e-2)
        assert analog_pointing._custom_ho_coordinates[
            1].alt.deg == pytest.approx(11.98, 1e-2)

        analog_pointing = self.ma.analog_pointing(
            pointing=Pointing.target_tracking(
                target=FixedTarget.from_name("Vir A"),
                time=Time(["2022-01-01T11:00:00", "2022-01-01T14:00:00"]),
                duration=TimeDelta(7200, format="sec")),
            configuration=NenuFAR_Configuration(beamsquint_correction=True,
                                                beamsquint_frequency=20 *
                                                u.MHz))
        assert analog_pointing._custom_ho_coordinates[
            0].alt.deg == pytest.approx(12.26, 1e-2)
        assert analog_pointing._custom_ho_coordinates[
            1].alt.deg == pytest.approx(11.98, 1e-2)
Example #2
0
def test_fixedtarget_set():
    src = FixedTarget.from_name(name="Cyg A", time=Time("2022-01-01T12:00:00"))
    set_times = src.set_time(t_min=Time("2022-01-01T12:00:00"),
                             elevation=0 * u.deg,
                             duration=TimeDelta(86400 * 2, format="sec"))
    assert set_times.size == 2
    assert set_times[0].jd == pytest.approx(2459581.489, 1e-3)
    assert set_times[1].jd == pytest.approx(2459582.486, 1e-3)

    set_time = src.next_set_time(time=Time("2022-01-01T12:00:00"),
                                 elevation=10 * u.deg)
    assert set_time.isscalar
    assert set_time.jd == pytest.approx(2459581.398, 1e-3)

    set_time = src.previous_set_time(time=Time("2022-01-01T12:00:00"),
                                     elevation=10 * u.deg)
    assert set_time.isscalar
    assert set_time.jd == pytest.approx(2459580.400, 1e-3)

    src_circum = FixedTarget.from_name(name="Cas A",
                                       time=Time("2022-01-01T12:00:00"))
    set_times = src_circum.set_time(t_min=Time("2022-01-01T12:00:00"),
                                    elevation=0 * u.deg,
                                    duration=TimeDelta(86400 * 2,
                                                       format="sec"))
    assert set_times.size == 0
    set_times = src_circum.set_time(t_min=Time("2022-01-01T12:00:00"),
                                    elevation=40 * u.deg,
                                    duration=TimeDelta(86400 * 2,
                                                       format="sec"))
    assert set_times.size == 2
    assert set_times[0].jd == pytest.approx(2459581.431, 1e-3)
    assert set_times[1].jd == pytest.approx(2459582.429, 1e-3)
Example #3
0
def test_fixedtarget_rise():
    src = FixedTarget.from_name(name="Cyg A", time=Time("2022-01-01T12:00:00"))
    rise_times = src.rise_time(t_min=Time("2022-01-01T12:00:00"),
                               elevation=0 * u.deg,
                               duration=TimeDelta(86400 * 2, format="sec"))
    assert rise_times.size == 2
    assert rise_times[0].jd == pytest.approx(2459581.601, 1e-3)
    assert rise_times[1].jd == pytest.approx(2459582.599, 1e-3)

    rise_time = src.next_rise_time(time=Time("2022-01-01T12:00:00"),
                                   elevation=10 * u.deg)
    assert rise_time.isscalar
    assert rise_time.jd == pytest.approx(2459581.692, 1e-3)

    rise_time = src.previous_rise_time(time=Time("2022-01-01T12:00:00"),
                                       elevation=10 * u.deg)
    assert rise_time.isscalar
    assert rise_time.jd == pytest.approx(2459580.695, 1e-3)

    src_circum = FixedTarget.from_name(name="Cas A",
                                       time=Time("2022-01-01T12:00:00"))
    rise_times = src_circum.rise_time(t_min=Time("2022-01-01T12:00:00"),
                                      elevation=0 * u.deg,
                                      duration=TimeDelta(86400 * 2,
                                                         format="sec"))
    assert rise_times.size == 0
    rise_times = src_circum.rise_time(t_min=Time("2022-01-01T12:00:00"),
                                      elevation=40 * u.deg,
                                      duration=TimeDelta(86400 * 2,
                                                         format="sec"))
    assert rise_times.size == 2
    assert rise_times[0].jd == pytest.approx(2459581.941, 1e-3)
    assert rise_times[1].jd == pytest.approx(2459582.939, 1e-3)
Example #4
0
def test_pointing_target_tracking():
    pointing = Pointing.target_tracking(target=FixedTarget.from_name("Cas A"),
                                        time=Time("2022-01-01T12:00:00"))
    assert pointing.coordinates.size == 1

    pointing = Pointing.target_tracking(
        target=FixedTarget.from_name("Cas A"),
        time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]))
    assert pointing.coordinates.size == 2
    assert pointing.horizontal_coordinates[1].az.deg == pytest.approx(
        48.45, 1e-2)

    pointing = Pointing.target_tracking(
        target=SolarSystemTarget.from_name("Sun"),
        time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]))
    assert np.unique(pointing.coordinates.ra.deg).size == 2
Example #5
0
def test_pointing_target_transit():
    pointing = Pointing.target_transit(target=FixedTarget.from_name("Cyg A"),
                                       t_min=Time("2022-01-01T12:00:00"),
                                       duration=TimeDelta(3600, format="sec"),
                                       dt=TimeDelta(3600, format="sec"),
                                       azimuth=180 * u.deg)
    assert pointing.custom_ho_coordinates[0, 0].az.deg == pytest.approx(
        179.954, 1e-3)
    assert pointing.custom_ho_coordinates[0, 0].alt.deg == pytest.approx(
        83.416, 1e-3)
Example #6
0
def test_fixedtarget_meridian_transit():
    src = FixedTarget.from_name(name="Cyg A", time=Time("2022-01-01T12:00:00"))
    transit_time = src.meridian_transit(t_min=Time("2022-01-01T12:00:00"),
                                        duration=TimeDelta(86400,
                                                           format='sec'),
                                        precision=TimeDelta(5, format='sec'),
                                        fast_compute=True)
    assert transit_time[0].jd == pytest.approx(2459581.0464, 1e-4)

    transit_times = src.meridian_transit(t_min=Time("2022-01-01T12:00:00"),
                                         duration=TimeDelta(86400 * 2,
                                                            format='sec'),
                                         precision=TimeDelta(5, format='sec'),
                                         fast_compute=False)
    assert transit_times.size == 2
    assert transit_times[1].jd == pytest.approx(2459582.0437, 1e-4)

    transit_time = src.next_meridian_transit(time=Time("2022-01-01T12:00:00"))
    assert transit_time.isscalar
    assert transit_time.jd == pytest.approx(2459581.0464, 1e-4)

    transit_time = src.previous_meridian_transit(
        time=Time("2022-01-01T12:00:00"))
    assert transit_time.isscalar
    assert transit_time.jd == pytest.approx(2459580.0491, 1e-4)

    az_transit = src.azimuth_transit(azimuth=200 * u.deg,
                                     t_min=Time("2022-01-01T12:00:00"))
    assert az_transit.size == 1
    assert az_transit.jd == pytest.approx(2459581.0551, 1e-4)

    az_transit = src.azimuth_transit(azimuth=330 * u.deg,
                                     t_min=Time("2022-01-01T12:00:00"))
    assert az_transit.size == 0

    src_circum = FixedTarget.from_name(name="Cas A",
                                       time=Time("2022-01-01T12:00:00"))
    az_transit = src_circum.azimuth_transit(azimuth=350 * u.deg,
                                            t_min=Time("2022-01-01T12:00:00"))
    assert az_transit.size == 2
    assert az_transit[0].jd == pytest.approx(2459581.1987, 1e-4)
    assert az_transit[1].jd == pytest.approx(2459581.6345, 1e-4)
Example #7
0
def test_fixedtarget_properties():
    src = FixedTarget(coordinates=SkyCoord(300, 40, unit="deg"),
                      time=Time("2022-01-01T12:00:00"))

    ha = src.hour_angle(fast_compute=True)
    assert ha[0].deg == pytest.approx(343.315, 1e-3)
    ha = src.hour_angle(fast_compute=False)
    assert ha[0].deg == pytest.approx(343.311, 1e-3)

    lst = src.local_sidereal_time(fast_compute=True)
    assert lst[0].deg == pytest.approx(283.315, 1e-3)
    lst = src.local_sidereal_time(fast_compute=False)
    assert lst[0].deg == pytest.approx(283.311, 1e-3)

    assert src.culmination_azimuth.to(u.deg).value == 180.0

    assert not src.is_circumpolar

    src = FixedTarget(coordinates=SkyCoord(100, 80, unit="deg"),
                      time=Time("2022-01-01T12:00:00"))

    assert src.is_circumpolar
Example #8
0
    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
Example #9
0
    def make_nearfield(self,
            radius: u.Quantity = 400*u.m,
            npix: int = 64,
            sources: list = []
        ):
        r""" Computes the Near-field image from the cross-correlation
            statistics data :math:`\mathcal{V}`.

            The distances between each Mini-Array :math:`{\rm MA}_i`
            and the ground positions :math:`Delta` is:

            .. math::
                d_{\rm{MA}_i} (x, y) = \sqrt{
                    ({\rm MA}_{i, x} - \Delta_x)^2 + ({\rm MA}_{i, y} - \Delta_y)^2 + \left( {\rm MA}_{i, z} - \sum_j \frac{{\rm MA}_{j, z}}{n_{\rm MA}} - 1 \right)^2
                } 

            Then, the near-field image :math:`n_f` can be retrieved
            as follows (:math:`k` and :math:`l` being two distinct
            Mini-Arrays):

            .. math::
                n_f (x, y) = \sum_{k, l} \left| \sum_{\nu} \langle \mathcal{V}_{\nu, k, l}(t) \rangle_t e^{2 \pi i \left( d_{{\rm MA}_k} - d_{{\rm MA}_l} \right) (x, y) \frac{\nu}{c}} \right|

            .. note::
                To simulate astrophysical source of brightness :math:`\mathcal{B}`
                footprint on the near-field, its visibility per baseline
                of Mini-Arrays :math:`k` and :math:`l` are computed as:

                .. math::
                    \mathcal{V}_{{\rm simu}, k, l} = \mathcal{B} e^{2 \pi i \left( \mathbf{r}_k - \mathbf{r}_l \right) \cdot \mathbf{u} \frac{\nu}{c}}

                with :math:`\mathbf{r}` the ENU position of the Mini-Arrays,
                :math:`\mathbf{u} = \left( \cos(\theta) \sin(\phi), \cos(\theta) \cos(\phi), sin(\theta) \right)`
                the ground projection vector (in East-North-Up coordinates),
                (:math:`\phi` and :math:`\theta` are the source horizontal
                coordinates azimuth and elevation respectively).
    

            :param radius:
                Radius of the ground image. Default is ``400m``.
            :type radius:
                :class:`~astropy.units.Quantity`
            :param npix:
                Number of pixels of the image size. Default is ``64``.
            :type npix:
                `int`
            :param sources:
                List of source names for which their near-field footprint
                may be computed. Only sources above 10 deg elevation
                will be considered.
            :type sources:
                `list`

            :returns:
                Tuple of near-field image and a dictionnary 
                containing all source footprints. 
            :rtype:
                `tuple`(:class:`~numpy.ndarray`, `dict`)

            :Example:

                from nenupy.io.xst import XST
                xst = XST("xst_file.fits")
                nearfield, src_dict = xst.make_nearfield(sources=["Cas A", "Sun"])

            .. versionadded:: 1.1.0

        """

        def compute_nearfield_imprint(visibilities, phase):
            # Phase and average in frequency
            nearfield = np.mean(
                visibilities[..., None, None] * phase,
                axis=0
            )
            # Average in baselines
            nearfield = np.nanmean(np.abs(nearfield), axis=0)
            with ProgressBar() if log.getEffectiveLevel() <= logging.INFO else DummyCtMgr():
                return nearfield.compute()

        # Mini-Array positions in ENU coordinates
        nenufar = NenuFAR()[self.mini_arrays]
        ma_etrs = l93_to_etrs(nenufar.antenna_positions)
        ma_enu = etrs_to_enu(ma_etrs)

        # Treat baselines
        ma1, ma2 = np.tril_indices(self.mini_arrays.size, 0)
        cross_mask = ma1 != ma2

        # Mean time of observation
        obs_time = self.time[0] + (self.time[-1] - self.time[0])/2.

        # Delays at the ground
        radius_m = radius.to(u.m).value
        ground_granularity = np.linspace(-radius_m, radius_m, npix)
        posx, posy = np.meshgrid(ground_granularity, ground_granularity)
        posz = np.ones_like(posx) * (np.average(ma_enu[:, 2]) + 1)
        ground_grid = np.stack((posx, posy, posz), axis=2)
        ground_distances = np.sqrt(
            np.sum(
                (ma_enu[:, None, None, :] - ground_grid[None])**2,
                axis=-1
            )
        )
        grid_delays = ground_distances[ma1] - ground_distances[ma2] # (nvis, npix, npix)
        n_bsl = ma1[cross_mask].size
        grid_delays = da.from_array(
            grid_delays[cross_mask],
            chunks=(np.floor(n_bsl/os.cpu_count()), npix, npix)
        )
    
        # Mean in time the visibilities
        vis = np.mean(
            self.value,
            axis=0
        )[..., cross_mask] # (nfreqs, nvis)
        vis = da.from_array(
            vis,
            chunks=(1, np.floor(n_bsl/os.cpu_count()))#(self.frequency.size, np.floor(n_bsl/os.cpu_count()))
        )

        # Make the nearfield image
        log.info(
            f"Computing nearfield (time: {self.time.size}, frequency: {self.frequency.size}, baselines: {vis.shape[1]}, pixels: {posx.size})... "
        )
        wvl = wavelength(self.frequency).to(u.m).value
        phase = np.exp(2.j * np.pi * (grid_delays[None, ...]/wvl[:, None, None, None]))
        log.debug("Computing the phase term...")
        with ProgressBar() if log.getEffectiveLevel() <= logging.INFO else DummyCtMgr():
            phase = phase.compute()
        log.debug("Computing the nearf-field...")
        nearfield = compute_nearfield_imprint(vis, phase)

        # Compute nearfield imprints for other sources
        simu_sources = {}
        for src_name in sources:

            # Check that the source is visible
            if src_name.lower() in ["sun", "moon", "venus", "mars", "jupiter", "saturn", "uranus", "neptune"]:
                src = SolarSystemTarget.from_name(name=src_name, time=obs_time)
            else:
                src = FixedTarget.from_name(name=src_name, time=obs_time)
            altaz = src.horizontal_coordinates#[0]
            if altaz.alt.deg <= 10:
                log.debug(f"{src_name}'s elevation {altaz[0].alt.deg}<=10deg, not considered for nearfield imprint.")
                continue

            # Projection from AltAz to ENU vector
            az_rad = altaz.az.rad
            el_rad = altaz.alt.rad
            cos_az = np.cos(az_rad)
            sin_az = np.sin(az_rad)
            cos_el = np.cos(el_rad)
            sin_el = np.sin(el_rad)
            to_enu = np.array(
                [cos_el*sin_az, cos_el*cos_az, sin_el]
            )
            # src_delays = np.matmul(
            #     ma_enu[ma1] - ma_enu[ma2],
            #     to_enu
            # )
            # src_delays = da.from_array(
            #     src_delays[cross_mask, :],
            #     chunks=((np.floor(n_bsl/os.cpu_count()), npix, npix), 1)
            # )
            
            ma1_enu = da.from_array(
                ma_enu[ma1[cross_mask]],
                chunks=np.floor(n_bsl/os.cpu_count())
            )
            ma2_enu = da.from_array(
                ma_enu[ma2[cross_mask]],
                chunks=np.floor(n_bsl/os.cpu_count())
            )
            src_delays = np.matmul(
                ma1_enu - ma2_enu,
                to_enu
            )

            # Simulate visibilities
            src_vis = np.exp(2.j * np.pi * (src_delays/wvl))
            src_vis = np.swapaxes(src_vis, 1, 0)
            log.debug(f"Computing the nearf-field imprint of {src_name}...")
            simu_sources[src_name] = compute_nearfield_imprint(src_vis, phase)

        return nearfield, simu_sources
Example #10
0
 def _select_target_type(source_name):
     # Check whether the source name matches a solar system object
     if source_name.upper() in SolarSystemSource._member_names_:
         return SolarSystemTarget.from_name(source_name, time=time)
     else:
         return FixedTarget.from_name(source_name, time=time)