Exemple #1
0
    def front_end(self, signal):
        """
        Apply front-end processes to a signal and return the output.

        The front-end consists of amplification according to data taken from
        NuRadioReco and signal clipping.

        Parameters
        ----------
        signal : Signal
            ``Signal`` object on which to apply the front-end processes.

        Returns
        -------
        Signal
            Signal processed by the antenna front end.

        """
        copy = Signal(signal.times, signal.values)
        copy.filter_frequencies(self.interpolate_filter, force_real=True)
        clipped_values = np.clip(copy.values * self.amplification,
                                 a_min=-self.amplifier_clipping,
                                 a_max=self.amplifier_clipping)
        return Signal(signal.times,
                      clipped_values,
                      value_type=signal.value_type)
Exemple #2
0
    def front_end(self, signal):
        """
        Apply front-end processes to a signal and return the output.

        The front-end consists of the full ARA electronics chain (including
        amplification) and signal clipping.

        Parameters
        ----------
        signal : Signal
            ``Signal`` object on which to apply the front-end processes.

        Returns
        -------
        Signal
            Signal processed by the antenna front end.

        """
        copy = Signal(signal.times, signal.values)
        copy.filter_frequencies(self.interpolate_filter, force_real=True)
        # sqrt(2) for 3dB splitter for TURF, SURF
        clipped_values = np.clip(copy.values / np.sqrt(2) * self.amplification,
                                 a_min=-self.amplifier_clipping,
                                 a_max=self.amplifier_clipping)
        return Signal(signal.times,
                      clipped_values,
                      value_type=signal.value_type)
Exemple #3
0
    def receive(self, signal, origin=None, polarization=None):
        """Process incoming signal according to the filter function and
        store it to the signals list. Subclasses may extend this fuction,
        but should end with super().receive(signal)."""
        copy = Signal(signal.times, signal.values,
                      value_type=Signal.ValueTypes.voltage)
        copy.filter_frequencies(self.response)

        if origin is None:
            d_gain = 1
        else:
            # Calculate theta and phi relative to the orientation
            r, theta, phi = self._convert_to_antenna_coordinates(origin)
            d_gain = self.directional_gain(theta=theta, phi=phi)

        if polarization is None:
            p_gain = 1
        else:
            p_gain = self.polarization_gain(normalize(polarization))

        signal_factor = d_gain * p_gain * self.efficiency

        if signal.value_type==Signal.ValueTypes.voltage:
            pass
        elif signal.value_type==Signal.ValueTypes.field:
            signal_factor /= self.antenna_factor
        else:
            raise ValueError("Signal's value type must be either "
                             +"voltage or field. Given "+str(signal.value_type))

        copy.values *= signal_factor
        self.signals.append(copy)
    def receive(self,
                signal,
                direction=None,
                polarization=None,
                force_real=True):
        """Process incoming signal according to the filter function and
        store it to the signals list. Subclasses may extend this fuction,
        but should end with super().receive(signal)."""
        copy = Signal(signal.times,
                      signal.values,
                      value_type=Signal.ValueTypes.voltage)
        copy.filter_frequencies(self.response, force_real=force_real)

        if direction is not None:
            # Calculate theta and phi relative to the orientation
            origin = self.position - normalize(direction)
            r, theta, phi = self._convert_to_antenna_coordinates(origin)
            freq_data, gain_data, phase_data = self.generate_directionality_gains(
                theta, phi)

            def interpolate_directionality(frequencies):
                interp_gains = np.interp(frequencies,
                                         freq_data,
                                         gain_data,
                                         left=0,
                                         right=0)
                interp_phases = np.interp(frequencies,
                                          freq_data,
                                          phase_data,
                                          left=0,
                                          right=0)
                return interp_gains * np.exp(1j * interp_phases)

            copy.filter_frequencies(interpolate_directionality,
                                    force_real=force_real)

        if polarization is None:
            p_gain = 1
        else:
            p_gain = self.polarization_gain(normalize(polarization))

        signal_factor = p_gain * self.efficiency

        if signal.value_type == Signal.ValueTypes.voltage:
            pass
        elif signal.value_type == Signal.ValueTypes.field:
            signal_factor /= self.antenna_factor
        else:
            raise ValueError("Signal's value type must be either " +
                             "voltage or field. Given " +
                             str(signal.value_type))

        copy.values *= signal_factor
        self.signals.append(copy)
 def front_end(self, signal):
     """Apply the front-end processing of the antenna signal, including
     electronics chain filters/amplification and clipping."""
     copy = Signal(signal.times, signal.values)
     copy.filter_frequencies(self.antenna.interpolate_filter,
                             force_real=True)
     clipped_values = np.clip(copy.values,
                              a_min=-self.amplifier_clipping,
                              a_max=self.amplifier_clipping)
     return Signal(signal.times,
                   clipped_values,
                   value_type=signal.value_type)
Exemple #6
0
 def test_filter_frequencies_force_real(self, signal):
     """Test that the filter_frequencies force_real option works"""
     resp = lambda f: int(f==0.2)
     copy = Signal(signal.times, signal.values)
     expected = Signal(signal.times, [-0.05,0.0190983,0.0618034,0.0190983,-0.05],
                       value_type=signal.value_type)
     copy.filter_frequencies(resp, force_real=False)
     for i in range(5):
         assert copy.values[i] == pytest.approx(expected.values[i])
     expected = Signal(signal.times, [-0.1,0.0381966,0.1236068,0.0381966,-0.1],
                       value_type=signal.value_type)
     signal.filter_frequencies(resp, force_real=True)
     for i in range(5):
         assert signal.values[i] == pytest.approx(expected.values[i])
