コード例 #1
0
def test_sky_lmn():
    sky = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
              time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
              frequency=np.linspace(30, 50, 6) * u.MHz,
              polarization="NE",
              value=0)
    l, m, n = sky.compute_lmn(phase_center=SkyCoord(200, 20, unit="deg"))
    assert l.shape == (1, 3)
    assert l[0, 1] == 0.
    assert m[0, 1] == 0.
    assert n[0, 1] == 1.
コード例 #2
0
def test_sky_get(mock_show):
    sky = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
              time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
              frequency=np.linspace(30, 50, 6) * u.MHz,
              polarization="NE",
              value=10)
    sky_slice = sky[0, 0, 0]
    assert sky_slice.value.shape == (3, )
    assert sky_slice.visible_sky.shape == (3, )
    sky_slice.plot(decibel=True, altaz_overlay=True)
コード例 #3
0
def test_sky_init():
    sky = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
              time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
              frequency=np.linspace(30, 50, 6) * u.MHz,
              polarization="NE",
              value=0)
    assert sky.shape == (2, 6, 1, 3)
    assert sky.time.size == 2
    assert sky.frequency.size == 6
    assert sky.polarization.size == 1
    assert sky.coordinates.size == 3
    assert sky.visible_mask.shape == (2, 6, 1, 3)
    assert sky.visible_mask[0, 0, 0, 1]
    assert sky.horizontal_coordinates.shape == (2, 3)
    assert str(
        sky
    ) == "<class 'nenupy.astro.sky.Sky'> instance\nvalue: (2, 6, 1, 3)\n\t* time: (2,)\n\t* frequency: (6,)\n\t* polarization: (1,)\n\t* coordinates: (3,)\n"
コード例 #4
0
 def test_beam(self):
     beam = self.ma.beam(sky=Sky(coordinates=SkyCoord([100, 200, 300],
                                                      [10, 50, 90],
                                                      unit="deg"),
                                 time=Time("2022-01-01T12:00:00"),
                                 frequency=50 * u.MHz,
                                 polarization=Polarization.NW),
                         pointing=Pointing.zenith_tracking(
                             time=Time("2022-01-01T11:00:00"),
                             duration=TimeDelta(7200, format="sec")),
                         configuration=NenuFAR_Configuration(
                             beamsquint_correction=True,
                             beamsquint_frequency=50 * u.MHz))
     assert beam.value.shape == (1, 1, 1, 3)
     beam_values = beam[0, 0, 0].value.compute()
     assert np.ma.is_masked(beam_values[0])
     assert beam_values[1] == pytest.approx(29.467, 1e-3)
     assert beam_values[2] == pytest.approx(301.108, 1e-3)
コード例 #5
0
def test_sky_operations():
    sky1 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 2) * u.MHz,
               polarization="NE",
               value=6)
    sky2 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 2) * u.MHz,
               polarization="NE",
               value=2)
    result = sky1 / sky2
    assert np.unique(result.value)[0] == 3.

    sky1 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 2) * u.MHz,
               polarization="NE",
               value=6)
    sky2 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 2) * u.MHz,
               polarization="NE",
               value=2)
    result = sky1 * sky2
    assert np.unique(result.value)[0] == 12.

    sky1 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 2) * u.MHz,
               polarization="NE",
               value=6)
    sky2 = Sky(coordinates=SkyCoord([100, 200, 300], [10, 20, 30], unit="deg"),
               time=Time(["2022-01-01T12:00:00", "2022-01-01T14:00:00"]),
               frequency=np.linspace(30, 50, 3) * u.MHz,
               polarization="NE",
               value=2)
    with pytest.raises(ValueError):
        result = sky1 / sky2
