def test_is_hit(self, triggerable_antenna): """Test that is_hit is true when there is a triggering signal and false otherwise""" assert not triggerable_antenna.is_hit triggerable_antenna.signals.append(Signal([0, 1], [0, 0])) assert not triggerable_antenna.is_hit triggerable_antenna.signals.append(Signal([0, 1], [0, 2])) assert triggerable_antenna.is_hit
def receive(self, signal, origin=None, polarization=None): """Process incoming signal according to the filter function and store it to the signals list. Subclasses may extend this fuction, but should end with super().receive(signal).""" copy = Signal(signal.times, signal.values, value_type=Signal.ValueTypes.voltage) copy.filter_frequencies(self.response) if origin is None: d_gain = 1 else: # Calculate theta and phi relative to the orientation r, theta, phi = self._convert_to_antenna_coordinates(origin) d_gain = self.directional_gain(theta=theta, phi=phi) if polarization is None: p_gain = 1 else: p_gain = self.polarization_gain(normalize(polarization)) signal_factor = d_gain * p_gain * self.efficiency if signal.value_type==Signal.ValueTypes.voltage: pass elif signal.value_type==Signal.ValueTypes.field: signal_factor /= self.antenna_factor else: raise ValueError("Signal's value type must be either " +"voltage or field. Given "+str(signal.value_type)) copy.values *= signal_factor self.signals.append(copy)
def front_end(self, signal): """ Apply front-end processes to a signal and return the output. The front-end consists of amplification according to data taken from NuRadioReco and signal clipping. Parameters ---------- signal : Signal ``Signal`` object on which to apply the front-end processes. Returns ------- Signal Signal processed by the antenna front end. """ copy = Signal(signal.times, signal.values) copy.filter_frequencies(self.interpolate_filter, force_real=True) clipped_values = np.clip(copy.values * self.amplification, a_min=-self.amplifier_clipping, a_max=self.amplifier_clipping) return Signal(signal.times, clipped_values, value_type=signal.value_type)
def test_uniqueness(self, signals): """Test that a new signal made from the values of the old one are not connected""" new = Signal(signals.times, signals.values) new.times[0] = -10000 new.values[0] = 10000 assert new.times[0] != signals.times[0] assert new.values[0] != signals.values[0]
def front_end(self, signal): """ Apply front-end processes to a signal and return the output. The front-end consists of the full ARA electronics chain (including amplification) and signal clipping. Parameters ---------- signal : Signal ``Signal`` object on which to apply the front-end processes. Returns ------- Signal Signal processed by the antenna front end. """ copy = Signal(signal.times, signal.values) copy.filter_frequencies(self.interpolate_filter, force_real=True) # sqrt(2) for 3dB splitter for TURF, SURF clipped_values = np.clip(copy.values / np.sqrt(2) * self.amplification, a_min=-self.amplifier_clipping, a_max=self.amplifier_clipping) return Signal(signal.times, clipped_values, value_type=signal.value_type)
def test_full_waveform(self, triggerable_antenna): """Test that full_waveform incorporates all waveforms (even untriggered)""" triggerable_antenna.signals.append(Signal([1], [2])) triggerable_antenna.signals.append(Signal([0], [0.5])) triggerable_antenna.signals.append(Signal([0, 1, 2], [0.1, 0.1, 0.1])) full = triggerable_antenna.full_waveform([-1, 0, 1, 2, 3]) assert np.array_equal(full.values, [0, 0.6, 2.1, 0.1, 0])
def test_addition_value_type_failure(self): """Test that adding signal objects with different value types fails""" signal_1 = Signal([0,1,2,3,4], [1,2,1,2,1], value_type=Signal.Type.voltage) signal_2 = Signal([0,1,2,3,4], [2,3,2,3,2], value_type=Signal.Type.field) with pytest.raises(ValueError): signal_sum = signal_1 + signal_2
def test_is_hit_during(self, noiseless_halver): """Test that is_hit_during works with the front end""" noiseless_halver.antenna.signals.append(Signal([0, 1e-9], [0, 1])) assert not noiseless_halver.is_hit_during([0, 1, 2]) noiseless_halver.antenna.signals.append(Signal([0, 1e-9], [0, 4])) assert noiseless_halver.is_hit_during([0, 1e-9, 2e-9]) assert not noiseless_halver.is_hit_during([-2e-9, -1e-9, 0]) assert not noiseless_halver.is_hit_during([2e-9, 3e-9, 4e-9])
def make_envelope(self, signal): """Return the signal envelope based on the antenna's envelope_method.""" if "hilbert" in self.envelope_method: return Signal(signal.times, signal.envelope, value_type=signal.value_type) elif "analytic" in self.envelope_method: if ("basic" in self.envelope_method or self.envelope_method == "analytic"): return basic_envelope_model(signal) else: raise ValueError("Only basic envelope circuit is modeled " + "analytically") elif "spice" in self.envelope_method: if not (pyspice.__available__): raise ModuleNotFoundError(pyspice.__modulenotfound__) if self.envelope_method == "spice": raise ValueError("Type of spice circuit to use must be " + "specified") copy = Signal(signal.times - signal.times[0], signal.values) ngspice_in = pyspice.SpiceSignal(copy) circuit = None # Try to match circuit name in spice_circuits keys for key, val in spice_circuits.items(): if key in self.envelope_method: circuit = val break # If circuit not matched, try manual matching of circuit name if circuit is None: if "simple" in self.envelope_method: circuit = spice_circuits['basic'] elif ("log amp" in self.envelope_method or "logarithmic amp" in self.envelope_method): circuit = spice_circuits['logamp'] elif "rectifier" in self.envelope_method: circuit = spice_circuits['bridge'] # If still no circuits match, raise error if circuit is None: raise ValueError("Circuit '" + self.envelope_method + "' not implemented") simulator = circuit.simulator(temperature=25, nominal_temperature=25, ngspice_shared=ngspice_in.shared) analysis = simulator.transient(step_time=signal.dt, end_time=copy.times[-1]) return Signal(signal.times, analysis.output, value_type=signal.value_type) else: raise ValueError("No envelope method matching '" + self.envelope_method + "'")
def test_is_hit(self, noiseless_halver): """Test that is_hit works with the front end""" assert not noiseless_halver.is_hit assert not noiseless_halver.antenna.is_hit noiseless_halver.antenna.signals.append(Signal([0, 1e-9], [1, 1])) assert not noiseless_halver.is_hit assert noiseless_halver.antenna.is_hit noiseless_halver.antenna.signals.append(Signal([0, 1e-9], [4, 4])) assert noiseless_halver.is_hit assert noiseless_halver.antenna.is_hit
def front_end(self, signal): """Apply the front-end processing of the antenna signal, including electronics chain filters/amplification and clipping.""" copy = Signal(signal.times, signal.values) copy.filter_frequencies(self.antenna.interpolate_filter, force_real=True) clipped_values = np.clip(copy.values, a_min=-self.amplifier_clipping, a_max=self.amplifier_clipping) return Signal(signal.times, clipped_values, value_type=signal.value_type)
def test_is_hit_mc_truth(self, halver): """Test that is_hit_mc_truth works with the front end""" np.random.seed(SEED) halver.antenna.noise_rms = 100 assert not halver.is_hit_mc_truth assert not halver.antenna.is_hit_mc_truth halver.antenna.signals.append(Signal([0, 1e-9], [1, 1])) assert not halver.is_hit_mc_truth assert not halver.antenna.is_hit_mc_truth halver.antenna.signals.append(Signal([0, 1e-9], [4, 4])) assert not halver.is_hit_mc_truth assert not halver.antenna.is_hit_mc_truth
def test_addition(self, signals): """Test that signal objects can be added""" expected = Signal(signals.times, 2*signals.values, signals.value_type) signal_sum = signals + signals assert np.array_equal(signal_sum.times, expected.times) assert np.array_equal(signal_sum.values, expected.values) assert signal_sum.value_type == expected.value_type
def test_default_frontend(self, ant_sys): """Test that the default front end just passes along the signal""" signal = Signal([0, 1, 2], [1, 2, 1]) fe_sig = ant_sys.front_end(signal) assert np.array_equal(fe_sig.times, signal.times) assert np.array_equal(fe_sig.values, signal.values) assert fe_sig.value_type == signal.value_type
def test_receive(self, ant_obj_sys): """Test that receive is passed along to underlying antenna""" assert ant_obj_sys.signals == [] assert ant_obj_sys.antenna.signals == [] ant_obj_sys.receive(Signal([0, 1, 2], [1, 2, 1], Signal.Type.voltage)) assert ant_obj_sys.signals != [] assert ant_obj_sys.antenna.signals != []
def test_full_waveform(self, halver): """Test that front end is applied to full waveform""" halver.antenna.signals.append(Signal([0, 0.5e-9, 1e-9], [2, 4, 2])) assert np.array_equal( halver.full_waveform([-0.5e-9, 0, 0.5e-9, 1e-9, 1.5e-9]).values, halver.antenna.full_waveform([-0.5e-9, 0, 0.5e-9, 1e-9, 1.5e-9 ]).values / 2)
def test_all_waveforms(self, halver): """Test that front end is applied to all_waveforms array""" halver.antenna.signals.append( Signal([0, 0.5e-9, 1e-9], [0.2, 0.4, 0.2])) assert halver.waveforms == [] assert np.array_equal(halver.all_waveforms[0].values, halver.antenna.all_waveforms[0].values / 2)
def test_delay_noise_calculation(self, antenna): """Test that antenna noise isn't calculated until it is needed""" antenna.receive(Signal([0, 1e-9, 2e-9], [0, 1, 0], Signal.Type.voltage)) assert antenna._noise_master is None antenna.waveforms assert antenna._noise_master is not None
def test_triggered(self, dummy_str): """Test that the default trigger just checks trigger of any antenna""" assert not dummy_str.triggered() dummy_str.build_antennas(antenna_class=Antenna, noisy=False) assert not dummy_str.triggered() dummy_str.subsets[0].signals.append(Signal([0, 1e-9], [0, 1])) assert dummy_str.triggered()
def test_triggered(self, dummy_combo): """Test that CombinedDetector trigger just checks trigger of any subset""" assert not dummy_combo.triggered() dummy_combo.build_antennas(antenna_class=Antenna, noisy=False) assert not dummy_combo.triggered() dummy_combo.subsets[0][0].signals.append(Signal([0, 1e-9], [0, 1])) assert dummy_combo.triggered()
def test_waveforms_exist(self, antenna): """Test that waveforms returns a waveform when a signal has been received""" antenna.receive(Signal([0,1e-9,2e-9], [0,1,0], Signal.ValueTypes.voltage)) assert antenna.waveforms != [] assert isinstance(antenna.waveforms[0], Signal) assert antenna._noises != [] assert antenna._triggers == [True]
def test_with_times(self, signal): """Test that with_times method works as expected""" times = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7] new = signal.with_times(times) expected = Signal(times, [0, 0, 1, 2, 1, 2, 1, 0, 0, 0]) for i in range(10): assert new.values[i] == pytest.approx(expected.values[i])
def test_is_hit_mc_truth(self, triggerable_antenna): """Test that is_hit_mc_truth appropriately rejects noise triggers""" assert not triggerable_antenna.is_hit_mc_truth triggerable_antenna.signals.append(Signal([0, 1], [0, 0])) assert not triggerable_antenna.is_hit_mc_truth triggerable_antenna.signals.append(Signal([0, 1], [0, 2])) assert triggerable_antenna.is_hit_mc_truth np.random.seed(SEED) noisy_antenna = Antenna(position=[0, 0, -200], freq_range=[500e6, 750e6], noise_rms=100) assert not noisy_antenna.is_hit_mc_truth noisy_antenna.signals.append(Signal([0, 1e-9], [0, 0])) assert not noisy_antenna.is_hit_mc_truth noisy_antenna.signals.append(Signal([0, 1e-9], [0, 2])) assert not noisy_antenna.is_hit_mc_truth
def test_with_times(self, signal): """Test that with_times method works as expected, interpolating and zero-padding""" times = np.linspace(-2, 7, 19) new = signal.with_times(times) expected = Signal(times, [0,0,0,0,1,1.5,2,1.5,1,1.5,2,1.5,1,0,0,0,0,0,0]) assert np.array_equal(new.values, expected.values) assert new.value_type == signal.value_type
def test_no_trigger_no_waveform(self, antenna): """Test that signals which don't trigger don't appear in waveforms, but do appear in all_waveforms""" antenna.trigger = lambda signal: False antenna.signals.append(Signal([0],[1])) assert antenna.is_hit == False assert antenna.waveforms == [] assert antenna.all_waveforms != []
def test_noises_not_recalculated(self, antenna): """Test that noise signals aren't recalculated every time""" antenna.signals.append(Signal([0],[1])) waveforms1 = antenna.waveforms noises1 = antenna._noises waveforms2 = antenna.waveforms noises2 = antenna._noises assert noises1 == noises2
def test_resample(self, signal): """Test signal resampling""" expected = Signal(signal.times[::2], [1.2, 1.6258408572364818, 1.3741591427635182]) signal.resample(3) for i in range(3): assert signal.times[i] == expected.times[i] assert signal.values[i] == pytest.approx(expected.values[i])
def test_clear(self, ant_obj_sys): """Test that clear clears the system and the underlying antenna""" ant_obj_sys.receive(Signal([0, 1, 2], [1, 2, 1], Signal.Type.voltage)) assert ant_obj_sys.signals != [] assert ant_obj_sys.antenna.signals != [] ant_obj_sys.clear() assert ant_obj_sys.signals == [] assert ant_obj_sys.antenna.signals == []
def noiseless_halver(): """Fixture for forming AntennaSystem which halves signals""" ant = Antenna(position=[0, 0, -250], noisy=False) ant_sys = AntennaSystem(ant) ant_sys.front_end = lambda signal: Signal( signal.times, signal.values / 2, value_type=signal.value_type) ant_sys.trigger = lambda signal: np.max(signal.values) > 1 return ant_sys
def test_noises_not_recalculated(self, antenna): """Test that noise signals aren't recalculated every time""" antenna.signals.append(Signal([0, 1e-9], [1, 1])) waveforms1 = antenna.waveforms noise_master_1 = antenna._noise_master waveforms2 = antenna.waveforms noise_master_2 = antenna._noise_master assert noise_master_1 == noise_master_2