예제 #1
0
    def generate_noise(self):
        """
        generates noise traces for all channels that will cause a high/low majority logic trigger

        Returns np.array of shape (n_channels, n_samples)
        """
        n_traces = [None] * self.n_majority
        t_bins = [None] * self.n_majority
        for iCh in range(self.n_majority):
            while n_traces[iCh] is None:
                spec = self.noise.bandlimited_noise(self.min_freq,
                                                    self.max_freq,
                                                    self.n_samples,
                                                    self.sampling_rate,
                                                    self.amplitude,
                                                    self.noise_type,
                                                    time_domain=False)
                spec *= self.filt
                trace = fft.freq2time(spec, self.sampling_rate)
                if (np.any(trace > self.threshold)
                        and np.any(trace < -self.threshold)):
                    triggered_bins = get_high_low_triggers(
                        trace, self.threshold, -self.threshold,
                        self.time_coincidence, self.dt)
                    if (True in triggered_bins):
                        t_bins[iCh] = triggered_bins
                        if (iCh == 0):
                            n_traces[iCh] = np.roll(
                                trace, self.trigger_bin -
                                np.argwhere(triggered_bins is True)[0])
                        else:
                            tmp = np.random.randint(self.trigger_bin_low,
                                                    self.trigger_bin)
                            n_traces[iCh] = np.roll(
                                trace,
                                tmp - np.argwhere(triggered_bins is True)[0])
        traces = np.zeros((self.n_channels, self.n_samples))
        rnd_iterator = list(range(self.n_channels))
        np.random.shuffle(rnd_iterator)
        for i, iCh in enumerate(rnd_iterator):
            if (i < self.n_majority):
                traces[iCh] = n_traces[i]
            else:
                spec = self.noise.bandlimited_noise(self.min_freq,
                                                    self.max_freq,
                                                    self.n_samples,
                                                    self.sampling_rate,
                                                    self.amplitude,
                                                    type=self.noise_type,
                                                    time_domain=False)
                spec *= self.filt
                traces[iCh] = fft.freq2time(spec, self.sampling_rate)
        return traces
예제 #2
0
def butterworth_filter_trace(trace, sampling_frequency, passband, order=8):
    """
    Filters a trace using a Butterworth filter.

    Parameters
    ----------
    trace: array of floats
        Trace to be filtered
    sampling_frequency: float
        Sampling frequency
    passband: (float, float) tuple
        Tuple indicating the cutoff frequencies
    order: integer
        Filter order

    Returns
    ------
    filtered_trace: array of floats
        The filtered trace
    """

    n_samples = len(trace)

    spectrum = fft.time2freq(trace, sampling_frequency)
    frequencies = np.fft.rfftfreq(n_samples, 1 / sampling_frequency)

    filtered_spectrum = apply_butterworth(spectrum, frequencies, passband, order)
    filtered_trace = fft.freq2time(filtered_spectrum, sampling_frequency)

    return filtered_trace
예제 #3
0
def get_channel_voltage_from_efield(station, electric_field, channels, detector, zenith, azimuth, antenna_pattern_provider, return_spectrum=True):
    """
    Returns the voltage traces that would result in the channels from the station's E-field.

    Parameters
    ------------------------
    station: Station
    electric_field: ElectricField
    channels: array of int
        IDs of the channels for which the expected voltages should be calculated
    detector: Detector
    zenith, azimuth: float
        incoming direction of the signal. Note that reflection and refraction
        at the air/ice boundary are already being taken into account.
    antenna_pattern_provider: AntennaPatternProvider
    return_spectrum: boolean
        if True, returns the spectrum, if False return the time trace
    """

    frequencies = electric_field.get_frequencies()
    spectrum = electric_field.get_frequency_spectrum()
    efield_antenna_factor = get_efield_antenna_factor(station, frequencies, channels, detector, zenith, azimuth, antenna_pattern_provider)
    if return_spectrum:
        voltage_spectrum = np.zeros((len(channels), len(frequencies)), dtype=np.complex)
        for i_ch, ch in enumerate(channels):
            voltage_spectrum[i_ch] = np.sum(efield_antenna_factor[i_ch] * np.array([spectrum[1], spectrum[2]]), axis=0)
        return voltage_spectrum
    else:
        voltage_trace = np.zeros((len(channels), 2 * (len(frequencies) - 1)), dtype=np.complex)
        for i_ch, ch in enumerate(channels):
            voltage_trace[i_ch] = fft.freq2time(np.sum(efield_antenna_factor[i_ch] * np.array([spectrum[1], spectrum[2]]), axis=0), electric_field.get_sampling_rate())
        return np.real(voltage_trace)
        def obj_xcorr(params):
            if(len(params) == 3):
                slope, ratio2, phase2 = params
                ratio = (np.arctan(ratio2) + np.pi * 0.5) / np.pi  # project -inf..inf on 0..1
            elif(len(params) == 2):
                slope, ratio2 = params
                phase2 = 0
                ratio = (np.arctan(ratio2) + np.pi * 0.5) / np.pi  # project -inf..inf on 0..1
            elif(len(params) == 1):
                phase2 = 0
                ratio = 0
                slope = params[0]
            phase = np.arctan(phase2)  # project -inf..+inf to -0.5 pi..0.5 pi

            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(1 - ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            chi2 = 0

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            positions = np.zeros(n_channels, dtype=np.int)
            max_xcorrs = np.zeros(n_channels)
            # first determine the position with the larges xcorr
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
                xcorr = np.abs(hp.get_normalized_xcorr(trace, analytic_traces[iCh]))
                positions[iCh] = np.argmax(np.abs(xcorr)) + 1
                max_xcorrs[iCh] = xcorr.max()
                chi2 -= xcorr.max()
            logger.debug("ratio = {:.2f}, slope = {:.4g}, phase = {:.0f} ({:.4f}), chi2 = {:.4g}".format(ratio, slope, phase / units.deg, phase2, chi2))
            return chi2
        def obj_amplitude_second_order(params, slope, phase, pos, compare='hilbert', debug_obj=0):
            ampPhi, ampTheta, second_order = params
            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ampTheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass, quadratic_term=second_order, quadratic_term_offset=bandpass[0])
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(ampPhi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass, quadratic_term=second_order, quadratic_term_offset=bandpass[0])
            chi2 = 0
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                fig, ax = plt.subplots(5, 2, sharex=False, figsize=(20, 10))

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            # first determine the position with the larges xcorr
            channel_max = 0
            for trace in V_timedomain:
                if np.max(np.abs(trace)) > channel_max:
                    channel_max = np.max(np.abs(trace))
                    argmax = np.argmax(np.abs(trace))
                    imin = np.int(max(argmax - 50 * sampling_rate, 0))
                    imax = np.int(argmax + 50 * sampling_rate)
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
                if compare == 'trace':
                    tmp = np.sum(np.abs(trace[imin:imax] - np.roll(analytic_traces[iCh], pos)[imin:imax]) ** 2) / noise_RMS ** 2
                elif compare == 'abs':
                    tmp = np.sum(np.abs(np.abs(trace[imin:imax]) - np.abs(np.roll(analytic_traces[iCh], pos)[imin:imax])) ** 2) / noise_RMS ** 2
                elif compare == 'hilbert':
                    tmp = np.sum(np.abs(np.abs(signal.hilbert(trace[imin:imax])) - np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos)[imin:imax]))) ** 2) / noise_RMS ** 2
                else:
                    raise NameError('Unsupported value for parameter "compare": {}. Value must be "trace", "abs" or "hilbert".'.format(compare))
                chi2 += tmp
                if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                    ax[iCh][1].plot(np.array(trace) / units.mV, label='measurement', color='blue')
                    ax[iCh][1].plot(np.roll(analytic_traces[iCh], pos) / units.mV, '--', label='fit', color='orange')
                    ax[iCh][1].plot(np.abs(signal.hilbert(trace)) / units.mV, linestyle=':', color='blue')
                    ax[iCh][1].plot(np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos))) / units.mV, ':', label='fit', color='orange')
                    ax[iCh][0].plot(np.abs(fft.time2freq(trace, sampling_rate)) / units.mV, label='measurement')
                    ax[iCh][0].plot(np.abs(fft.time2freq(np.roll(analytic_traces[iCh], pos), sampling_rate)) / units.mV, '--', label='fit')
                    ax[iCh][0].set_xlim([0, 600])
                    ax[iCh][1].set_xlim([imin - 500, imax + 500])
                    ax[iCh][1].axvline(imin, linestyle='--', alpha=.8)
                    ax[iCh][1].axvline(imax, linestyle='--', alpha=.8)
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                sim_channel = station.get_sim_station().get_channel(0)[0]
                ax[4][0].plot(sim_channel.get_frequencies() / units.MHz, np.abs(pulse.get_analytic_pulse_freq(ampTheta, slope, phase, len(sim_channel.get_times()), sim_channel.get_sampling_rate(), bandpass=bandpass, quadratic_term=second_order)), '--', color='orange')
                ax[4][0].plot(sim_channel.get_frequencies() / units.MHz, np.abs(station.get_sim_station().get_channel(0)[0].get_frequency_spectrum()[1]), color='blue')
                ax[4][1].plot(sim_channel.get_frequencies() / units.MHz, np.abs(pulse.get_analytic_pulse_freq(ampPhi, slope, phase, len(sim_channel.get_times()), sim_channel.get_sampling_rate(), bandpass=bandpass, quadratic_term=second_order)), '--', color='orange')
                ax[4][1].plot(sim_channel.get_frequencies() / units.MHz, np.abs(sim_channel.get_frequency_spectrum()[2]), color='blue')
                ax[4][0].set_xlim([20, 500])
                ax[4][1].set_xlim([20, 500])
            logger.debug("amp phi = {:.4g}, amp theta = {:.4g}, slope = {:.4g} chi2 = {:.8g}".format(ampPhi, ampTheta, slope, chi2))
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                fig.tight_layout()
                plt.show()
                self.i_slope_fit_iterations = 0
            self.i_slope_fit_iterations += 1
            return chi2
예제 #6
0
    def run(self, evt, station, det):
        t = time.time()

        # access simulated efield and high level parameters
        sim_station = station.get_sim_station()
        sim_station_id = sim_station.get_id()

        # loop over all channels
        for efield in sim_station.get_electric_fields():
            # one efield might be valid for multiple channels, hence we loop over all channels this efiels is valid for,
            # convolve each trace with the antenna response for the given angles
            # and transform it to the time domain to calculate the max. amplitude
            channel_ids = efield.get_channel_ids()
            for channel_id in channel_ids:
                logger.debug('channel id {}'.format(channel_id))

                zenith = efield[efp.zenith]
                azimuth = efield[efp.azimuth]

                ff = efield.get_frequencies()
                efield_fft = efield.get_frequency_spectrum()

                # get antenna pattern for current channel
                antenna_model = det.get_antenna_model(sim_station_id,
                                                      channel_id, zenith)
                antenna_pattern = self.antenna_provider.load_antenna_pattern(
                    antenna_model, interpolation_method='complex')
                ori = det.get_antenna_orientation(sim_station_id, channel_id)
                logger.debug("zen {:.0f}, az {:.0f}".format(
                    zenith / units.deg, azimuth / units.deg))
                VEL = antenna_pattern.get_antenna_response_vectorized(
                    ff, zenith, azimuth, *ori)

                # Apply antenna response to electric field
                voltage_fft = efield_fft[2] * VEL['phi'] + efield_fft[1] * VEL[
                    'theta']

                # Remove DC offset
                voltage_fft[np.where(ff < 5 * units.MHz)] = 0.

                voltage = fft.freq2time(voltage_fft,
                                        efield.get_sampling_rate())
                h = np.abs(signal.hilbert(voltage))
                maximum = np.abs(voltage).max()
                maximum_envelope = h.max()

                if not efield.has_parameter(efp.max_amp_antenna):
                    efield[efp.max_amp_antenna] = {}
                    efield[efp.max_amp_antenna_envelope] = {}
                efield[efp.max_amp_antenna][channel_id] = maximum
                efield[efp.
                       max_amp_antenna_envelope][channel_id] = maximum_envelope

        self.__t += time.time() - t
예제 #7
0
def fold_efields(efield, zenith, azimuth, antenna_orientation,
                 antenna_pattern):
    """
	A function to do fold efields with the antenna response

	Apply the complex response of the antenna (the vector effective length)
	to an efield, and return the efield after the antenna

	Parameters
	----------
	signal: icetradio.I3EField
		the efield at the antenna

	zenith: float
		the zenith angle (in radians!) of the signal incident on the antenna

	azimuth: float
		the azimuth angle (in radians!) of the signal incident on the antenna

	antenna_orientation: array
		array of floats, specifically the orientation_theta, orientation_phi,
		rotation_theta, and rotation_phi, as they are defined in the NuRadioReco framework
		see https://nu-radio.github.io/NuRadioReco/pages/detector_database_fields.html#antenna-table
		or also the definitions in I3IceAntennaGeo
		https://code.icecube.wisc.edu/projects/icecube/browser/IceCube/sandbox/brianclark/ehe/radio/trunk/dataclasses/public/dataclasses/geometry/I3IceAntennaGeo.h 

	antenna_pattern: NuRadioReco.detector.antennapattern
		the antenna pattern for this antenna

	Returns
	-------
	trace: 
		the voltage trace that will be observed after being folded with the antenna
	"""

    # get the frequencies where the efield needs to be evaluated
    ff = util_dataclasses.get_frequencies_I3EField(efield)

    # get the fourier transforms of the field
    eTheta_freq = fft.time2freq(efield.eTheta.trace,
                                efield.eTheta.samplingRate)
    ePhi_freq = fft.time2freq(efield.ePhi.trace, efield.ePhi.samplingRate)

    # get the vector effective length (VEL)
    antenna_response = antenna_pattern.get_antenna_response_vectorized(
        ff, zenith, azimuth, *antenna_orientation)
    VEL = np.array([antenna_response['theta'], antenna_response['phi']])
    voltage_fft = np.sum(VEL * np.array([eTheta_freq, ePhi_freq]), axis=0)

    # we need to make sure to cancel out the DC offset
    voltage_fft[np.where(ff < 5 * units.MHz)] = 0.
    voltage_trace = fft.freq2time(voltage_fft, efield.eR.samplingRate)
    return voltage_trace
예제 #8
0
def apply_amplifier_filter(voltage_trace, dT, amplifier_filter_response):
    """
	A function to apply amplifier+filter responses to a voltage trace

	Apply the complex response of the amplifier and filter (magnitude and phase)
	to a voltage trace, and return the trace after amplification

	Parameters
	----------
	voltage_trace: array
		the trace to which we want to apply the amplifier and filter

	dT: float
		the time between samples of the voltage trace

	azimuth: float
		the azimuth angle (in radians!) of the signal incident on the antenna

	amplifier_filter_response: array
		The dict containing the amplifier + filter response
		As loaded in the load_filter_amplifier_response function
	

	Returns
	-------
	trace: 
		the voltage trace that will be observed after applying the amps + filters

	"""
    orig_frequencies = amplifier_filter_response['frequencies']
    orig_phase = amplifier_filter_response['phase']
    orig_gain = amplifier_filter_response['gain']

    # interpolate the phase and gain
    interp_phase = interp1d(orig_frequencies,
                            np.unwrap(orig_phase),
                            bounds_error=False,
                            fill_value=0)
    interp_gain = interp1d(orig_frequencies,
                           orig_gain,
                           bounds_error=False,
                           fill_value=0)

    num_samples = len(voltage_trace)  # the number of samples
    frequencies = np.fft.rfftfreq(num_samples, dT)
    gain = interp_gain(frequencies)
    phase = np.exp(1j * interp_phase(frequencies))

    the_fft = fft.time2freq(voltage_trace, 1. / dT)
    the_fft *= (gain * phase)
    the_result_trace = fft.freq2time(the_fft, 1. / dT)
    return the_result_trace