コード例 #6
0
    def attenuation_from_zenith(self,
                                coordinates,
                                time: Time = Time.now(),
                                frequency: u.Quantity = 50 * u.MHz,
                                polarization: Polarization = Polarization.NW):
        """ Returns the attenuation factor evaluated at given ``coordinates``
            compared to the zenithal Mini-Array beam gain.

            :param coordinates:
                Sky positions equatorial coordinates.
            :type coordinates:
                :class:`~astropy.coordinates.SkyCoord`
            :param time:
                UTC time at which the attenuation is evaluated. Default is ``now``.
            :type time:
                :class:`~astropy.time.Time`
            :param frequency:
                Frequency at which the attenuation is evaluated. Default is ``50 MHz``.
            :type frquency:
                :class:`~astropy.units.Quantity`
            :param polarization:
                NenuFAR antenna polarization. Default is ``Polarization.NW``.
            :type polarization:
                :class:`~nenupy.instru.nenufar.Polarization`
    
            :returns:
                Attenuation factor shaped as ``(time, frequency, polarization, coordinates)``.
                ``NaN`` is returned for any ``coordinates`` that is below the horizon.
            :rtype:
                :class:`~numpy.ndarray`

            :Example:
                >>> from nenupy.instru.nenufar import MiniArray
                >>> from astropy.coordinates import SkyCoord
                >>> ma = MiniArray(index=0)
                >>> attenuation = ma.attenuation_from_zenith(
                        coordinates=SkyCoord.from_name("Cyg A")
                    )
                
                >>> from nenupy.instru.nenufar import MiniArray
                >>> from astropy.coordinates import SkyCoord
                >>> import astropy.units as u
                >>> ma = MiniArray(index=0)
                >>> attenuation = ma.attenuation_from_zenith(
                        coordinates=SkyCoord.from_name("Cyg A"),
                        frequency=np.linspace(20, 80, 10)*u.MHz
                    )

            .. versionadded:: 2.0.0

        """
        # Define the pointing towards the zenith
        pointing = Pointing.zenith_tracking(time=time.reshape((1, )),
                                            duration=TimeDelta(10,
                                                               format="sec"))

        # Compute the local zenith in equatorial coordinates
        local_zenith = SkyCoord(
            180,
            90,
            unit="deg",
            frame=AltAz(obstime=time, location=nenufar_position)).transform_to(
                coordinates.frame)

        # Find the coordinates below the horizon and compute a mask
        input_coord_altaz = radec_to_altaz(radec=coordinates, time=time)
        invisible_mask = input_coord_altaz.alt.deg <= 0

        # Concatenate local_zenith and coordinates
        if coordinates.obstime is None:
            coordinates.obstime = local_zenith.obstime
        if coordinates.location is None:
            coordinates.location = local_zenith.location
        if coordinates.isscalar:
            coordinates = coordinates.reshape((1, ))
        coordinates = coordinates.insert(0, local_zenith)

        # Prepare a Sky instance for the beam simulation
        sky = Sky(coordinates=coordinates,
                  frequency=frequency,
                  time=time,
                  polarization=polarization)

        # Compute the beam
        beam = self.beam(sky=sky, pointing=pointing)

        # Compute the attenuation factor relative to the zenith (first member)
        values = beam.value.compute()
        output_values = values[..., 1:] / np.expand_dims(values[..., 0], 3)
        output_values[..., invisible_mask] = np.nan

        return output_values
コード例 #7
0
    def beam(
        self,
        sky: Sky,
        pointing: Pointing,
        analog_pointing: Pointing = None,
        configuration: NenuFAR_Configuration = NenuFAR_Configuration()
    ) -> Sky:
        r""" Computes the NenuFAR beam over the ``sky`` for a given
            ``pointing``.

            .. math::
                \mathcal{G}_{\rm NenuFAR}(\nu, \phi, \theta) = \mathcal{F}_{\rm NenuFAR} (\nu, \phi, \theta) \sum_{\rm MA} \mathcal{G}_{\rm MA}(\nu, \phi, \theta)

            where :math:`\nu` is the frequency, :math:`\phi` is the azimuth,
            :math:`\theta` is the elevation,
            :math:`\mathcal{G}_{\rm MA}` is the MiniArray response (see :meth:`~nenupy.instru.nenufar.MiniArray.beam`)
            and :math:`\mathcal{F}_{\rm NenuFAR}` is the array factor.

            This method considers the ``sky`` as the desired output (in terms of
            time, frequency, polarization and sky positions). It evaluates the effective 
            pointing directions for every time step defined in ``sky`` regarding
            the ``pointing`` input.

            :param sky:
                Desired output contained in a :class:`~nenupy.astro.sky.Sky` instance.
                (:attr:`~nenupy.astro.sky.Sky.time`, :attr:`~nenupy.astro.sky.Sky.frequency`,
                :attr:`~nenupy.astro.sky.Sky.polarization` and
                :attr:`~nenupy.astro.sky.Sky.coordinates` are used as inputs for the computation).
            :type sky:
                :class:`~nenupy.astro.sky.Sky`
            :param pointing:
                Instance of :class:`~nenupy.astro.pointing.Pointing` that defines
                the targeted **numerical** pointing directions over the time.
            :type pointing:
                :class:`~nenupy.astro.pointing.Pointing`
            :param analog_pointing:
                Instance of :class:`~nenupy.astro.pointing.Pointing` that defines
                the **analog** pointing directions over the time.
                This pointing is subject to beamsquint corrections.
            :type analog_pointing:
                :class:`~nenupy.astro.pointing.Pointing`
            :param configuration:
                NenuFAR configuration to consider during the beam simulation.
                The beamsquint correction and its frequency setting are defined here.
                Default is ``NenuFAR_Configuration(beamsquint_correction=True, beamsquint_frequency=50MHz)``.
            :type configuration:
                :class:`~nenupy.instru.nenufar.NenuFAR_Configuration`

            :returns:
                The instance of :class:`~nenupy.astro.sky.Sky`
                given as input is returned, its attribute
                :attr:`~nenupy.astro.sky.Sky.value` is updated
                with the result of the beam computation (stored as
                an :class:`~dask.array.Array`) and shaped as 
                ``(time, frequency, polarization, coordinates)``.
            :rtype:
                :class:`~nenupy.astro.sky.Sky`

            .. seealso::
                :meth:`~nenupy.instru.interferometer.Interferometer.array_factor` and :ref:`beam_simulation_doc`

        """
        log.info(f"Computing <class 'NenuFAR'> beam ({self.size} "
                 f"Mini-Arrays, {sky.time.size} time and "
                 f"{sky.frequency.size} frequency slots).")

        # Sorting out the analog pointing, make it equal to the
        # numerical pointing if it is not specifically defined.
        if not analog_pointing:
            analog_pointing = pointing
            log.info(
                "Analog pointing is set according to the numerical pointing.")

        # Computing the Array Factor of the whole NenuFAR array.
        array_factor = self.array_factor(sky=sky, pointing=pointing)

        # Finding the unique Mini-Array rotations and the number
        # of MAs corresponding to each rotation.
        rots, indices, counts = np.unique(
            self.miniarray_rotations.to(u.deg).value % 60,
            return_counts=True,
            return_index=True)

        # Summing all different (due to rotation) Mini-Array beam
        # patterns, although only executing it at most 6 times
        # because there could only be 6 different rotations.
        # Even though antGain updates the same sky instance, the
        # value attr * count creates new memeory allocations.
        antenna_gain = np.sum(np.array([
            gain(sky=sky,
                 pointing=analog_pointing,
                 configuration=configuration).value * count
            for gain, count in zip(self.antenna_gains[indices], counts)
        ]),
                              axis=0)

        # Updating the sky object value array where the the sky
        # is above the horizon as the product of the NenuFAR array
        # factor and the combined Mini-Array gain patterns.
        sky.value = array_factor * antenna_gain

        return sky