Exemple #7
0
    def propagate(self, signal=None, polarization=None):
        """
        Propagate the signal with optional polarization along the ray path.

        Applies the frequency-dependent signal attenuation along the ray path
        and shifts the times according to the ray time of flight. Additionally
        provides the s and p polarization directions.

        Parameters
        ----------
        signal : Signal, optional
            ``Signal`` object to propagate.
        polarization : array_like, optional
            Vector representing the linear polarization of the `signal`.

        Returns
        -------
        tuple of Signal
            Tuple of ``Signal`` objects representing the s and p polarizations
            of the original `signal` attenuated along the ray path. Only
            returned if `signal` was not ``None``.
        tuple of ndarray
            Tuple of polarization vectors representing the s and p polarization
            directions of the `signal` at the end of the ray path. Only
            returned if `polarization` was not ``None``.

        See Also
        --------
        pyrex.Signal : Base class for time-domain signals.

        """
        if polarization is None:
            if signal is None:
                return

            else:
                copy = Signal(signal.times+self.tof, signal.values,
                              value_type=signal.value_type)
                copy.filter_frequencies(self.attenuation)
                return copy

        else:
            # Unit vectors perpendicular and parallel to plane of incidence
            # at the launching point
            u_s0 = normalize(np.cross(self.emitted_direction, [0, 0, 1]))
            u_p0 = normalize(np.cross(u_s0, self.emitted_direction))
            # Unit vector parallel to plane of incidence at the receiving point
            # (perpendicular vector stays the same)
            u_p1 = normalize(np.cross(u_s0, self.received_direction))

            if signal is None:
                return (u_s0, u_p1)

            else:
                # Amplitudes of s and p components
                pol_s = np.dot(polarization, u_s0)
                pol_p = np.dot(polarization, u_p0)
                # Fresnel reflectances of s and p components
                f_s, f_p = self.fresnel
                # Apply fresnel s and p coefficients in addition to attenuation
                attenuation_s = lambda freqs: self.attenuation(freqs) * f_s
                attenuation_p = lambda freqs: self.attenuation(freqs) * f_p
                signal_s = Signal(signal.times+self.tof, signal.values*pol_s,
                                  value_type=signal.value_type)
                signal_p = Signal(signal.times+self.tof, signal.values*pol_p,
                                  value_type=signal.value_type)
                signal_s.filter_frequencies(attenuation_s, force_real=True)
                signal_p.filter_frequencies(attenuation_p, force_real=True)
                return (signal_s, signal_p), (u_s0, u_p1)
Exemple #8
0
    def apply_response(self,
                       signal,
                       direction=None,
                       polarization=None,
                       force_real=True):
        """
        Process the complete antenna response for an incoming signal.

        Processes the incoming signal according to the frequency response of
        the antenna, the efficiency, and the antenna factor. May also apply the
        directionality and the polarization gain depending on the provided
        parameters. Subclasses may wish to overwrite this function if the
        full antenna response cannot be divided nicely into the described
        pieces.

        Parameters
        ----------
        signal : Signal
            Incoming ``Signal`` object to process.
        direction : array_like, optional
            Vector denoting the direction of travel of the signal as it reaches
            the antenna. If ``None`` no directional response will be applied.
        polarization : array_like, optional
            Vector denoting the signal's polarization direction. If ``None``
            no polarization gain will be applied.
        force_real : boolean, optional
            Whether or not the frequency response should be redefined in the
            negative-frequency domain to keep the values of the filtered signal
            real.

        Returns
        -------
        Signal
            Processed ``Signal`` object after the complete antenna response has
            been applied. Should have a ``value_type`` of ``voltage``.

        Raises
        ------
        ValueError
            If the given `signal` does not have a ``value_type`` of ``voltage``
            or ``field``.

        See Also
        --------
        pyrex.Signal : Base class for time-domain signals.

        """
        copy = Signal(signal.times,
                      signal.values,
                      value_type=Signal.Type.voltage)
        copy.filter_frequencies(self.frequency_response, force_real=force_real)

        if direction is not None and polarization is not None:
            # Calculate theta and phi relative to the orientation
            origin = self.position - normalize(direction)
            r, theta, phi = self._convert_to_antenna_coordinates(origin)
            # Calculate polarization vector in the antenna coordinates
            y_axis = np.cross(self.z_axis, self.x_axis)
            transformation = np.array([self.x_axis, y_axis, self.z_axis])
            ant_pol = np.dot(transformation, normalize(polarization))
            # Calculate directional response as a function of frequency
            directive_response = self.directional_response(theta, phi, ant_pol)
            copy.filter_frequencies(directive_response, force_real=force_real)

        elif (direction is not None and polarization is None
              or direction is None and polarization is not None):
            raise ValueError(
                "Direction and polarization must be specified together")

        signal_factor = self.efficiency

        if signal.value_type == Signal.Type.voltage:
            pass
        elif signal.value_type == Signal.Type.field:
            signal_factor /= self.antenna_factor
        else:
            raise ValueError("Signal's value type must be either " +
                             "voltage or field. Given " +
                             str(signal.value_type))

        copy *= signal_factor

        return copy