예제 #9
0
    def tunnel_diode(self, channel):
        """
        Calculate a signal as processed by the tunnel diode.
        The given signal is convolved with the tunnel diode response as in
        AraSim.

        The diode model used in this module returns a dimensionless power trace,
        where the antenna resistance is only a normalisation for the final
        voltage. That's why the antenna resistance has been fixed to a value
        of 8.5 ohms.

        Parameters
        ----------
        channel: Channel
            Signal to be processed by the tunnel diode.

        Returns
        -------
        trace_after_tunnel_diode: array
            Signal output of the tunnel diode for the input `channel`.
            Careful! This trace is dimensionless and comes from a convolution
            of the power with the diode response.
        """
        t_max = 1e-7 * units.s
        antenna_resistance = 8.5 * units.ohm
        n_pts = int(t_max * channel.get_sampling_rate())
        times = np.linspace(0, t_max, n_pts + 1)
        diode_resp = self._td_fdown1(times) + self._td_fdown2(times)
        t_slice = times > self._td_args['up'][1]
        diode_resp[t_slice] += self._td_fup(times[t_slice])
        conv = scipy.signal.convolve(channel.get_trace() ** 2 / antenna_resistance,
                                     diode_resp, mode='full')
        # conv multiplied by dt so that the amplitude stays constant for
        # varying dts (determined emperically, see ARVZAskaryanSignal comments)
        # Setting output
        trace_after_tunnel_diode = conv / channel.get_sampling_rate()
        trace_after_tunnel_diode = trace_after_tunnel_diode[:channel.get_trace().shape[0]]

        # We filter the output if the band is specified
        if self._output_passband != (None, None):

            sampling_rate = channel.get_sampling_rate()
            trace_spectrum = time2freq(trace_after_tunnel_diode, sampling_rate)
            frequencies = np.linspace(0, sampling_rate / 2, len(trace_spectrum))
            if self._output_passband[0] is None:
                b, a = butter(6, self._output_passband[1], 'lowpass', analog=True)
            else:
                b, a = butter(6, self._output_passband, 'bandpass', analog=True)
            w, h = freqs(b, a, frequencies)
            trace_after_tunnel_diode = freq2time(h * trace_spectrum, sampling_rate)

        return trace_after_tunnel_diode
예제 #10
0
    def get_trace(self):
        """
        returns the time trace. If the frequency spectrum was modified before,
        an ifft is performed automatically to have the time domain representation
        up to date.

        Returns: 1 or N dimensional np.array of floats
            the time trace
        """
        if(not self.__time_domain_up_to_date):
            self._time_trace = fft.freq2time(self._frequency_spectrum, self._sampling_rate)
            self.__time_domain_up_to_date = True
            self._frequency_spectrum = None
        return np.copy(self._time_trace)
예제 #11
0
def loop(zipped):

    threshold = float(zipped[0])
    seed = int(zipped[1])

    station = NuRadioReco.framework.station.Station(station_id)
    evt = NuRadioReco.framework.event.Event(0, 0)

    channelGenericNoiseAdder = NuRadioReco.modules.channelGenericNoiseAdder.channelGenericNoiseAdder(
    )
    channelGenericNoiseAdder.begin(seed=seed)

    for channel_id in channel_ids:
        # Noise rms is amplified to greater than Vrms so that, after filtering, its the Vrms we expect
        spectrum = channelGenericNoiseAdder.bandlimited_noise(
            min_freq,
            max_freq,
            n_samples,
            sampling_rate,
            amplitude,
            type="rayleigh",
            time_domain=False)

        trace = fft.freq2time(spectrum * filt, sampling_rate)

        channel = NuRadioReco.framework.channel.Channel(channel_id)
        channel.set_trace(trace, sampling_rate)
        station.add_channel(channel)

    threshold_ = threshold * np.power(Vrms, 2.0)

    triggered = triggerSimulator.run(evt,
                                     station,
                                     det,
                                     Vrms,
                                     threshold_,
                                     triggered_channels=channels,
                                     phasing_angles=default_angles,
                                     ref_index=1.75,
                                     trigger_name='primary_phasing',
                                     trigger_adc=False,
                                     adc_output='voltage',
                                     trigger_filter=None,
                                     upsampling_factor=upsampling_factor,
                                     window=window_length,
                                     step=step_length)

    return triggered
예제 #12
0
    def get_filtered_trace(self, passband, filter_type='butter', order=10):
        """
        Returns the trace after applying a filter to it. This does not change the stored trace.

        Parameters:
        --------------
        passband: list of floats
            lower and upper bound of the filter passband
        filter_type: string
            type of the applied filter. Options are rectangular, butter and butterabs
        order: int
            Order of the Butterworth filter, if the filter types butter or butterabs are chosen
        """
        spec = copy.copy(self.get_frequency_spectrum())
        freq = self.get_frequencies()
        filter_response = bandpass_filter.get_filter_response(freq, passband, filter_type, order)
        spec *= filter_response
        return fft.freq2time(spec, self.get_sampling_rate())