コード例 #8
0
ファイル: interferometer.py プロジェクト: AlanLoh/nenupy
    def beam(self, sky: Sky, pointing: Pointing) -> Sky:
        r""" Computes the phased-array response :math:`\mathcal{G}` over the ``sky`` for a given
            ``pointing``.

            .. math::
                \mathcal{G}(\nu, \phi, \theta) = \sum_{\rm ant} \mathcal{F}(\nu, \phi, \theta) \mathcal{G}_{\rm ant} (\nu, \phi, \theta)

            where :math:`\nu` is the frequency, :math:`\phi` is the azimuth,
            :math:`\theta` is the elevation,
            :math:`\mathcal{G}_{\rm ant}` is the individual array element radiation pattern and
            :math:`\mathcal{F}` is the array factor.

            This method considers the ``sky`` as the desired output (in terms of
            time, frequency, polarization and sky positions). It evaluates the effective 
            pointing directions for every time step defined in ``sky`` regarding
            the ``pointing`` input.

            :param sky:
                Desired output contained in a :class:`~nenupy.astro.sky.Sky` instance.
            :type sky:
                :class:`~nenupy.astro.sky.Sky`
            :param pointing:
                Instance of :class:`~nenupy.astro.pointing.Pointing` that defines
                the targeted pointing directions over the time.
            :type pointing:
                :class:`~nenupy.astro.pointing.Pointing`

            :return:
                The instance of :class:`~nenupy.astro.sky.Sky`
                given as input is returned, its attribute
                :attr:`~nenupy.astro.sky.Sky.value` is updated
                with the result of the beam computation (stored as
                an :class:`~dask.array.Array`) and shaped as 
                ``(time, frequency, polarization, coordinates)``.
            :rtype:
                :class:`~nenupy.astro.sky.Sky`

            .. seealso::
                :meth:`~nenupy.instru.interferometer.Interferometer.array_factor`

        """

        # Compute the array factor
        array_factor = self.array_factor(sky=sky, pointing=pointing)

        # Compute the total antenna gain, i.e. the sum of all
        # antenna gains for beamforming.
        antenna_gain = np.sum(np.array(
            [gain(sky=sky, pointing=pointing) for gain in self.antenna_gains]),
                              axis=0)

        # Rechunk the Dask Array before the computation
        # coord_chunk = array_factor.shape[-1]//cpu_count()
        # coord_chunk = 1 if coord_chunk == 0 else coord_chunk
        # array_factor = array_factor.rechunk(array_factor.shape[:-1] + (coord_chunk,))

        # Perform the Dask computation of array factor times antenna
        # gains. Update the sky instance values.
        sky.value = array_factor * antenna_gain

        return sky