예제 #13
0
def delay_trace(trace, sampling_frequency, time_delay, delayed_samples):
    """
    Delays a trace by transforming it to frequency and multiplying by phases.
    Since this method is cyclic, the trace has to be cropped. It only accepts
    positive delays, so some samples from the beginning are thrown away and then
    some samples from the end so that the total number of samples is equal to
    the argument delayed samples.

    Parameters
    ----------
    trace: array of floats
        Array containing the trace
    sampling_frequency: float
        Sampling rate for the trace
    time_delay: float
        Time delay used for transforming the trace. Must be positive or 0
    delayed_samples: integer
        Number of samples that the delayed trace must contain

    Returns
    -------
    delayed_trace: array of floats
        The delayed, cropped trace
    """

    if time_delay < 0:
        msg = 'Time delay must be positive'
        raise ValueError(msg)

    n_samples = len(trace)

    spectrum = fft.time2freq(trace, sampling_frequency)
    frequencies = np.fft.rfftfreq(n_samples, 1 / sampling_frequency)

    spectrum *= np.exp(-1j * 2 * np.pi * frequencies * time_delay)

    delayed_trace = fft.freq2time(spectrum, sampling_frequency)

    init_sample = int(time_delay * sampling_frequency) + 1

    delayed_trace = delayed_trace[init_sample:None]
    delayed_trace = delayed_trace[:delayed_samples]

    return delayed_trace
 def __calculate_time_delays_amp(self, amp_type):
     """
     helper function to calculate the time delay of the amp for a delta pulse
     """
     amp_response_f = analog_components.load_amp_response(amp_type)
     sampling_rate = 10 * units.GHz  # assume a huge sampling rate to have a good time resolution
     n = 2 ** 12
     trace = np.zeros(n)
     trace[n // 2] = 1
     max_time = trace.argmax() / sampling_rate
     spec = fft.time2freq(trace, sampling_rate)
     ff = np.fft.rfftfreq(n, 1. / sampling_rate)
     amp_response_gain = amp_response_f['gain'](ff)
     amp_response_phase = amp_response_f['phase'](ff)
     mask = (ff < 70 * units.MHz) & (ff > 40 * units.MHz)
     spec[~mask] = 0
     trace2 = fft.freq2time(spec * amp_response_gain * amp_response_phase, sampling_rate)
     max_time2 = np.abs(trace2).argmax() / sampling_rate
     return max_time2 - max_time
    def __calculate_time_delays_cable(self):
        """
        helper function to calculate the time delay of the amp for a delta pulse
        """
        sampling_rate = 10 * units.GHz  # assume a huge sampling rate to have a good time resolution
        n = 2 ** 12
        trace = np.zeros(n)
        trace[n // 2] = 1
        max_time = trace.argmax() / sampling_rate
        spec = fft.time2freq(trace, sampling_rate)
        ff = np.fft.rfftfreq(n, 1. / sampling_rate)
        response = analog_components.get_cable_response(ff)
        response_gain = response['gain']
        response_phase = response['phase']
        trace2 = fft.freq2time(spec * response_gain * response_phase, sampling_rate)
#         import matplotlib.pyplot as plt
#         fig, ax = plt.subplots(1, 1)
#         ax.plot(trace)
#         ax.plot(trace2)
#         plt.show()
        max_time2 = np.abs(trace2).argmax() / sampling_rate
        return max_time2 - max_time
예제 #16
0
def get_analytic_pulse(amp_p0, amp_p1, phase_p0, n_samples_time,
                       sampling_rate,
                       phase_p1=0, bandpass=None,
                       quadratic_term=0, quadratic_term_offset=0):
    """
    Analytic pulse as described in PhD thesis Glaser and NuRadioReco paper in the time domain

    Parameters
    ----------
    amp_p0: float
        amplitude parameter of analytic pulse
    amp_p1:
        slope parameter of analytic pulse
    phase_p0:
        phase parameter of analytic pulse
    n_samples_time:
        numer of samples in time-domain
    sampling_rate:
        sampling rate of trace
    phase_p1:
        default 0

    bandpass:
        default None

    quadratic_term:
        default 0

    quadratic_term_offset:
        default 0

    """
    xx = get_analytic_pulse_freq(amp_p0, amp_p1, phase_p0, n_samples_time,
                                 sampling_rate, phase_p1=phase_p1,
                                 bandpass=bandpass,
                                 quadratic_term=quadratic_term,
                                 quadratic_term_offset=quadratic_term_offset)
    return fft.freq2time(xx, sampling_rate)
        def obj_amplitude(params, slope, phase, pos, debug_obj=0):
            if(len(params) == 2):
                ampPhi, ampTheta = params
            elif(len(params) == 1):
                ampPhi = params[0]
                ampTheta = 0
            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ampTheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(ampPhi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            chi2 = 0

            if(debug_obj):
                fig, ax = plt.subplots(4, 2, sharex=True)

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            # first determine the position with the larges xcorr
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)

                argmax = np.argmax(np.abs(trace))
                imin = np.int(argmax - 30 * sampling_rate)
                imax = np.int(argmax + 50 * sampling_rate)

                tmp = np.sum(np.abs(trace[imin:imax] - np.roll(analytic_traces[iCh], pos)[imin:imax]) / noise_RMS)
                chi2 += tmp ** 2
                if(debug_obj):
                    ax[iCh][0].plot(trace, label='measurement')
                    ax[iCh][0].plot(np.roll(analytic_traces[iCh], pos), '--', label='fit')
                    ax[iCh][1].plot(trace - np.roll(analytic_traces[iCh], pos), label='delta')
                    ax[iCh][1].set_xlim(imin, imax)
            logger.debug("amp phi = {:.4g}, amp theta = {:.4g} , chi2 = {:.2g}".format(ampPhi, ampTheta, chi2))
            if(debug_obj):
                fig.suptitle("amp phi = {:.4g}, amp theta = {:.4g} , chi2 = {:.2g}".format(ampPhi, ampTheta, chi2))
                fig.tight_layout()
                plt.show()
            return chi2
예제 #18
0
def generate_signal(deposited_energy,
                    shower_axis,
                    em_or_had,
                    launch_vector,
                    distance,
                    arrival_time,
                    n_index,
                    attenuation_values,
                    dt,
                    n_samples,
                    model,
                    seed,
                    keep_unattenuated_fields=False):
    """
	A function to generate askaryan fields at the antennas

	Get the askaryan signals/fields at the antenna. This means that the fields
	returned by this function will already include the polarization factors,
	the 1/R, and the attenuation due to the ice.

	Parameters
	----------
	deposited_energy: double or float
		energy deposited in the shower in eV
	
	shower_axis: I3Position
		the shower axis
	
	launch_vector: I3Position
		the launch vector of the ray that makes the signal

	distance: float
		the path length traveled by the signal in m (including ray bending!)

	arrival_time: float
		the time the field arrives at the antenna, in seconds (including ray bending!)

	n_index: float
		the index of refraction at the vertex

	atttenuation_values: complex np array
		the complex frequency-dependent attenuation factors

	dt: float
		the time between samples for the askaryan emission, in seconds

	n_samples: int
		the number of samples to have in the Askaryan emission

	model: string
		what Askaryan model should be used to generate the emission
		options are described in NuRadioMC.SignalGen.askaryan
		https://github.com/nu-radio/NuRadioMC/blob/master/NuRadioMC/SignalGen/askaryan.py

	seed: int
		what random number seed should be used in generating the askaryan emission

	keep_unattenuated_fields: bool
		whether or not to keep a copy of the E-fields that does not have attenuation factors applied
		default is False, to reduce file output sizes

	Returns
	-------
	signal: I3RadioSignal
		the radio signal container for this event
	"""

    local_launch_vector = util_dataclasses.i3pos_to_np(launch_vector)
    local_shower_axis = util_dataclasses.i3pos_to_np(shower_axis)

    viewing_angle = hp.get_angle(local_shower_axis, local_launch_vector)

    signal = askaryan.get_time_trace(energy=deposited_energy,
                                     theta=viewing_angle,
                                     N=n_samples,
                                     dt=dt,
                                     shower_type=em_or_had,
                                     n_index=n_index,
                                     R=distance,
                                     model=model,
                                     seed=seed)

    signal_spectrum = fft.time2freq(signal, 1. / dt)
    attenuated_signal_spectrum = signal_spectrum * attenuation_values
    attenuated_signal = fft.freq2time(attenuated_signal_spectrum, 1. / dt)

    # calculate the polarization
    polarization_direction_onsky = util_geo.calculate_polarization_vector(
        local_launch_vector, local_shower_axis)
    icetray.logging.log_debug("Polarization direction on sky {}".format(
        polarization_direction_onsky))

    # create the e-fields at the antenna
    this_eR_attenuated, this_eTheta_attenuated, this_ePhi_attenuated = np.outer(
        polarization_direction_onsky, attenuated_signal)

    # store the eR, eTheta, ePhi components in trace for attenuated field
    sampling_rate = 1. / dt
    eR_attenuated = util_dataclasses.fill_I3Trace(this_eR_attenuated,
                                                  arrival_time, sampling_rate)
    eTheta_attenuated = util_dataclasses.fill_I3Trace(this_eTheta_attenuated,
                                                      arrival_time,
                                                      sampling_rate)
    ePhi_attenuated = util_dataclasses.fill_I3Trace(this_ePhi_attenuated,
                                                    arrival_time,
                                                    sampling_rate)

    # put those traces into fields
    field_watt = util_dataclasses.fill_I3EField(eR_attenuated,
                                                eTheta_attenuated,
                                                ePhi_attenuated)

    # and finally, create and return a signal object
    signal = icetradio.I3RadioSignal()
    signal.view_angle = viewing_angle * icetray.I3Units.rad
    signal.polarization_vector = util_dataclasses.np_to_i3pos(
        polarization_direction_onsky, 'sph')
    signal.field_watt = field_watt

    if keep_unattenuated_fields:
        # make a copy of the fields that doesn't include the attenuation factor
        # we can generally *not* save this information as a space saving measure
        this_eR, this_eTheta, this_ePhi = np.outer(
            polarization_direction_onsky, signal)
        eR = util_dataclasses.fill_I3Trace(this_eR, arrival_time,
                                           sampling_rate)
        eTheta = util_dataclasses.fill_I3Trace(this_eTheta, arrival_time,
                                               sampling_rate)
        ePhi = util_dataclasses.fill_I3Trace(this_ePhi, arrival_time,
                                             sampling_rate)
        field_noatt = util_dataclasses.fill_I3EField(eR, eTheta, ePhi)
        signal.field_noatt = field_noatt

    return signal
    def run(self, evt, station, det, debug=False, debug_plotpath=None,
            use_channels=None,
            bandpass=None,
            use_MC_direction=False):
        """
        run method. This function is executed for each event

        Parameters
        ---------
        evt
        station
        det
        debug: bool
            if True debug plotting is enables
        debug_plotpath: string or None
            if not None plots will be saved to a file rather then shown. Plots will
            be save into the `debug_plotpath` directory
        use_channels: array of ints (default: [0, 1, 2, 3])
            the channel ids to use for the electric field reconstruction
            default: 0 - 3
        bandpass: [float, float] (default: [100 * units.MHz, 500 * units.MHz])
            the lower and upper frequecy for which the analytic pulse is calculated.
            A butterworth filter of 10th order and a rectangular filter is applied.
            default 100 - 500 MHz
        use_MC_direction: bool
            use simulated direction instead of reconstructed direction
        """
        if use_channels is None:
            use_channels = [0, 1, 2, 3]
        if bandpass is None:
            bandpass = [100 * units.MHz, 500 * units.MHz]
        self.__counter += 1
        station_id = station.get_id()
        logger.info("event {}, station {}".format(evt.get_id(), station_id))
        if use_MC_direction and (station.get_sim_station() is not None):
            zenith = station.get_sim_station()[stnp.zenith]
            azimuth = station.get_sim_station()[stnp.azimuth]
            sim_present = True
        else:
            logger.warning("Using reconstructed angles as no simulation present")
            zenith = station[stnp.zenith]
            azimuth = station[stnp.azimuth]
            sim_present = False

        efield_antenna_factor, V, V_timedomain = get_array_of_channels(station, use_channels,
                                                                       det, zenith, azimuth, self.antenna_provider,
                                                                       time_domain=True)
        sampling_rate = station.get_channel(0).get_sampling_rate()
        n_samples_time = V_timedomain.shape[1]

        noise_RMS = det.get_noise_RMS(station.get_id(), 0)

        def obj_xcorr(params):
            if(len(params) == 3):
                slope, ratio2, phase2 = params
                ratio = (np.arctan(ratio2) + np.pi * 0.5) / np.pi  # project -inf..inf on 0..1
            elif(len(params) == 2):
                slope, ratio2 = params
                phase2 = 0
                ratio = (np.arctan(ratio2) + np.pi * 0.5) / np.pi  # project -inf..inf on 0..1
            elif(len(params) == 1):
                phase2 = 0
                ratio = 0
                slope = params[0]
            phase = np.arctan(phase2)  # project -inf..+inf to -0.5 pi..0.5 pi

            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(1 - ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            chi2 = 0

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            positions = np.zeros(n_channels, dtype=np.int)
            max_xcorrs = np.zeros(n_channels)
            # first determine the position with the larges xcorr
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
                xcorr = np.abs(hp.get_normalized_xcorr(trace, analytic_traces[iCh]))
                positions[iCh] = np.argmax(np.abs(xcorr)) + 1
                max_xcorrs[iCh] = xcorr.max()
                chi2 -= xcorr.max()
            logger.debug("ratio = {:.2f}, slope = {:.4g}, phase = {:.0f} ({:.4f}), chi2 = {:.4g}".format(ratio, slope, phase / units.deg, phase2, chi2))
            return chi2

        def obj_amplitude(params, slope, phase, pos, debug_obj=0):
            if(len(params) == 2):
                ampPhi, ampTheta = params
            elif(len(params) == 1):
                ampPhi = params[0]
                ampTheta = 0
            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ampTheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(ampPhi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            chi2 = 0

            if(debug_obj):
                fig, ax = plt.subplots(4, 2, sharex=True)

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            # first determine the position with the larges xcorr
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)

                argmax = np.argmax(np.abs(trace))
                imin = np.int(argmax - 30 * sampling_rate)
                imax = np.int(argmax + 50 * sampling_rate)

                tmp = np.sum(np.abs(trace[imin:imax] - np.roll(analytic_traces[iCh], pos)[imin:imax]) / noise_RMS)
                chi2 += tmp ** 2
                if(debug_obj):
                    ax[iCh][0].plot(trace, label='measurement')
                    ax[iCh][0].plot(np.roll(analytic_traces[iCh], pos), '--', label='fit')
                    ax[iCh][1].plot(trace - np.roll(analytic_traces[iCh], pos), label='delta')
                    ax[iCh][1].set_xlim(imin, imax)
            logger.debug("amp phi = {:.4g}, amp theta = {:.4g} , chi2 = {:.2g}".format(ampPhi, ampTheta, chi2))
            if(debug_obj):
                fig.suptitle("amp phi = {:.4g}, amp theta = {:.4g} , chi2 = {:.2g}".format(ampPhi, ampTheta, chi2))
                fig.tight_layout()
                plt.show()
            return chi2

        def obj_amplitude_slope(params, phase, pos, compare='hilbert', debug_obj=0):
            ampPhi, ampTheta, slope = params
            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ampTheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(ampPhi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
            chi2 = 0
            if(debug_obj and self.i_slope_fit_iterations % 25 == 0):
                fig, ax = plt.subplots(4, 2, sharex=False, figsize=(20, 10))

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            # first determine the position with the larges xcorr
            channel_max = 0
            for trace in V_timedomain:
                if np.max(np.abs(trace)) > channel_max:
                    channel_max = np.max(np.abs(trace))
                    argmax = np.argmax(np.abs(trace))
                    imin = np.int(max(argmax - 50 * sampling_rate, 0))
                    imax = np.int(argmax + 50 * sampling_rate)
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
                if compare == 'trace':
                    tmp = np.sum(np.abs(trace[imin:imax] - np.roll(analytic_traces[iCh], pos)[imin:imax]) ** 2) / noise_RMS ** 2
                elif compare == 'abs':
                    tmp = np.sum(np.abs(np.abs(trace[imin:imax]) - np.abs(np.roll(analytic_traces[iCh], pos)[imin:imax])) ** 2) / noise_RMS ** 2
                elif compare == 'hilbert':
                    tmp = np.sum(np.abs(np.abs(signal.hilbert(trace[imin:imax])) - np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos)[imin:imax]))) ** 2) / noise_RMS ** 2
                else:
                    raise NameError('Unsupported value for parameter "compare": {}. Value must be "trace", "abs" or "hilbert".'.format(compare))
                chi2 += tmp
                if(debug_obj and self.i_slope_fit_iterations % 25 == 0):
                    ax[iCh][1].plot(np.array(trace) / units.mV, label='measurement', color='blue')
                    ax[iCh][1].plot(np.roll(analytic_traces[iCh], pos) / units.mV, '--', label='fit', color='orange')
                    ax[iCh][1].plot(np.abs(signal.hilbert(trace)) / units.mV, linestyle=':', color='blue')
                    ax[iCh][1].plot(np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos))) / units.mV, ':', label='fit', color='orange')
                    # ax[iCh][1].plot(trace - np.roll(analytic_traces[iCh], pos), label='delta')
                    ax[iCh][0].plot(np.abs(fft.time2freq(trace, sampling_rate)) / units.mV, label='measurement')
                    ax[iCh][0].plot(np.abs(fft.time2freq(np.roll(analytic_traces[iCh], pos), sampling_rate)) / units.mV, '--', label='fit')
                    ax[iCh][0].set_xlim([0, 600])
                    ax[iCh][1].set_xlim([imin - 500, imax + 500])
                    ax[iCh][1].axvline(imin, linestyle='--', alpha=.8)
                    ax[iCh][1].axvline(imax, linestyle='--', alpha=.8)
            logger.debug("amp phi = {:.4g}, amp theta = {:.4g}, slope = {:.4g} chi2 = {:.8g}".format(ampPhi, ampTheta, slope, chi2))
            if(debug_obj and self.i_slope_fit_iterations % 25 == 0):
                fig.tight_layout()
                plt.show()
                self.i_slope_fit_iterations = 0
            self.i_slope_fit_iterations += 1
            return chi2

        def obj_amplitude_second_order(params, slope, phase, pos, compare='hilbert', debug_obj=0):
            ampPhi, ampTheta, second_order = params
            analytic_pulse_theta = pulse.get_analytic_pulse_freq(ampTheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass, quadratic_term=second_order, quadratic_term_offset=bandpass[0])
            analytic_pulse_phi = pulse.get_analytic_pulse_freq(ampPhi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass, quadratic_term=second_order, quadratic_term_offset=bandpass[0])
            chi2 = 0
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                fig, ax = plt.subplots(5, 2, sharex=False, figsize=(20, 10))

            n_channels = len(V_timedomain)
            analytic_traces = np.zeros((n_channels, n_samples_time))
            # first determine the position with the larges xcorr
            channel_max = 0
            for trace in V_timedomain:
                if np.max(np.abs(trace)) > channel_max:
                    channel_max = np.max(np.abs(trace))
                    argmax = np.argmax(np.abs(trace))
                    imin = np.int(max(argmax - 50 * sampling_rate, 0))
                    imax = np.int(argmax + 50 * sampling_rate)
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta, analytic_pulse_phi]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
                if compare == 'trace':
                    tmp = np.sum(np.abs(trace[imin:imax] - np.roll(analytic_traces[iCh], pos)[imin:imax]) ** 2) / noise_RMS ** 2
                elif compare == 'abs':
                    tmp = np.sum(np.abs(np.abs(trace[imin:imax]) - np.abs(np.roll(analytic_traces[iCh], pos)[imin:imax])) ** 2) / noise_RMS ** 2
                elif compare == 'hilbert':
                    tmp = np.sum(np.abs(np.abs(signal.hilbert(trace[imin:imax])) - np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos)[imin:imax]))) ** 2) / noise_RMS ** 2
                else:
                    raise NameError('Unsupported value for parameter "compare": {}. Value must be "trace", "abs" or "hilbert".'.format(compare))
                chi2 += tmp
                if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                    ax[iCh][1].plot(np.array(trace) / units.mV, label='measurement', color='blue')
                    ax[iCh][1].plot(np.roll(analytic_traces[iCh], pos) / units.mV, '--', label='fit', color='orange')
                    ax[iCh][1].plot(np.abs(signal.hilbert(trace)) / units.mV, linestyle=':', color='blue')
                    ax[iCh][1].plot(np.abs(signal.hilbert(np.roll(analytic_traces[iCh], pos))) / units.mV, ':', label='fit', color='orange')
                    ax[iCh][0].plot(np.abs(fft.time2freq(trace, sampling_rate)) / units.mV, label='measurement')
                    ax[iCh][0].plot(np.abs(fft.time2freq(np.roll(analytic_traces[iCh], pos), sampling_rate)) / units.mV, '--', label='fit')
                    ax[iCh][0].set_xlim([0, 600])
                    ax[iCh][1].set_xlim([imin - 500, imax + 500])
                    ax[iCh][1].axvline(imin, linestyle='--', alpha=.8)
                    ax[iCh][1].axvline(imax, linestyle='--', alpha=.8)
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                sim_channel = station.get_sim_station().get_channel(0)[0]
                ax[4][0].plot(sim_channel.get_frequencies() / units.MHz, np.abs(pulse.get_analytic_pulse_freq(ampTheta, slope, phase, len(sim_channel.get_times()), sim_channel.get_sampling_rate(), bandpass=bandpass, quadratic_term=second_order)), '--', color='orange')
                ax[4][0].plot(sim_channel.get_frequencies() / units.MHz, np.abs(station.get_sim_station().get_channel(0)[0].get_frequency_spectrum()[1]), color='blue')
                ax[4][1].plot(sim_channel.get_frequencies() / units.MHz, np.abs(pulse.get_analytic_pulse_freq(ampPhi, slope, phase, len(sim_channel.get_times()), sim_channel.get_sampling_rate(), bandpass=bandpass, quadratic_term=second_order)), '--', color='orange')
                ax[4][1].plot(sim_channel.get_frequencies() / units.MHz, np.abs(sim_channel.get_frequency_spectrum()[2]), color='blue')
                ax[4][0].set_xlim([20, 500])
                ax[4][1].set_xlim([20, 500])
            logger.debug("amp phi = {:.4g}, amp theta = {:.4g}, slope = {:.4g} chi2 = {:.8g}".format(ampPhi, ampTheta, slope, chi2))
            if(debug_obj and self.i_slope_fit_iterations % 50 == 0):
                fig.tight_layout()
                plt.show()
                self.i_slope_fit_iterations = 0
            self.i_slope_fit_iterations += 1
            return chi2

        method = "Nelder-Mead"
        options = {'maxiter': 1000,
                   'disp': True}

        res = opt.minimize(obj_xcorr, x0=[-1], method=method, options=options)
        logger.info("slope xcorr fit, slope = {:.3g} with fmin = {:.3f}".format(res.x[0], res.fun))
        # plot objective function
        phase = 0
        ratio = 0
        slope = res.x[0]
        if slope > 0 or slope < -50:  # sanity check
            slope = -1.9
        analytic_pulse_theta_freq = pulse.get_analytic_pulse_freq(ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
        analytic_pulse_phi_freq = pulse.get_analytic_pulse_freq(1 - ratio, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)

        n_channels = len(V_timedomain)
        analytic_traces = np.zeros((n_channels, n_samples_time))
        positions = np.zeros(n_channels, dtype=np.int)
        max_xcorrs = np.zeros(n_channels)
        for iCh, trace in enumerate(V_timedomain):
            analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta_freq, analytic_pulse_phi_freq]), axis=0)
            analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, sampling_rate)
            xcorr = np.abs(hp.get_normalized_xcorr(trace, analytic_traces[iCh]))
            positions[iCh] = np.argmax(np.abs(xcorr)) + 1
            max_xcorrs[iCh] = xcorr.max()
        pos = positions[np.argmax(max_xcorrs)]
        for iCh, trace in enumerate(V_timedomain):
            analytic_traces[iCh] = np.roll(analytic_traces[iCh], pos)

        res_amp = opt.minimize(obj_amplitude, x0=[1.], args=(slope, phase, pos, 0), method=method, options=options)
        logger.info("amplitude fit, Aphi = {:.3g} with fmin = {:.5e}".format(res_amp.x[0], res_amp.fun))
        res_amp = opt.minimize(obj_amplitude, x0=[res_amp.x[0], 0], args=(slope, phase, pos, 0), method=method, options=options)
        logger.info("amplitude fit, Aphi = {:.3g} Atheta = {:.3g} with fmin = {:.5e}".format(res_amp.x[0], res_amp.x[1], res_amp.fun))
        # counts number of iterations in the slope fit. Used so we do not need to show the plots every iteration
        self.i_slope_fit_iterations = 0
        res_amp_slope = opt.minimize(obj_amplitude_slope, x0=[res_amp.x[0], res_amp.x[1], slope], args=(phase, pos, 'hilbert', False),
                                     method=method, options=options)

        # calculate uncertainties
        def Wrapper(params):
            return obj_amplitude_slope(params, phase, pos, 0)

        try:
            cov = covariance(Wrapper, res_amp_slope.x, 0.5, fast=True)
        except:
            cov = np.zeros((3, 3))
        logger.info("slope fit, Aphi = {:.3g}+-{:.3g} Atheta = {:.3g}+-{:.3g}, slope = {:.3g}+-{:.3g} with fmin = {:.5e}".format(
            res_amp_slope.x[0],
            cov[0, 0] ** 0.5,
            res_amp_slope.x[1], cov[1, 1] ** 0.5,
            res_amp_slope.x[2], cov[2, 2] ** 0.5,
            res_amp_slope.fun)
        )
        logger.info("covariance matrix \n{}".format(cov))
        if(cov[0, 0] > 0 and cov[1, 1] > 0 and cov[2, 2] > 0):
            logger.info("correlation matrix \n{}".format(hp.covariance_to_correlation(cov)))
        Aphi = res_amp_slope.x[0]
        Atheta = res_amp_slope.x[1]
        slope = res_amp_slope.x[2]
        Aphi_error = cov[0, 0] ** 0.5
        Atheta_error = cov[1, 1] ** 0.5

        # plot objective function
        if 0:
            fo, ao = plt.subplots(1, 1)
            ss = np.linspace(-6, -0, 100)
            oos = [obj_amplitude_slope([res_amp_slope.x[0], res_amp_slope.x[1], s], phase, pos) for s in ss]
            ao.plot(ss, oos)

            n = 10
            x = np.linspace(res_amp_slope.x[0] * 0.6, res_amp_slope.x[0] * 1.4, n)
            y = np.linspace(-5, -1, n)
            X, Y = np.meshgrid(x, y)
            Z = np.zeros((n, n))
            for i in range(n):
                for j in range(n):
                    Z[i, j] = obj_amplitude_slope([X[i, j], X[i, j] * res_amp_slope.x[1] / res_amp_slope.x[0], Y[i, j]], phase, pos)

            fig, ax = plt.subplots(1, 1)
            ax.pcolor(X, Y, Z, cmap='viridis_r', vmin=res_amp_slope.fun, vmax=res_amp_slope.fun * 2)

        analytic_pulse_theta = pulse.get_analytic_pulse(Atheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
        analytic_pulse_phi = pulse.get_analytic_pulse(Aphi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
        analytic_pulse_theta_freq = pulse.get_analytic_pulse_freq(Atheta, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)
        analytic_pulse_phi_freq = pulse.get_analytic_pulse_freq(Aphi, slope, phase, n_samples_time, sampling_rate, bandpass=bandpass)

        analytic_pulse_theta = np.roll(analytic_pulse_theta, pos)
        analytic_pulse_phi = np.roll(analytic_pulse_phi, pos)
        station_trace = np.array([np.zeros_like(analytic_pulse_theta), analytic_pulse_theta, analytic_pulse_phi])

        electric_field = NuRadioReco.framework.electric_field.ElectricField(use_channels)
        electric_field.set_trace(station_trace, sampling_rate)
        energy_fluence = trace_utilities.get_electric_field_energy_fluence(electric_field.get_trace(), electric_field.get_times())
        electric_field.set_parameter(efp.signal_energy_fluence, energy_fluence)
        electric_field.set_parameter_error(efp.signal_energy_fluence, np.array([0, Atheta_error, Aphi_error]))
        electric_field.set_parameter(efp.cr_spectrum_slope, slope)
        electric_field.set_parameter(efp.zenith, zenith)
        electric_field.set_parameter(efp.azimuth, azimuth)
        # calculate high level parameters
        x = np.sign(Atheta) * np.abs(Atheta) ** 0.5
        y = np.sign(Aphi) * np.abs(Aphi) ** 0.5
        sx = Atheta_error * 0.5
        sy = Aphi_error * 0.5
        pol_angle = np.arctan2(abs(y), abs(x))
        pol_angle_error = 1. / (x ** 2 + y ** 2) * (y ** 2 * sx ** 2 + x ** 2 + sy ** 2) ** 0.5  # gaussian error propagation
        logger.info("polarization angle = {:.1f} +- {:.1f}".format(pol_angle / units.deg, pol_angle_error / units.deg))
        electric_field.set_parameter(efp.polarization_angle, pol_angle)
        electric_field.set_parameter_error(efp.polarization_angle, pol_angle_error)

        # compute expeted polarization
        site = det.get_site(station.get_id())
        exp_efield = hp.get_lorentzforce_vector(zenith, azimuth, hp.get_magnetic_field_vector(site))
        cs = coordinatesystems.cstrafo(zenith, azimuth, site=site)
        exp_efield_onsky = cs.transform_from_ground_to_onsky(exp_efield)
        exp_pol_angle = np.arctan2(exp_efield_onsky[2], exp_efield_onsky[1])
        logger.info("expected polarization angle = {:.1f}".format(exp_pol_angle / units.deg))
        electric_field.set_parameter(efp.polarization_angle_expectation, exp_pol_angle)
        res_amp_second_order = opt.minimize(
            obj_amplitude_second_order,
            x0=[res_amp_slope.x[0], res_amp_slope.x[1], 0],
            args=(slope, phase, pos, 'hilbert', False),
            method=method,
            options=options
        )
        second_order_correction = res_amp_second_order.x[2]
        electric_field.set_parameter(efp.cr_spectrum_quadratic_term, second_order_correction)
        # figure out the timing of the electric field
        voltages_from_efield = trace_utilities.get_channel_voltage_from_efield(station, electric_field, use_channels, det, zenith, azimuth, self.antenna_provider, False)
        correlation = np.zeros(voltages_from_efield.shape[1] + station.get_channel(use_channels[0]).get_trace().shape[0] - 1)
        channel_trace_start_times = []
        for channel_id in use_channels:
            channel_trace_start_times.append(station.get_channel(channel_id).get_trace_start_time())
        average_trace_start_time = np.average(channel_trace_start_times)
        for i_trace, v_trace in enumerate(voltages_from_efield):
            channel = station.get_channel(use_channels[i_trace])
            time_shift = geo_utl.get_time_delay_from_direction(zenith, azimuth, det.get_relative_position(station.get_id(), use_channels[i_trace])) - (channel.get_trace_start_time() - average_trace_start_time)
            voltage_trace = np.roll(np.copy(v_trace), int(time_shift * electric_field.get_sampling_rate()))
            correlation += signal.correlate(voltage_trace, channel.get_trace())
        toffset = (np.arange(0, correlation.shape[0]) - channel.get_trace().shape[0]) / electric_field.get_sampling_rate()
        electric_field.set_trace_start_time(-toffset[np.argmax(correlation)] + average_trace_start_time)
        station.add_electric_field(electric_field)

        if debug:
            analytic_traces = np.zeros((n_channels, n_samples_time))
            for iCh, trace in enumerate(V_timedomain):
                analytic_trace_fft = np.sum(efield_antenna_factor[iCh] * np.array([analytic_pulse_theta_freq, analytic_pulse_phi_freq]), axis=0)
                analytic_traces[iCh] = fft.freq2time(analytic_trace_fft, electric_field.get_sampling_rate())
                analytic_traces[iCh] = np.roll(analytic_traces[iCh], pos)
            fig, (ax2, ax2f) = plt.subplots(2, 1, figsize=(10, 8))
            lw = 2

            times = station.get_times() / units.ns
            ax2.plot(times, station.get_trace()[1] / units.mV * units.m, "-C0", label="analytic eTheta", lw=lw)
            ax2.plot(times, station.get_trace()[2] / units.mV * units.m, "-C1", label="analytic ePhi", lw=lw)
            tmax = times[np.argmax(station.get_trace()[2])]
            ax2.set_xlim(tmax - 40, tmax + 50)

            ff = station.get_frequencies() / units.MHz
            df = ff[1] - ff[0]
            ax2f.plot(ff[ff < 600], np.abs(station.get_frequency_spectrum()[1][ff < 600]) / df / units.mV * units.m, "-C0", label="analytic eTheta", lw=lw)
            ax2f.plot(ff[ff < 600], np.abs(station.get_frequency_spectrum()[2][ff < 600]) / df / units.mV * units.m, "-C1", label="analytic ePhi", lw=lw)

            if station.has_sim_station():
                sim_station = station.get_sim_station()
                logger.debug("station start time {:.1f}ns, relativ sim station time = {:.1f}".format(station.get_trace_start_time(), sim_station.get_trace_start_time()))
                df = (sim_station.get_frequencies()[1] - sim_station.get_frequencies()[0]) / units.MHz
                c = 1.
                ffsim = sim_station.get_frequencies()
                mask = (ffsim > 100 * units.MHz) & (ffsim < 500 * units.MHz)
                result = poly.polyfit(ffsim[mask], np.log10(np.abs(sim_station.get_frequency_spectrum()[2][mask]) / df / units.mV * units.m), 1, full=True)
                logger.info("polyfit result = {:.2g}  {:.2g}".format(*result[0]))
                ax2.plot(sim_station.get_times() / units.ns, sim_station.get_trace()[1] / units.mV * units.m * c, "--C0", label="simulation eTheta", lw=lw)
                ax2.plot(sim_station.get_times() / units.ns, sim_station.get_trace()[2] / units.mV * units.m * c, "--C1", label="simulation ePhi", lw=lw)
                ax2f.plot(sim_station.get_frequencies() / units.MHz, np.abs(sim_station.get_frequency_spectrum()[1]) / df / units.mV * units.m * c, "--C0", label="simulation eTheta", lw=lw)
                ax2f.plot(sim_station.get_frequencies() / units.MHz, np.abs(sim_station.get_frequency_spectrum()[2]) / df / units.mV * units.m * c, "--C1", label="simulation ePhi", lw=lw)

                ax2f.plot(ffsim / units.MHz, 10 ** (result[0][0] + result[0][1] * ffsim), "C3:")

            ax2.legend(fontsize="xx-small")
            ax2.set_xlabel("time [ns]")
            ax2.set_ylabel("electric-field [mV/m]")
            ax2f.set_ylim(1e-3, 5)
            ax2f.set_xlabel("Frequency [MHz]")
            ax2f.set_xlim(100, 500)
            ax2f.semilogy(True)
            if sim_present:
                sim = station.get_sim_station()
                fig.suptitle("Simulation: Zenith {:.1f}, Azimuth {:.1f}".format(np.rad2deg(sim[stnp.zenith]), np.rad2deg(sim[stnp.azimuth])))
            else:
                fig.suptitle("Data: reconstructed zenith {:.1f}, azimuth {:.1f}".format(np.rad2deg(zenith), np.rad2deg(azimuth)))
            fig.tight_layout()
            fig.subplots_adjust(top=0.95)
            if(debug_plotpath is not None):
                fig.savefig(os.path.join(debug_plotpath, 'run_{:05d}_event_{:06d}_efield.png'.format(evt.get_run_number(), evt.get_id())))
                plt.close(fig)

            # plot antenna response and channels
            fig, ax = plt.subplots(len(V), 3, sharex='col', sharey='col')
            for iCh in range(len(V)):
                mask = ff > 100
                ax[iCh, 0].plot(ff[mask], np.abs(efield_antenna_factor[iCh][0])[mask], label="theta, channel {}".format(use_channels[iCh]), lw=lw)
                ax[iCh, 0].plot(ff[mask], np.abs(efield_antenna_factor[iCh][1])[mask], label="phi, channel {}".format(use_channels[iCh]), lw=lw)
                ax[iCh, 0].legend(fontsize='xx-small')
                ax[iCh, 0].set_xlim(100, 500)
                ax[iCh, 1].set_xlim(400, 600)
                ax[iCh, 2].set_xlim(400, 600)
                ax[iCh, 1].plot(times, V_timedomain[iCh] / units.micro / units.V, lw=lw)
                ax[iCh, 1].plot(times, analytic_traces[iCh] / units.micro / units.V, '--', lw=lw)
                ax[iCh, 2].plot(times, (V_timedomain[iCh] - analytic_traces[iCh]) / units.micro / units.V, '-', lw=lw)
                ax[iCh, 0].set_ylabel("H [m]")
                ax[iCh, 1].set_ylabel(r"V [$\mu$V]")
                ax[iCh, 2].set_ylabel(r"$\Delta$V [$\mu$V]")
                RMS = det.get_noise_RMS(station.get_id(), 0)
                ax[iCh, 1].text(0.6, 0.8, 'S/N={:.1f}'.format(np.max(np.abs(V_timedomain[iCh])) / RMS), transform=ax[iCh, 1].transAxes)
            ax[0][2].set_ylim(ax[0][1].get_ylim())
            ax[-1, 1].set_xlabel("time [ns]")
            ax[-1, 2].set_xlabel("time [ns]")
            ax[-1, 0].set_xlabel("frequency [MHz]")
            fig.tight_layout()
            if(debug_plotpath is not None):
                fig.savefig(os.path.join(debug_plotpath, 'run_{:05d}_event_{:06d}_channels.png'.format(evt.get_run_number(), evt.get_id())))
                plt.close(fig)
    def bandlimited_noise(self,
                          min_freq,
                          max_freq,
                          n_samples,
                          sampling_rate,
                          amplitude,
                          type='perfect_white',
                          time_domain=True,
                          bandwidth=None):
        """
        Generating noise of n_samples in a bandwidth [min_freq,max_freq].

        Parameters
        ---------

        min_freq: float
            Minimum frequency of passband for noise generation
            min_freq = None: Only the DC component is removed. If the DC component should be included,
            min_freq = 0 has to be specified
        max_freq: float
            Maximum frequency of passband for noise generation
            If the maximum frequency is above the Nquist frequencey (0.5 * sampling rate), the Nquist frequency is used
            max_freq = None: Frequencies up to Nyquist freq are used.
        n_samples: int
            number of samples in the time domain
        sampling_rate: float
            desired sampling rate of data
        amplitude: float
            desired voltage of noise as V_rms (only roughly, since bandpass limited)
        type: string
            perfect_white: flat frequency spectrum
            rayleigh: Amplitude of each frequency bin is drawn from a Rayleigh distribution
            # white: flat frequency spectrum with random jitter
        time_domain: bool (default True)
            if True returns noise in the time domain, if False it returns the noise in the frequency domain. The latter
            might be more performant as the noise is generated internally in the frequency domain.
        bandwidth: float or None (default)
            if this parameter is specified, the amplitude is interpreted as the amplitude for the bandwidth specified here
            Otherwise the amplitude is interpreted for the bandwidth of min(max_freq, 0.5 * sampling rate) - min_freq
            If `bandwidth` is larger then (min(max_freq, 0.5 * sampling rate) - min_freq) it has the same effect as `None`

        Comments
        --------
        *   Note that by design the max frequency is the Nyquist frequency, even if a bigger max_freq
            is implemented (RL 17-Sept-2018)

        *   Add 'multi_white' noise option on 20-Sept-2018 (RL)

        """
        frequencies = np.fft.rfftfreq(n_samples, 1. / sampling_rate)

        n_samples_freq = len(frequencies)

        if min_freq is None or min_freq == 0:
            # remove DC component; fftfreq returns the DC component as 0-th element and the negative
            # frequencies at the end, so frequencies[1] should be the lowest frequency; it seems safer,
            # to take the difference between two frequencies to determine the minimum frequency, in case
            # future versions of numpy change the order and maybe put the negative frequencies first
            min_freq = 0.5 * (frequencies[2] - frequencies[1])
            self.logger.info(' Set min_freq from None to {} MHz!'.format(
                min_freq / units.MHz))
        if max_freq is None:
            # sample up to Nyquist frequency
            max_freq = max(frequencies)
            self.logger.info(' Set max_freq from None to {} GHz!'.format(
                max_freq / units.GHz))
        selection = (frequencies >= min_freq) & (frequencies <= max_freq)

        nbinsactive = np.sum(selection)
        self.logger.debug(
            'Total number of frequency bins (bilateral spectrum) : {} , of those active: {} '
            .format(n_samples, nbinsactive))

        # Debug plots
        #         f1 = plt.figure()
        #         plt.plot (frequencies/max(frequencies))
        #         plt.plot(fbinsactive,'kx')

        if (bandwidth is not None):
            sampling_bandwidth = min(0.5 * sampling_rate, max_freq) - min_freq
            amplitude *= 1. / (
                bandwidth / (sampling_bandwidth)
            )**0.5  # normalize noise level to the bandwidth its generated for

        ampl = np.zeros(n_samples_freq)
        sigscale = (1. * n_samples) / np.sqrt(nbinsactive)
        if type == 'perfect_white':
            ampl[selection] = amplitude * sigscale
        elif type == 'rayleigh':
            fsigma = amplitude * sigscale / np.sqrt(2.)
            ampl[selection] = self.__random_generator.rayleigh(
                fsigma, nbinsactive)
#         elif type == 'white':
# FIXME: amplitude normalization is not correct for 'white'
#             ampl = np.random.rand(n_samples) * 0.05 * amplitude + amplitude * np.sqrt(2.*n_samples * 2)
        else:
            self.logger.error("Other types of noise not yet implemented.")
            raise NotImplementedError(
                "Other types of noise not yet implemented.")

        noise = self.add_random_phases(ampl, n_samples) / sampling_rate
        if (time_domain):
            return fft.freq2time(noise, sampling_rate, n=n_samples)
        else:
            return noise
예제 #21
0
    def run(self, event, station, max_distance, z_width, grid_spacing, direction_guess=None, debug=False, use_dnr=False):
        """
        Execute the 2D vertex reconstruction

        Parameters
        ---------------
        station: Station
            The station for which the vertex shall be reconstructed
        max_distance: number
            Maximum distance up to which the vertex position shall be searched
        z_width: number
            Vertical size of the search area. If direction_guess is specified, a
            stripe of z_width to each side of the initial direction will be searched.
            If direction_guess is not specified, z_width is the maximum depth up
            to which the vertex will be searched.
        grid_spacing: number
            Distance between two points of the grid on which the vertex is searched
        direction_guess: number, defaults to None
            Zenith for an initial guess of the vertex direction. If specified,
            a strip if width 2*z_width around the guessed direction will be searched
        debug: boolean
            If True, debug plots will be produced
        use_dnr: boolean
            If True, DnR pulses are included in the reconstruction by correlating
            the channel waveforms with themselves.
        """
        distances = np.arange(50. * units.m, max_distance, grid_spacing)
        if direction_guess is None:
            heights = np.arange(-z_width, 0, grid_spacing)
        else:
            heights = np.arange(-z_width, z_width, grid_spacing)
        x_0, z_0 = np.meshgrid(distances, heights)
        # Create list of coordinates at which we look for the vertex position
        # If we have an initial guess for the vertex direction, we only check possible vertex locations around that
        # direction, otherwise we search the whole space
        if direction_guess is None:
            x_coords = x_0
            z_coords = z_0
        else:
            x_coords = np.cos(direction_guess - 90. * units.deg) * x_0 + np.sin(direction_guess - 90. * units.deg) * z_0
            z_coords = -np.sin(direction_guess - 90. * units.deg) * x_0 + np.cos(direction_guess - 90. * units.deg) * z_0

        correlation_sum = np.zeros(x_coords.shape)

        corr_range = 50. * units.ns
        for i_pair, channel_pair in enumerate(self.__channel_pairs):
            ch1 = station.get_channel(channel_pair[0])
            ch2 = station.get_channel(channel_pair[1])
            snr1 = np.max(np.abs(ch1.get_trace()))
            snr2 = np.max(np.abs(ch2.get_trace()))
            if snr1 == 0 or snr2 == 0:
                continue
            spec1 = np.copy(ch1.get_frequency_spectrum())
            spec2 = np.copy(ch2.get_frequency_spectrum())
            if self.__passband is not None:
                b, a = scipy.signal.butter(10, self.__passband, 'bandpass', analog=True)
                w, h = scipy.signal.freqs(b, a, ch1.get_frequencies())
                spec1 *= h
                spec2 *= h
            trace1 = fft.freq2time(spec1, ch1.get_sampling_rate())
            trace2 = fft.freq2time(spec2, ch2.get_sampling_rate())
            if self.__template is not None:
                corr_1 = hp.get_normalized_xcorr(trace1, self.__template)
                corr_2 = hp.get_normalized_xcorr(trace2, self.__template)
                self.__correlation = np.zeros_like(corr_1)
                sample_shifts = np.arange(-len(corr_1) // 2, len(corr_1) // 2, dtype=int)
                toffset = sample_shifts / ch1.get_sampling_rate()
                for i_shift, shift_sample in enumerate(sample_shifts):
                    self.__correlation[i_shift] = np.max(corr_1 * np.roll(corr_2, shift_sample))
            else:
                t_max1 = ch1.get_times()[np.argmax(np.abs(trace1))]
                t_max2 = ch2.get_times()[np.argmax(np.abs(trace2))]
                if snr1 > snr2:
                    trace1[np.abs(ch1.get_times() - t_max1) > corr_range] = 0
                else:
                    trace2[np.abs(ch2.get_times() - t_max2) > corr_range] = 0
                self.__correlation = np.abs(scipy.signal.correlate(trace1, trace2))
                toffset = -(np.arange(0, self.__correlation.shape[0]) - self.__correlation.shape[0] / 2.) / ch1.get_sampling_rate()
                if np.sum(np.abs(self.__correlation)) > 0:
                    self.__correlation /= np.sum(np.abs(self.__correlation))
            corr_snr = np.max(self.__correlation) / np.mean(self.__correlation[self.__correlation > 0])
            self.__sampling_rate = ch1.get_sampling_rate()
            self.__channel_pair = channel_pair
            self.__channel_positions = [self.__detector.get_relative_position(self.__station_id, channel_pair[0]), self.__detector.get_relative_position(self.__station_id, channel_pair[1])]
            correlation_array = np.zeros_like(correlation_sum)
            # Check every hypothesis for which ray types the antennas might have detected
            for i_ray in range(len(self.__ray_types)):
                self.__current_ray_types = self.__ray_types[i_ray]
                correlation_array = np.maximum(self.get_correlation_array_2d(x_coords, z_coords), correlation_array)
            if np.max(correlation_array) > 0:
                if self.__template is None:
                    correlation_sum += correlation_array / np.max(correlation_array) * corr_snr
                else:
                    correlation_sum += correlation_array
            max_corr_index = np.unravel_index(np.argmax(correlation_sum), correlation_sum.shape)
            max_corr_r = x_coords[max_corr_index[0]][max_corr_index[1]]
            max_corr_z = z_coords[max_corr_index[0]][max_corr_index[1]]

            if debug:
                fig1 = plt.figure(figsize=(12, 4))
                fig2 = plt.figure(figsize=(8, 12))
                ax1_1 = fig1.add_subplot(1, 3, 1)
                ax1_2 = fig1.add_subplot(1, 3, 2, sharey=ax1_1)
                ax1_3 = fig1.add_subplot(1, 3, 3)
                ax1_1.plot(ch1.get_times(), ch1.get_trace() / units.mV, c='C0', alpha=.3)
                ax1_2.plot(ch2.get_times(), ch2.get_trace() / units.mV, c='C1', alpha=.3)
                ax1_1.plot(ch1.get_times()[np.abs(trace1) > 0], trace1[np.abs(trace1) > 0] / units.mV, c='C0', alpha=1)
                ax1_2.plot(ch2.get_times()[np.abs(trace2) > 0], trace2[np.abs(trace2) > 0] / units.mV, c='C1', alpha=1)
                ax1_1.plot(ch1.get_times()[:len(self.__template)], self.__template, c='k')
                ax1_1.set_xlabel('t [ns]')
                ax1_1.set_ylabel('U [mV]')
                ax1_1.set_title('Channel {}'.format(self.__channel_pair[0]))
                ax1_2.set_xlabel('t [ns]')
                ax1_2.set_ylabel('U [mV]')
                ax1_2.set_title('Channel {}'.format(self.__channel_pair[1]))

                ax1_3.plot(toffset, self.__correlation)
                ax1_3.set_title('$SNR_{corr}$=%.2f' % (corr_snr))
                ax1_1.grid()
                ax1_2.grid()
                ax1_3.grid()
                fig1.tight_layout()
                ax2_1 = fig2.add_subplot(211)
                ax2_2 = fig2.add_subplot(212)
                corr_plots = ax2_1.pcolor(x_coords, z_coords, correlation_array)
                sum_plots = ax2_2.pcolor(x_coords, z_coords, correlation_sum)
                fig2.colorbar(corr_plots, ax=ax2_1)
                fig2.colorbar(sum_plots, ax=ax2_2)
                sim_vertex = None
                for shower in event.get_sim_showers():
                    if shower.has_parameter(shp.vertex):
                        sim_vertex = shower.get_parameter(shp.vertex)
                if sim_vertex is not None:
                    ax2_1.axvline(np.sqrt(sim_vertex[0]**2 + sim_vertex[1]**2), c='r', linestyle=':')
                    ax2_1.axhline(sim_vertex[2], c='r', linestyle=':')
                    ax2_2.axvline(np.sqrt(sim_vertex[0]**2 + sim_vertex[1]**2), c='r', linestyle=':')
                    ax2_2.axhline(sim_vertex[2], c='r', linestyle=':')

                ax2_1.axvline(max_corr_r, c='k', linestyle=':')
                ax2_1.axhline(max_corr_z, c='k', linestyle=':')
                ax2_2.axvline(max_corr_r, c='k', linestyle=':')
                ax2_2.axhline(max_corr_z, c='k', linestyle=':')

                fig2.tight_layout()
                plt.show()
                plt.close('all')
        if use_dnr:
            dnr_correlation_sum = np.zeros(x_coords.shape)
            for channel_id in self.__channel_ids:
                channel = station.get_channel(channel_id)
                spec = channel.get_frequency_spectrum()
                if self.__passband is not None:
                    b, a = scipy.signal.butter(10, self.__passband, 'bandpass', analog=True)
                    w, h = scipy.signal.freqs(b, a, channel.get_frequencies())
                    spec *= h
                trace = fft.freq2time(spec, channel.get_sampling_rate())
                corr = hp.get_normalized_xcorr(trace, self.__template)
                self.__correlation = np.zeros_like(corr)
                sample_shifts = np.arange(-len(corr) // 2, len(corr) // 2, dtype=int)
                toffset = sample_shifts / channel.get_sampling_rate()
                for i_shift, shift_sample in enumerate(sample_shifts):
                    self.__correlation[i_shift] = np.max(corr * np.roll(corr, shift_sample))
                self.__correlation[np.abs(toffset) <= 5] = 0
                self.__sampling_rate = channel.get_sampling_rate()
                self.__channel_pair = [channel_id, channel_id]
                self.__channel_positions = [self.__detector.get_relative_position(self.__station_id, channel_id),
                                            self.__detector.get_relative_position(self.__station_id, channel_id)]
                correlation_array = np.zeros_like(correlation_sum)
                for i_ray in range(len(self.__dnr_ray_types)):
                    self.__current_ray_types = self.__dnr_ray_types[i_ray]
                    correlation_array = np.maximum(self.get_correlation_array_2d(x_coords, z_coords), correlation_array)
                if np.max(correlation_array) > 0:
                    dnr_correlation_sum += correlation_array
            max_corr_dnr_index = np.unravel_index(np.argmax(correlation_sum + dnr_correlation_sum), correlation_sum.shape)
            max_corr_dnr_r = x_coords[max_corr_dnr_index[0]][max_corr_dnr_index[1]]
            max_corr_dnr_z = z_coords[max_corr_dnr_index[0]][max_corr_dnr_index[1]]

        if self.__output_path is not None:
            plt.close('all')
            if use_dnr:
                fig3 = plt.figure(figsize=(12, 12))
                ax3_1 = fig3.add_subplot(321)
                ax3_2 = fig3.add_subplot(322)
                ax3_3 = fig3.add_subplot(3, 2, (3, 6))
            else:
                fig3 = plt.figure(figsize=(8, 8))
                ax3_1 = fig3.add_subplot(111)
            import skimage.transform
            downscaled_image = skimage.transform.rescale(correlation_sum, .2)
            rescaled_xcoords = skimage.transform.rescale(x_coords, .2)
            rescaled_zcoords = skimage.transform.rescale(z_coords, .2)
            corr_plot = ax3_1.pcolor(rescaled_xcoords, rescaled_zcoords, downscaled_image)
            ax3_1.grid()
            ax3_1.set_aspect('equal')
            plt.colorbar(corr_plot, ax=ax3_1)
            sim_vertex = None
            for shower in event.get_sim_showers():
                if shower.has_parameter(shp.vertex):
                    sim_vertex = shower.get_parameter(shp.vertex)
            if sim_vertex is not None:
                ax3_1.axvline(np.sqrt(sim_vertex[0] ** 2 + sim_vertex[1] ** 2), c='r', linestyle=':')
                ax3_1.axhline(sim_vertex[2], c='r', linestyle=':')
                ax3_1.axvline(max_corr_r, c='k', linestyle=':')
                ax3_1.axhline(max_corr_z, c='k', linestyle=':')
            if use_dnr:
                downscaled_dnr_image = skimage.transform.rescale(dnr_correlation_sum, .2)
                dnr_corr_plot = ax3_2.pcolor(rescaled_xcoords, rescaled_zcoords, downscaled_dnr_image)
                if np.max(downscaled_dnr_image) > .1:
                    ax3_2.contour(rescaled_xcoords, rescaled_zcoords, downscaled_dnr_image, levels=[.1], colors='k', alpha=.3)
                ax3_2.grid()
                ax3_2.set_aspect('equal')
                plt.colorbar(dnr_corr_plot, ax=ax3_2)
                combined_corr_plot = ax3_3.pcolor(rescaled_xcoords, rescaled_zcoords, downscaled_dnr_image + downscaled_image)
                ax3_3.grid()
                ax3_3.set_aspect('equal')
                plt.colorbar(combined_corr_plot, ax=ax3_3)
                if sim_vertex is not None:
                    ax3_2.axvline(np.sqrt(sim_vertex[0] ** 2 + sim_vertex[1] ** 2), c='r', linestyle=':')
                    ax3_2.axhline(sim_vertex[2], c='r', linestyle=':')
                    ax3_3.axvline(np.sqrt(sim_vertex[0] ** 2 + sim_vertex[1] ** 2), c='r', linestyle=':')
                    ax3_3.axhline(sim_vertex[2], c='r', linestyle=':')
                    ax3_3.axvline(max_corr_dnr_r, c='k', linestyle=':')
                    ax3_3.axhline(max_corr_dnr_z, c='k', linestyle=':')
            fig3.tight_layout()
            fig3.savefig('{}/vertex_reco_{}.png'.format(self.__output_path, event.get_id()))

        if max_corr_index is None:
            return
        self.__rec_x = x_coords[max_corr_index[0]][max_corr_index[1]]
        self.__rec_z = z_coords[max_corr_index[0]][max_corr_index[1]]
        station.set_parameter(stnp.vertex_2D_fit, [self.__rec_x, self.__rec_z])

        return
    def run(self, event, station, det, channel_ids, passband):
        """
        Run the module on a station

        Parameters:
        _________________
        event: NuRadioReco.framework.event.Event object
            The event the module should be run on
        station: NuRadioReco.framework.station.Station object
            The station the module should be run on
        det: NuRadioReco.detector.detector.Detector object of child object
            The detector description
        channel_ids: array of int
            IDs of the channels the module should be run on
        passband: array of float
            Lower and upper bound of the bandpass filter that is applied to the
            channel waveforms when determining correlation to the template. Can
            be used to filter out frequencies that are dominated by noise.
        """

        # Create data structured to store pulse properties
        propagation_times = np.zeros((len(channel_ids), 3))
        receive_angles = np.zeros((len(channel_ids), 3))
        found_solutions = np.zeros((len(channel_ids), 3))
        # Get vertex position
        vertex_position = None
        if self.__use_sim:
            for sim_shower in event.get_sim_showers():
                if sim_shower.has_parameter(shp.vertex):
                    vertex_position = sim_shower.get_parameter(shp.vertex)
                    break
        else:
            if station.has_parameter(stnp.nu_vertex):
                vertex_position = station.get_parameter(stnp.nu_vertex)
            elif station.has_parameter(stnp.vertex_2D_fit):
                vertex_2d = station.get_parameter(stnp.vertex_2D_fit)
                vertex_position = np.array([vertex_2d[0], 0, vertex_2d[1]])
        if vertex_position is None:
            raise RuntimeError('Could not find vertex position')
        correlation_size = 0
        for i_channel, channel_id in enumerate(channel_ids):
            channel = station.get_channel(channel_id)
            if channel.get_sampling_rate(
            ) != self.__electric_field_template.get_sampling_rate():
                raise RuntimeError(
                    'The channels and the electric field remplate need to have the same sampling rate.'
                )
            # Calculate size of largest autocorrelation
            if channel.get_number_of_samples(
            ) + self.__electric_field_template.get_number_of_samples(
            ) - 1 > correlation_size:
                correlation_size = channel.get_number_of_samples(
                ) + self.__electric_field_template.get_number_of_samples() - 1
            channel_position = det.get_relative_position(
                station.get_id(), channel_id)
            raytracer = NuRadioMC.SignalProp.analyticraytracing.ray_tracing(
                x1=vertex_position, x2=channel_position, medium=self.__medium)
            raytracer.find_solutions()
            # Loop through all 3 ray path types and store the properties into the data structure
            for i_solution, solution in enumerate(raytracer.get_results()):
                solution_type = raytracer.get_solution_type(i_solution)
                found_solutions[i_channel, solution_type - 1] += 1
                propagation_times[i_channel, solution_type -
                                  1] = raytracer.get_travel_time(i_solution)
                receive_vector = raytracer.get_receive_vector(i_solution)
                receive_angles[i_channel, solution_type -
                               1] = radiotools.helper.cartesian_to_spherical(
                                   receive_vector[0], receive_vector[1],
                                   receive_vector[2])[0]
        # We only want the relative time differences between channels, so we subtract the mean propagation time
        for i_solution in range(3):
            if len(propagation_times[:, i_solution][
                    propagation_times[:, i_solution] > 0]) > 0:
                propagation_times[:, i_solution][
                    propagation_times[:, i_solution] > 0] -= np.mean(
                        propagation_times[:, i_solution][
                            propagation_times[:, i_solution] > 0])
        correlation_sum = np.zeros((3, correlation_size))
        # Now we check which ray path results in the best correlation between channels
        for i_channel, channel_id in enumerate(channel_ids):
            channel = station.get_channel(channel_id)
            antenna_pattern = self.__antenna_provider.load_antenna_pattern(
                det.get_antenna_model(station.get_id(), channel_id))
            antenna_orientation = det.get_antenna_orientation(
                station.get_id(), channel_id)
            for i_solution in range(3):
                if found_solutions[i_channel, i_solution] > 0:
                    # We calculate the voltage template from the electric field template using the receiving angles
                    # for that raytracing solution
                    antenna_response = antenna_pattern.get_antenna_response_vectorized(
                        self.__electric_field_template.get_frequencies(),
                        receive_angles[i_channel, i_solution], 0.,
                        antenna_orientation[0], antenna_orientation[1],
                        antenna_orientation[2], antenna_orientation[3])
                    # For simplicity, we assume equal contribution on the E_theta and E_phi component
                    channel_template_spec = fft.time2freq(self.__electric_field_template.get_filtered_trace(passband), self.__electric_field_template.get_sampling_rate()) * \
                        det.get_amplifier_response(station.get_id(), channel_id, self.__electric_field_template.get_frequencies()) * (antenna_response['theta'] + antenna_response['phi'])
                    channel_template_trace = fft.freq2time(
                        channel_template_spec,
                        self.__electric_field_template.get_sampling_rate())
                    # We apply the expected time shift for the raytracing solution and calculate the correlation with the template
                    channel.apply_time_shift(
                        -propagation_times[i_channel, i_solution], True)
                    correlation = radiotools.helper.get_normalized_xcorr(
                        channel_template_trace,
                        channel.get_filtered_trace(passband))
                    correlation = np.abs(correlation)
                    correlation_sum[i_solution][:len(correlation
                                                     )] += correlation
                    channel.apply_time_shift(
                        propagation_times[i_channel, i_solution], True)

        correlation_sum_max = np.max(correlation_sum, axis=1)
        # We look for the raytracing solution that yielded the best correlation and store the corresponding properties
        # in the channel parameters
        for i_channel, channel_id in enumerate(channel_ids):
            channel = station.get_channel(channel_id)
            channel.set_parameter(
                chp.signal_time_offset,
                propagation_times[i_channel,
                                  np.argmax(correlation_sum_max)])
            channel.set_parameter(
                chp.signal_receiving_zenith,
                receive_angles[i_channel,
                               np.argmax(correlation_sum_max)])
            channel.set_parameter(
                chp.signal_ray_type,
                self.__raytracing_types[np.argmax(correlation_sum_max)])
예제 #23
0
def update_electric_field_plot(
    log_energy,
    viewing_angle,
    shower_type,
    polarization_angle,
    model,
    propagation_length,
    attenuation_model,
    sampling_rate):

    viewing_angle = viewing_angle * units.deg
    polarization_angle = polarization_angle * units.deg
    propagation_length  = propagation_length * units.km
    energy = np.power(10., log_energy)
    samples = int(512 * sampling_rate)
    ior = 1.78
    cherenkov_angle = np.arccos(1./ior)
    distance = 1.*units.km
    try:
        efield_spectrum = NuRadioMC.SignalGen.askaryan.get_frequency_spectrum(
            energy,
            cherenkov_angle + viewing_angle,
            samples,
            1./sampling_rate,
            shower_type,
            ior,
            distance,
            model,
            same_shower=True
        )
    except:
        efield_spectrum = NuRadioMC.SignalGen.askaryan.get_frequency_spectrum(
            energy,
            cherenkov_angle + viewing_angle,
            samples,
            1./sampling_rate,
            shower_type,
            ior,
            distance,
            model,
            same_shower=False
        )
    freqs = np.fft.rfftfreq(samples, 1./sampling_rate)
    if propagation_length > 0:
        attenuation_length = NuRadioMC.utilities.attenuation.get_attenuation_length(200., freqs, attenuation_model)
        efield_spectrum *= np.exp(-propagation_length/attenuation_length)
    efield_spectrum_theta = efield_spectrum * np.cos(polarization_angle)
    efield_spectrum_phi = efield_spectrum * np.sin(polarization_angle)
    efield_trace = fft.freq2time(efield_spectrum, sampling_rate)
    efield_trace_theta = efield_trace * np.cos(polarization_angle)
    efield_trace_phi = efield_trace * np.sin(polarization_angle)
    times = np.arange(samples) / sampling_rate

    fig = plotly.subplots.make_subplots(rows=1, cols=2,
        shared_xaxes=False, shared_yaxes=False,
        vertical_spacing=0.01, subplot_titles=['Time Trace', 'Spectrum'])
    fig.append_trace(go.Scatter(
        x=times/units.ns,
        y=efield_trace_theta/(units.mV/units.m),
        name='E_theta (t)'
    ),1,1)
    fig.append_trace(go.Scatter(
        x=times/units.ns,
        y=efield_trace_phi/(units.mV/units.m),
        name='E_phi (t)'
    ),1,1)
    fig.append_trace(go.Scatter(
        x=freqs/units.MHz,
        y=np.abs(efield_spectrum_theta)/(units.mV/units.m/units.GHz),
        name='E_theta (f)'
    ),1,2)
    fig.append_trace(go.Scatter(
        x=freqs/units.MHz,
        y=np.abs(efield_spectrum_phi)/(units.mV/units.m/units.GHz),
        name='E_phi (f)'
    ),1,2)
    max_time = times[np.argmax(np.sqrt(efield_trace_phi**2+efield_trace_theta**2))]
    fig.update_xaxes(title_text='t [ns]', range=[max_time-50*units.ns, max_time+50*units.ns], row=1, col=1)
    fig.update_xaxes(title_text='f [MHz]', row=1, col=2)
    fig.update_yaxes(title_text='E[mV/m]', row=1, col=1)
    fig.update_yaxes(title_text='E [mV/m/GHz]', row=1, col=2)
    return [fig, json.dumps({'theta':efield_trace_theta.tolist(), 'phi':efield_trace_phi.tolist()})]
예제 #24
0
    def run(self, evt, station, det):
        t = time.time()

        # access simulated efield and high level parameters
        sim_station = station.get_sim_station()
        if (len(sim_station.get_electric_fields()) == 0):
            raise LookupError(f"station {station.get_id()} has no efields")
        sim_station_id = sim_station.get_id()

        # first we determine the trace start time of all channels and correct
        # for different cable delays
        times_min = []
        times_max = []
        for iCh in det.get_channel_ids(sim_station_id):
            for electric_field in sim_station.get_electric_fields_for_channels(
                [iCh]):
                time_resolution = 1. / electric_field.get_sampling_rate()
                cab_delay = det.get_cable_delay(sim_station_id, iCh)
                t0 = electric_field.get_trace_start_time() + cab_delay
                # if we have a cosmic ray event, the different signal travel time to the antennas has to be taken into account
                if sim_station.is_cosmic_ray():
                    site = det.get_site(sim_station_id)
                    antenna_position = det.get_relative_position(
                        sim_station_id, iCh) - electric_field.get_position()
                    if sim_station.get_parameter(
                            stnp.zenith
                    ) > 90 * units.deg:  # signal is coming from below, so we take IOR of ice
                        index_of_refraction = ice.get_refractive_index(
                            antenna_position[2], site)
                    else:  # signal is coming from above, so we take IOR of air
                        index_of_refraction = ice.get_refractive_index(1, site)
                    # For cosmic ray events, we only have one electric field for all channels, so we have to account
                    # for the difference in signal travel between channels. IMPORTANT: This is only accurate
                    # if all channels have the same z coordinate
                    travel_time_shift = geo_utl.get_time_delay_from_direction(
                        sim_station.get_parameter(stnp.zenith),
                        sim_station.get_parameter(stnp.azimuth),
                        antenna_position, index_of_refraction)
                    t0 += travel_time_shift
                if (
                        not np.isnan(t0)
                ):  # trace start time is None if no ray tracing solution was found and channel contains only zeros
                    times_min.append(t0)
                    times_max.append(t0 +
                                     electric_field.get_number_of_samples() /
                                     electric_field.get_sampling_rate())
                    self.logger.debug(
                        "trace start time {}, cab_delty {}, tracelength {}".
                        format(
                            electric_field.get_trace_start_time(), cab_delay,
                            electric_field.get_number_of_samples() /
                            electric_field.get_sampling_rate()))
        times_min = np.array(times_min)
        times_max = np.array(times_max)
        if times_min.min() < 0:
            times_min -= times_min.min()
            times_max -= times_min.min()
        times_min = np.array(times_min) - self.__pre_pulse_time
        times_max = np.array(times_max) + self.__post_pulse_time
        trace_length = times_max.max() - times_min.min()
        trace_length_samples = int(round(trace_length / time_resolution))
        if trace_length_samples % 2 != 0:
            trace_length_samples += 1
        self.logger.debug(
            "smallest trace start time {:.1f}, largest trace time {:.1f} -> n_samples = {:d} {:.0f}ns)"
            .format(times_min.min(), times_max.max(), trace_length_samples,
                    trace_length / units.ns))

        # loop over all channels
        for channel_id in det.get_channel_ids(station.get_id()):

            # one channel might contain multiple channels to store the signals from multiple ray paths,
            # so we loop over all simulated channels with the same id,
            # convolve each trace with the antenna response for the given angles
            # and everything up in the time domain
            self.logger.debug('channel id {}'.format(channel_id))
            channel = NuRadioReco.framework.channel.Channel(channel_id)
            channel_spectrum = None
            trace_object = None
            if (self.__debug):
                from matplotlib import pyplot as plt
                fig, axes = plt.subplots(2, 1)
            for electric_field in sim_station.get_electric_fields_for_channels(
                [channel_id]):

                # all simulated channels have a different trace start time
                # in a measurement, all channels have the same physical start time
                # so we need to create one long trace that can hold all the different channel times
                # to achieve a good time resolution, we upsample the trace first.
                new_efield = NuRadioReco.framework.base_trace.BaseTrace(
                )  # create new data structure with new efield length
                new_efield.set_trace(copy.copy(electric_field.get_trace()),
                                     electric_field.get_sampling_rate())
                new_trace = np.zeros((3, trace_length_samples))
                # calculate the start bin
                if (not np.isnan(electric_field.get_trace_start_time())):
                    cab_delay = det.get_cable_delay(sim_station_id, channel_id)
                    if sim_station.is_cosmic_ray():
                        site = det.get_site(sim_station_id)
                        antenna_position = det.get_relative_position(
                            sim_station_id,
                            channel_id) - electric_field.get_position()
                        if sim_station.get_parameter(
                                stnp.zenith
                        ) > 90 * units.deg:  # signal is coming from below, so we take IOR of ice
                            index_of_refraction = ice.get_refractive_index(
                                antenna_position[2], site)
                        else:  # signal is coming from above, so we take IOR of air
                            index_of_refraction = ice.get_refractive_index(
                                1, site)
                        travel_time_shift = geo_utl.get_time_delay_from_direction(
                            sim_station.get_parameter(stnp.zenith),
                            sim_station.get_parameter(stnp.azimuth),
                            antenna_position, index_of_refraction)
                        start_time = electric_field.get_trace_start_time(
                        ) + cab_delay - times_min.min() + travel_time_shift
                        start_bin = int(round(start_time / time_resolution))
                        time_remainder = start_time - start_bin * time_resolution
                    else:
                        start_time = electric_field.get_trace_start_time(
                        ) + cab_delay - times_min.min()
                        start_bin = int(round(start_time / time_resolution))
                        time_remainder = start_time - start_bin * time_resolution
                    self.logger.debug(
                        'channel {}, start time {:.1f} = bin {:d}, ray solution {}'
                        .format(
                            channel_id,
                            electric_field.get_trace_start_time() + cab_delay,
                            start_bin, electric_field[efp.ray_path_type]))
                    new_efield.apply_time_shift(time_remainder)
                    new_trace[:, start_bin:(start_bin +
                                            new_efield.get_number_of_samples()
                                            )] = new_efield.get_trace()
                trace_object = NuRadioReco.framework.base_trace.BaseTrace()
                trace_object.set_trace(new_trace, 1. / time_resolution)
                trace_object.set_trace_start_time(
                    np.min(times_min) - cab_delay)
                if (self.__debug):
                    axes[0].plot(trace_object.get_times(),
                                 new_trace[1],
                                 label="eTheta {}".format(
                                     electric_field[efp.ray_path_type]),
                                 c='C0')
                    axes[0].plot(trace_object.get_times(),
                                 new_trace[2],
                                 label="ePhi {}".format(
                                     electric_field[efp.ray_path_type]),
                                 c='C0',
                                 linestyle=':')
                    axes[0].plot(electric_field.get_times(),
                                 electric_field.get_trace()[1],
                                 c='C1',
                                 linestyle='-',
                                 alpha=.5)
                    axes[0].plot(electric_field.get_times(),
                                 electric_field.get_trace()[2],
                                 c='C1',
                                 linestyle=':',
                                 alpha=.5)
                ff = trace_object.get_frequencies()
                efield_fft = trace_object.get_frequency_spectrum()

                zenith = electric_field[efp.zenith]
                azimuth = electric_field[efp.azimuth]

                # get antenna pattern for current channel
                VEL = trace_utilities.get_efield_antenna_factor(
                    sim_station, ff, [channel_id], det, zenith, azimuth,
                    self.antenna_provider)

                if VEL is None:  # this can happen if there is not signal path to the antenna
                    voltage_fft = np.zeros_like(
                        efield_fft[1])  # set voltage trace to zeros
                else:
                    # Apply antenna response to electric field
                    VEL = VEL[
                        0]  # we only requested the VEL for one channel, so selecting it
                    voltage_fft = np.sum(
                        VEL * np.array([efield_fft[1], efield_fft[2]]), axis=0)

                # Remove DC offset
                voltage_fft[np.where(ff < 5 * units.MHz)] = 0.

                if (self.__debug):
                    axes[1].plot(trace_object.get_times(),
                                 fft.freq2time(
                                     voltage_fft,
                                     electric_field.get_sampling_rate()),
                                 label="{}, zen = {:.0f}deg".format(
                                     electric_field[efp.ray_path_type],
                                     zenith / units.deg))

                if ('amp' in self.__uncertainty):
                    voltage_fft *= np.random.normal(
                        1, self.__uncertainty['amp'][channel_id])
                if ('sys_amp' in self.__uncertainty):
                    voltage_fft *= self.__uncertainty['sys_amp'][channel_id]

                if (channel_spectrum is None):
                    channel_spectrum = voltage_fft
                else:
                    channel_spectrum += voltage_fft

            if (self.__debug):
                axes[0].legend(loc='upper left')
                axes[1].legend(loc='upper left')
                plt.show()
            if trace_object is None:  # this happens if don't have any efield for this channel
                # set the trace to zeros
                channel.set_trace(np.zeros(trace_length_samples),
                                  1. / time_resolution)
            else:
                channel.set_frequency_spectrum(
                    channel_spectrum, trace_object.get_sampling_rate())
            channel.set_trace_start_time(times_min.min())

            station.add_channel(channel)
        self.__t += time.time() - t
예제 #25
0
def update_voltage_plot(
    electric_field,
    antenna_type,
    signal_zenith,
    signal_azimuth,
    amplifier_type,
    filter_toggle,
    filter_band,
    sampling_rate
):
    samples = int(512 * sampling_rate)
    electric_field = json.loads(electric_field)
    if electric_field is None:
        return {}, {}, ""
    antenna_pattern = antennapattern_provider.load_antenna_pattern(antenna_type)
    freqs = np.fft.rfftfreq(samples, 1. / sampling_rate)
    times = np.arange(samples) / sampling_rate
    signal_zenith = signal_zenith * units.deg
    signal_azimuth = signal_azimuth * units.deg

    antenna_response = antenna_pattern.get_antenna_response_vectorized(
        freqs,
        signal_zenith,
        signal_azimuth,
        0.,
        0.,
        90.*units.deg,
        180.*units.deg
    )
    detector_response_theta = antenna_response['theta']
    detector_response_phi = antenna_response['phi']
    channel_spectrum = antenna_response['theta'] * fft.time2freq(electric_field['theta'], sampling_rate) + antenna_response['phi'] * fft.time2freq(electric_field['phi'], sampling_rate)
    if amplifier_type is not None:
        if amplifier_type == 'iglu' or amplifier_type == 'rno_surface':
            amp_response = NuRadioReco.detector.RNO_G.analog_components.load_amp_response(amplifier_type)
            amplifier_response = amp_response['gain'](freqs) * amp_response['phase'](freqs)
        else:
            amp_response = NuRadioReco.detector.ARIANNA.analog_components.load_amplifier_response(amplifier_type)
            amplifier_response = amp_response['gain'](freqs) * amp_response['phase'](freqs)
        channel_spectrum = channel_spectrum * amplifier_response
        detector_response_theta *= amplifier_response
        detector_response_phi *= amplifier_response
    if 'filter' in filter_toggle:
        mask = freqs > 0
        b, a = scipy.signal.butter(10, filter_band, 'bandpass', analog=True)
        w, h = scipy.signal.freqs(b, a, freqs[mask])
        channel_spectrum[mask] = channel_spectrum[mask] * np.abs(h)
        detector_response_theta[mask] *= np.abs(h)
        detector_response_phi[mask] *= np.abs(h)
    channel_trace = fft.freq2time(channel_spectrum, sampling_rate)
    max_time = times[np.argmax(np.abs(channel_trace))]
    fig = plotly.subplots.make_subplots(rows=1, cols=2,
        shared_xaxes=False, shared_yaxes=False,
        vertical_spacing=0.01, subplot_titles=['Time Trace', 'Spectrum'])
    fig.append_trace(go.Scatter(
        x=times / units.ns,
        y=channel_trace / units.mV,
        name='U (t)'
    ), 1, 1)
    fig.append_trace(go.Scatter(
        x=freqs / units.MHz,
        y=np.abs(channel_spectrum) / (units.mV / units.GHz),
        name='U (f)'
    ), 1, 2)
    fig.update_xaxes(title_text='t [ns]', range=[max_time - 50.*units.ns, max_time + 50.*units.ns], row=1, col=1)
    fig.update_xaxes(title_text='f [MHz]', row=1, col=2)
    fig.update_yaxes(title_text='U [mV]', row=1, col=1)
    fig.update_yaxes(title_text='U [mV/GHz]', row=1, col=2)

    fig2 = plotly.subplots.make_subplots(rows=1, cols=2,
        shared_xaxes=False, shared_yaxes=True,
        vertical_spacing=0.01, subplot_titles=['Theta', 'Phi'])
    fig2.append_trace(go.Scatter(
        x=freqs / units.MHz,
        y=np.abs(detector_response_theta),
        name='Theta'
    ), 1, 1)
    fig2.append_trace(go.Scatter(
        x=freqs / units.MHz,
        y=np.abs(detector_response_phi),
        name='Phi'
    ), 1, 2)
    fig2.update_xaxes(title_text='f [MHz]', row=1, col=1)
    fig2.update_xaxes(title_text='f [MHz]', row=1, col=2)
    fig2.update_yaxes(title_text='VEL [m]', row=1, col=1)
    fig2.update_yaxes(title_text='VEL [m]', row=1, col=2)

    s = io.StringIO()
    np.savetxt(s, np.array([times, channel_trace]).T, delimiter=',')
    csv_string = "data:text/csv;charset=utf-8,%EF%BB%BF" + s.getvalue()

    return [fig, fig2, csv_string]
    def run(self, event, station, det, debug=False):
        azimuth_grid_2d, z_grid_2d = np.meshgrid(self.__azimuths_2d,
                                                 self.__z_coordinates_2d)
        distance_correlations = np.zeros(self.__distances_2d.shape)
        full_correlations = np.zeros(
            (len(self.__distances_2d), len(self.__z_coordinates_2d),
             len(self.__azimuths_2d)))

        if debug:
            plt.close('all')
            fig1 = plt.figure(figsize=(12, (len(self.__channel_pairs) // 2 +
                                            len(self.__channel_pairs) % 2)))
        self.__pair_correlations = np.zeros(
            (len(self.__channel_pairs),
             station.get_channel(
                 self.__channel_ids[0]).get_number_of_samples() +
             self.__electric_field_template.get_number_of_samples() - 1))
        for i_pair, channel_pair in enumerate(self.__channel_pairs):
            channel_1 = station.get_channel(channel_pair[0])
            channel_2 = station.get_channel(channel_pair[1])
            antenna_response = trace_utilities.get_efield_antenna_factor(
                station=station,
                frequencies=self.__electric_field_template.get_frequencies(),
                channels=[channel_pair[0]],
                detector=det,
                zenith=90. * units.deg,
                azimuth=0,
                antenna_pattern_provider=self.__antenna_pattern_provider)[0]
            voltage_spec = (
                antenna_response[0] *
                self.__electric_field_template.get_frequency_spectrum() +
                antenna_response[1] *
                self.__electric_field_template.get_frequency_spectrum()
            ) * det.get_amplifier_response(
                station.get_id(), channel_pair[0],
                self.__electric_field_template.get_frequencies())
            if self.__passband is not None:
                voltage_spec *= bandpass_filter.get_filter_response(
                    self.__electric_field_template.get_frequencies(),
                    self.__passband, 'butterabs', 10)
            voltage_template = fft.freq2time(voltage_spec,
                                             self.__sampling_rate)
            voltage_template /= np.max(np.abs(voltage_template))
            if self.__passband is None:
                corr_1 = np.abs(
                    hp.get_normalized_xcorr(channel_1.get_trace(),
                                            voltage_template))
                corr_2 = np.abs(
                    hp.get_normalized_xcorr(channel_2.get_trace(),
                                            voltage_template))
            else:
                corr_1 = np.abs(
                    hp.get_normalized_xcorr(
                        channel_1.get_filtered_trace(self.__passband,
                                                     'butterabs', 10),
                        voltage_template))
                corr_2 = np.abs(
                    hp.get_normalized_xcorr(
                        channel_2.get_filtered_trace(self.__passband,
                                                     'butterabs', 10),
                        voltage_template))
            correlation_product = np.zeros_like(corr_1)
            sample_shifts = np.arange(-len(corr_1) // 2,
                                      len(corr_1) // 2,
                                      dtype=int)
            toffset = sample_shifts / channel_1.get_sampling_rate()
            for i_shift, shift_sample in enumerate(sample_shifts):
                correlation_product[i_shift] = np.max(
                    (corr_1 * np.roll(corr_2, shift_sample)))
            self.__pair_correlations[i_pair] = correlation_product
            if debug:
                ax1_1 = fig1.add_subplot(
                    len(self.__channel_pairs) // 2 +
                    len(self.__channel_pairs) % 2, 2, i_pair + 1)
                ax1_1.grid()
                ax1_1.plot(toffset, correlation_product)
        if debug:
            fig1.tight_layout()
            fig1.savefig('{}/{}_{}_correlation.png'.format(
                self.__debug_folder, event.get_run_number(), event.get_id()))
        for i_dist, distance in enumerate(self.__distances_2d):
            self.__current_distance = distance
            correlation_sum = np.zeros_like(azimuth_grid_2d)

            for i_pair, channel_pair in enumerate(self.__channel_pairs):
                self.__correlation = self.__pair_correlations[i_pair]
                self.__channel_pair = channel_pair
                self.__channel_positions = [
                    self.__detector.get_relative_position(
                        self.__station_id, channel_pair[0]),
                    self.__detector.get_relative_position(
                        self.__station_id, channel_pair[1])
                ]
                correlation_map = np.zeros_like(correlation_sum)
                for i_ray in range(len(self.__ray_types)):
                    self.__current_ray_types = self.__ray_types[i_ray]
                    correlation_map = np.maximum(
                        self.get_correlation_array_2d(azimuth_grid_2d,
                                                      z_grid_2d),
                        correlation_map)
                correlation_sum += correlation_map
            distance_correlations[i_dist] = np.max(correlation_sum)
            full_correlations[i_dist] = correlation_sum

        corr_fit_threshold = .7 * np.max(full_correlations)
        flattened_corr = np.max(full_correlations, axis=2).T
        i_max_d = np.argmax(flattened_corr, axis=0)
        corr_mask_d = np.max(flattened_corr, axis=0) > corr_fit_threshold
        line_fit_d = np.polyfit(self.__distances_2d[corr_mask_d],
                                self.__z_coordinates_2d[i_max_d][corr_mask_d],
                                1)
        residuals_d = np.sum(
            (self.__z_coordinates_2d[i_max_d][corr_mask_d] -
             self.__distances_2d[corr_mask_d] * line_fit_d[0] - line_fit_d[1])
            **2) / np.sum(corr_mask_d.astype(int))
        i_max_z = np.argmax(flattened_corr, axis=1)
        corr_mask_z = np.max(flattened_corr, axis=1) > corr_fit_threshold
        line_fit_z = np.polyfit(self.__distances_2d[i_max_z][corr_mask_z],
                                self.__z_coordinates_2d[corr_mask_z], 1)
        residuals_z = np.sum(
            (self.__z_coordinates_2d[corr_mask_z] -
             self.__distances_2d[i_max_z][corr_mask_z] * line_fit_z[0] -
             line_fit_z[1])**2) / np.sum(corr_mask_z.astype(int))
        if residuals_d <= residuals_z:
            slope = line_fit_d[0]
            offset = line_fit_d[1]
            max_z_offset = np.max([
                50,
                np.min([
                    200,
                    np.max(self.__z_coordinates_2d[i_max_d][corr_mask_d] -
                           self.__distances_2d[corr_mask_d] * slope - offset)
                ])
            ])
            min_z_offset = np.max([
                50,
                np.min([
                    200,
                    np.max(-self.__z_coordinates_2d[i_max_d][corr_mask_d] +
                           self.__distances_2d[corr_mask_d] * slope + offset)
                ])
            ])
            flattened_corr_theta = np.max(full_correlations, axis=1)
            theta_corr_mask = np.max(flattened_corr_theta,
                                     axis=1) >= corr_fit_threshold
            i_max_theta = np.argmax(flattened_corr_theta, axis=1)
            median_theta = np.median(
                self.__azimuths_2d[i_max_theta][theta_corr_mask])
            z_fit = False
        else:
            slope = line_fit_z[0]
            offset = line_fit_z[1]
            max_z_offset = np.max([
                50,
                np.min([
                    200,
                    np.max(self.__z_coordinates_2d[corr_mask_z] -
                           self.__distances_2d[i_max_z][corr_mask_z] * slope -
                           offset)
                ])
            ])
            min_z_offset = np.max([
                50,
                np.min([
                    200,
                    np.max(-self.__z_coordinates_2d[corr_mask_z] +
                           self.__distances_2d[i_max_z][corr_mask_z] * slope +
                           offset)
                ])
            ])
            flattened_corr_theta = np.max(full_correlations, axis=0)
            theta_corr_mask = np.max(flattened_corr_theta,
                                     axis=1) >= corr_fit_threshold
            i_max_theta = np.argmax(flattened_corr_theta, axis=1)
            median_theta = np.median(
                self.__azimuths_2d[i_max_theta][theta_corr_mask])
            z_fit = True
        if debug:
            self.__draw_2d_correlation_map(event, full_correlations)
            self.__draw_search_zones(event, slope, offset, line_fit_d,
                                     line_fit_z, min_z_offset, max_z_offset,
                                     i_max_d, i_max_z, corr_mask_d,
                                     corr_mask_z, z_fit, i_max_theta,
                                     theta_corr_mask, median_theta)

        # <--- 3D Fit ---> #
        distances_3d = np.arange(self.__distances_2d[0],
                                 self.__distances_2d[-1],
                                 self.__distance_step_3d)
        z_coords = slope * distances_3d + offset
        distances_3d = distances_3d[(z_coords < 0) & (z_coords > -2700)]
        search_heights = np.arange(-1.1 * min_z_offset, 1.1 * max_z_offset,
                                   self.__z_step_3d)
        x_0, y_0, z_0 = np.meshgrid(distances_3d, self.__widths_3d,
                                    search_heights)

        z_coords = z_0 + slope * x_0 + offset
        x_coords = np.cos(median_theta) * x_0 - y_0 * np.sin(median_theta)
        y_coords = np.sin(median_theta) * x_0 + y_0 * np.cos(median_theta)

        correlation_sum = np.zeros_like(z_coords)

        for i_pair, channel_pair in enumerate(self.__channel_pairs):
            self.__correlation = self.__pair_correlations[i_pair]
            self.__channel_pair = channel_pair
            self.__channel_positions = [
                self.__detector.get_relative_position(self.__station_id,
                                                      channel_pair[0]),
                self.__detector.get_relative_position(self.__station_id,
                                                      channel_pair[1])
            ]
            correlation_map = np.zeros_like(correlation_sum)
            for i_ray in range(len(self.__ray_types)):
                self.__current_ray_types = self.__ray_types[i_ray]
                correlation_map = np.maximum(
                    self.get_correlation_array_3d(x_coords, y_coords,
                                                  z_coords), correlation_map)
            correlation_sum += correlation_map
        i_max = np.unravel_index(np.argmax(correlation_sum),
                                 correlation_sum.shape)
        if debug:
            self.__draw_vertex_reco(event,
                                    correlation_sum / np.max(correlation_sum),
                                    x_0, y_0, z_0, x_coords, y_coords,
                                    z_coords, slope, offset, median_theta,
                                    i_max)
        # <<--- DnR Reco --->> #
        self.__self_correlations = np.zeros(
            (len(self.__channel_ids), station.get_channel(
                self.__channel_ids[0]).get_number_of_samples() +
             self.__electric_field_template.get_number_of_samples() - 1))
        self_correlation_sum = np.zeros_like(z_coords)
        for i_channel, channel_id in enumerate(self.__channel_ids):
            channel = station.get_channel(channel_id)
            antenna_response = trace_utilities.get_efield_antenna_factor(
                station=station,
                frequencies=self.__electric_field_template.get_frequencies(),
                channels=[channel_id],
                detector=det,
                zenith=90. * units.deg,
                azimuth=0,
                antenna_pattern_provider=self.__antenna_pattern_provider)[0]
            voltage_spec = (
                antenna_response[0] *
                self.__electric_field_template.get_frequency_spectrum() +
                antenna_response[1] *
                self.__electric_field_template.get_frequency_spectrum()
            ) * det.get_amplifier_response(
                station.get_id(), channel_id,
                self.__electric_field_template.get_frequencies())
            if self.__passband is not None:
                voltage_spec *= bandpass_filter.get_filter_response(
                    self.__electric_field_template.get_frequencies(),
                    self.__passband, 'butter', 10)
            voltage_template = fft.freq2time(voltage_spec,
                                             self.__sampling_rate)
            voltage_template /= np.max(np.abs(voltage_template))
            if self.__passband is None:
                corr_1 = hp.get_normalized_xcorr(channel.get_trace(),
                                                 voltage_template)
                corr_2 = hp.get_normalized_xcorr(channel.get_trace(),
                                                 voltage_template)
            else:
                corr_1 = np.abs(
                    hp.get_normalized_xcorr(
                        channel.get_filtered_trace(self.__passband, 'butter',
                                                   10), voltage_template))
                corr_2 = np.abs(
                    hp.get_normalized_xcorr(
                        channel.get_filtered_trace(self.__passband, 'butter',
                                                   10), voltage_template))
            correlation_product = np.zeros_like(corr_1)
            sample_shifts = np.arange(-len(corr_1) // 2,
                                      len(corr_1) // 2,
                                      dtype=int)
            toffset = sample_shifts / channel.get_sampling_rate()
            for i_shift, shift_sample in enumerate(sample_shifts):
                correlation_product[i_shift] = np.max(
                    (corr_1 * np.roll(corr_2, shift_sample)))
            correlation_product = np.abs(correlation_product)
            correlation_product[np.abs(toffset) < 20] = 0

            self.__correlation = correlation_product
            self.__channel_pair = [channel_id, channel_id]
            self.__channel_positions = [
                self.__detector.get_relative_position(self.__station_id,
                                                      channel_id),
                self.__detector.get_relative_position(self.__station_id,
                                                      channel_id)
            ]
            correlation_map = np.zeros_like(correlation_sum)
            for i_ray in range(len(self.__ray_types)):
                if self.__ray_types[i_ray][0] != self.__ray_types[i_ray][1]:
                    self.__current_ray_types = self.__ray_types[i_ray]
                    correlation_map = np.maximum(
                        self.get_correlation_array_3d(x_coords, y_coords,
                                                      z_coords),
                        correlation_map)
            self_correlation_sum += correlation_map
        combined_correlations = correlation_sum / len(
            self.__channel_pairs) + self_correlation_sum / len(
                self.__channel_ids)
        i_max_dnr = np.unravel_index(np.argmax(combined_correlations),
                                     combined_correlations.shape)
        vertex_x = x_coords[i_max_dnr]
        vertex_y = y_coords[i_max_dnr]
        vertex_z = z_coords[i_max_dnr]
        station.set_parameter(stnp.nu_vertex,
                              np.array([vertex_x, vertex_y, vertex_z]))
        if debug:
            self.__draw_dnr_reco(
                event, correlation_sum / np.max(correlation_sum),
                self_correlation_sum / np.max(self_correlation_sum),
                combined_correlations / np.max(combined_correlations), x_0,
                y_0, z_0, slope, offset, median_theta, i_max, i_max_dnr)
예제 #27
0
    def run(
        self,
        event,
        station,
        detector,
        passband=None
    ):
        """
        Adds noise resulting from galactic radio emission to the channel traces

        Parameters
        --------------
        event: Event object
            The event containing the station to whose channels noise shall be added
        station: Station object
            The station whose channels noise shall be added to
        detector: Detector object
            The detector description
        passband: list of float
            Lower and upper bound of the frequency range in which noise shall be
            added
        """
        if passband is None:
            passband = [10 * units.MHz, 1000 * units.MHz]
        self.__gdsm = pygdsm.pygsm.GlobalSkyModel()
        site_latitude, site_longitude = detector.get_site_coordinates(station.get_id())
        site_location = astropy.coordinates.EarthLocation(lat=site_latitude * astropy.units.deg, lon=site_longitude * astropy.units.deg)
        station_time = station.get_station_time()
        station_time.format = 'iso'
        noise_temperatures = np.zeros((len(self.__interpolaiton_frequencies), healpy.pixelfunc.nside2npix(self.__n_side)))
        local_cs = astropy.coordinates.AltAz(location=site_location, obstime=station_time)
        solid_angle = healpy.pixelfunc.nside2pixarea(self.__n_side, degrees=False)
        pixel_longitudes, pixel_latitudes = healpy.pixelfunc.pix2ang(self.__n_side, range(healpy.pixelfunc.nside2npix(self.__n_side)), lonlat=True)
        pixel_longitudes *= units.deg
        pixel_latitudes *= units.deg
        galactic_coordinates = astropy.coordinates.Galactic(l=pixel_longitudes * astropy.units.rad, b=pixel_latitudes * astropy.units.rad)
        local_coordinates = galactic_coordinates.transform_to(local_cs)
        n_ice = ice.get_refractive_index(-0.01, detector.get_site(station.get_id()))
        # save noise temperatures for all directions and frequencies
        for i_freq, noise_freq in enumerate(self.__interpolaiton_frequencies):
            radio_sky = self.__gdsm.generate(noise_freq / units.MHz)
            radio_sky = healpy.pixelfunc.ud_grade(radio_sky, self.__n_side)
            noise_temperatures[i_freq] = radio_sky

        for channel in station.iter_channels():
            antenna_pattern = self.__antenna_pattern_provider.load_antenna_pattern(detector.get_antenna_model(station.get_id(), channel.get_id()))
            freqs = channel.get_frequencies()
            d_f = freqs[2] - freqs[1]
            sampling_rate = channel.get_sampling_rate()
            channel_spectrum = channel.get_frequency_spectrum()
            passband_filter = (freqs > passband[0]) & (freqs < passband[1])
            noise_spec_sum = np.zeros_like(channel.get_frequency_spectrum())
            flux_sum = np.zeros(freqs[passband_filter].shape)
            efield_sum = np.zeros((3, freqs.shape[0]), dtype=np.complex)
            if self.__debug:
                plt.close('all')
                fig = plt.figure(figsize=(12, 8))
                ax_1 = fig.add_subplot(221)
                ax_2 = fig.add_subplot(222)
                ax_3 = fig.add_subplot(223)
                ax_4 = fig.add_subplot(224)
                ax_1.grid()
                ax_1.set_yscale('log')
                ax_2.grid()
                ax_2.set_yscale('log')
                ax_3.grid()
                ax_3.set_yscale('log')
                ax_4.grid()
                ax_4.set_yscale('log')
                ax_1.set_xlabel('f [MHz]')
                ax_2.set_xlabel('f [MHz]')
                ax_3.set_xlabel('f [MHz]')
                ax_4.set_xlabel('f [MHz]')
                ax_1.set_ylabel('T [K]')
                ax_2.set_ylabel('S [W/m²/MHz]')
                ax_3.set_ylabel('E [V/m]')
                ax_4.set_ylabel('U [V]')
                ax_4.plot(channel.get_frequencies() / units.MHz, np.abs(channel.get_frequency_spectrum()), c='C0')
                ax_4.set_ylim([1.e-8, None])
                fig1 = plt.figure()
                ax1 = fig1.add_subplot(211)
                ax2 = fig1.add_subplot(212)
                ax1.plot(channel.get_times(), channel.get_trace(), label='original trace')
                ax2.plot(channel.get_frequencies() / units.MHz, np.abs(channel.get_frequency_spectrum()), label='original spectrum')
                ax1.grid()
                ax2.grid()
            for i_pixel in range(healpy.pixelfunc.nside2npix(self.__n_side)):
                azimuth = local_coordinates[i_pixel].az.rad
                zenith = np.pi / 2. - local_coordinates[i_pixel].alt.rad
                if zenith > 90. * units.deg:
                    continue
                temperature_interpolator = scipy.interpolate.interp1d(self.__interpolaiton_frequencies, np.log10(noise_temperatures[:, i_pixel]), kind='quadratic')
                noise_temperature = np.power(10, temperature_interpolator(freqs[passband_filter]))
                # calculate spectral radiance of radio signal using rayleigh-jeans law
                S = (2. * (scipy.constants.Boltzmann * units.joule / units.kelvin) * freqs[passband_filter]**2 / (scipy.constants.c * units.m / units.s)**2 * (noise_temperature) * solid_angle)
                S[np.isnan(S)] = 0
                # calculate radiance per energy bin
                S_per_bin = S * d_f
                flux_sum += S_per_bin
                # calculate electric field per energy bin from the radiance per bin
                E = np.sqrt(S_per_bin / (scipy.constants.c * units.m / units.s * scipy.constants.epsilon_0 * (units.coulomb / units.V / units.m))) / (d_f)
                if self.__debug:
                    ax_1.scatter(self.__interpolaiton_frequencies / units.MHz, noise_temperatures[:, i_pixel] / units.kelvin, c='k', alpha=.01)
                    ax_1.plot(freqs[passband_filter] / units.MHz, noise_temperature, c='k', alpha=.02)
                    ax_2.plot(freqs[passband_filter] / units.MHz, S_per_bin / d_f / (units.watt / units.m**2 / units.MHz), c='k', alpha=.02)
                    ax_3.plot(freqs[passband_filter] / units.MHz, E / (units.V / units.m), c='k', alpha=.02)

                # assign random phases and polarizations to electric field
                noise_spectrum = np.zeros((3, freqs.shape[0]), dtype=np.complex)
                phases = np.random.uniform(0, 2. * np.pi, len(S))
                polarizations = np.random.uniform(0, 2. * np.pi, len(S))

                noise_spectrum[1][passband_filter] = np.exp(1j * phases) * E * np.cos(polarizations)
                noise_spectrum[2][passband_filter] = np.exp(1j * phases) * E * np.sin(polarizations)
                efield_sum += noise_spectrum
                antenna_orientation = detector.get_antenna_orientation(station.get_id(), channel.get_id())
                # consider signal reflection at ice surface
                if detector.get_relative_position(station.get_id(), channel.get_id())[2] < 0:
                    t_theta = geometryUtilities.get_fresnel_t_p(zenith, n_ice, 1)
                    t_phi = geometryUtilities.get_fresnel_t_s(zenith, n_ice, 1)
                    fresnel_zenith = geometryUtilities.get_fresnel_angle(zenith, 1., n_ice)
                    if fresnel_zenith is None:
                        continue
                else:
                    t_theta = 1
                    t_phi = 1
                    fresnel_zenith = zenith
                # fold electric field with antenna response
                antenna_response = antenna_pattern.get_antenna_response_vectorized(freqs, fresnel_zenith, azimuth, *antenna_orientation)
                channel_noise_spectrum = antenna_response['theta'] * noise_spectrum[1] * t_theta + antenna_response['phi'] * noise_spectrum[2] * t_phi
                if self.__debug:
                    ax_4.plot(freqs / units.MHz, np.abs(channel_noise_spectrum) / units.V, c='k', alpha=.01)
                channel_spectrum += channel_noise_spectrum
                noise_spec_sum += channel_noise_spectrum
            channel.set_frequency_spectrum(channel_spectrum, sampling_rate)
            if self.__debug:
                ax_2.plot(freqs[passband_filter] / units.MHz, flux_sum / d_f / (units.watt / units.m**2 / units.MHz), c='C0', label='total flux')
                ax_3.plot(freqs[passband_filter] / units.MHz, np.sqrt(np.abs(efield_sum[1])**2 + np.abs(efield_sum[2])**2)[passband_filter] / (units.V / units.m), c='k', linestyle='-', label='sum of E-fields')
                ax_3.plot(freqs[passband_filter] / units.MHz, np.sqrt(flux_sum / (scipy.constants.c * (units.m / units.s)) / (scipy.constants.epsilon_0 * (units.farad / units.m))) / d_f / (units.V / units.m), c='C2', label='E-field from total flux')
                ax_4.plot(channel.get_frequencies() / units.MHz, np.abs(noise_spec_sum), c='k', linestyle=':', label='total noise')
                ax_2.legend()
                ax_3.legend()
                ax_4.legend()
                fig.tight_layout()
                ax1.plot(channel.get_times(), channel.get_trace(), label='new trace')
                ax1.plot(channel.get_times(), fft.freq2time(noise_spec_sum, channel.get_sampling_rate()), label='noise')
                ax2.plot(channel.get_frequencies() / units.MHz, np.abs(channel.get_frequency_spectrum()), label='new spectrum')
                ax2.plot(channel.get_frequencies() / units.MHz, np.abs(noise_spec_sum), label='noise')
                ax1.legend()
                ax2.legend()
                plt.show()
예제 #28
0
ax.legend()
ax.set_title("askaryan module")
plt.show()
a = 1 / 0

# 2nd pyrex
fig, ax = plt.subplots(1, 1)
n_trace = 2**10  # samples of trace
dt = 0.1 * units.ns
tt = np.arange(0, n_trace * dt, dt)
ff = np.fft.rfftfreq(n_trace, dt)

trace = fft.freq2time(
    param.get_frequency_spectrum(E,
                                 theta,
                                 ff,
                                 0,
                                 n_index,
                                 R,
                                 model='Alvarez2012'), 1 / dt)
ax.plot(tt / units.ns,
        trace,
        label="dt = {:.2f}ns, n={}".format(dt / units.ns, n_trace))

n_trace = 2**12  # samples of trace
dt = 0.1 * units.ns
tt = np.arange(0, n_trace * dt, dt)
ff = np.fft.rfftfreq(n_trace, dt)
trace = fft.freq2time(
    param.get_frequency_spectrum(E,
                                 theta,
                                 ff,