Signals and traces
Traces, signal processing and detector layouts: grand.basis.
Handling a set of 3D traces |
|
The two kinds of trace a detector unit carries. |
|
Handling Detector Unit (DU) network, footprint plots |
|
Functions used in the pipline execution. |
grand.basis.traces_event
Handling a set of 3D traces
- grand.basis.traces_event.get_psd(trace, f_samp_mhz, nperseg=0)[source]
Reference estimation of power spectrum density by Welch’s method
- class grand.basis.traces_event.Handling3dTraces(name='NotDefined')[source]
Bases:
objectHandling a set of traces associated to one event observed on Detector Unit network
Initialisation, with two methods:
init_traces()
optionally with init_network()
Features:
some plots : trace , power spectrum, footprint, …
compute time where the trace is maximun
compute time for each sample of trace
compute a common time for trace
Public attributes:
name str: name of the set of trace
traces float(nb_du, 3, nb_sample): trace 3D
idx2idt int(nb_du): array of identifier of DU
t_start_ns float(nb_du): [ns] time of first sample of trace
t_samples float(nb_du, nb_dim, nb_sample): [ns]
f_samp_mhz float(nb_du,): [MHz] frequency sampling
idt2idx dict: for each identifier return the index in array
unit_trace str: string unit of trace
network object: content position network
- init_traces(traces, du_id=None, t_start_ns=None, f_samp_mhz=2000)[source]
- Parameters:
traces (ndarray, shape (n_du, 3, n_samples)) – The traces.
du_id (sequence, optional) – Identifier of each unit.
t_start_ns (ndarray, optional) – Start time of each trace, in nanoseconds.
f_samp_mhz (float or ndarray, optional) – Sampling frequency, in MHz.
- init_network(du_pos)[source]
- Parameters:
du_pos (ndarray, shape (n_du, 3)) – Position of each unit, in the array frame.
- keep_only_trace_with_ident(l_idt)[source]
Keep trace with identifier defined in list <l_idt>
- Parameters:
l_idt (sequence) – Identifiers of the units to keep.
- keep_only_trace_with_index(l_idx)[source]
Keep trace at index defined in list <l_idx>
- Parameters:
l_idx (sequence of int) – Indices of the traces to keep.
- reduce_nb_trace(new_nb_du)[source]
reduces the number of traces to the first <new_nb_du>
Feature to reduce computation, for debugging
- Parameters:
new_nb_du (int) – Number of traces to keep, taken from the start.
- downsize_sampling(fact)[source]
Downsampling with scipy decimate function
- Parameters:
fact (int) – Decimation factor.
- remove_trace_low_signal(threshold, norm_traces=None)[source]
Remove trace where <norm_traces> is lower than <threshold>
- Parameters:
threshold (float) – Minimum amplitude to keep.
norm_traces (ndarray, optional) – Precomputed norms, to avoid recomputing them.
- Returns:
Indices of the traces that were kept.
- Return type:
ndarray
- copy(new_traces=None, deepcopy=True)[source]
Return a copy of current object where traces can be modify
The type of copy is copy with reference, not a deepcopy https://stackoverflow.com/questions/3975376/why-updating-shallow-copy-dictionary-doesnt-update-original-dictionary/3975388#3975388
- if new_traces is :
None : object with same value
0 : the return object has a traces with same shape but set to 0
np.array : the return object has new_traces as traces
- Parameters:
new_traces (ndarray, optional) – Traces for the copy; the existing ones by default.
deepcopy (bool, optional) – Copy the arrays rather than referencing them.
- Returns:
The copy.
- Return type:
- get_delta_t_ns()[source]
Return sampling rate in ns
- Returns:
Sampling interval of each trace, in nanoseconds.
- Return type:
ndarray
- get_max_abs()[source]
Find absolute maximal value in trace for each detector
- Returns:
Largest absolute value of each trace.
- Return type:
ndarray
- get_max_norm()[source]
Return array of maximal of 3D norm in trace for each detector
- Returns:
Largest three-component norm of each trace.
- Return type:
ndarray
- get_norm()[source]
Return norm of traces for each time sample
- Returns:
Three-component norm at every sample.
- Return type:
ndarray, shape (n_du, n_samples)
- get_tmax_vmax(hilbert=True, interpol='parab')[source]
Return time where norm of the amplitude of the Hilbert tranform is max
- get_size_trace()[source]
Return the number of sample in trace
- Returns:
Number of samples per trace.
- Return type:
- get_snr_and_noise()[source]
Return a crude estimation of SNR and noise level
- Crude estimation because:
noise is estimated at the end of trace
max value has different estimator.
- Returns:
snr (ndarray) – Signal-to-noise ratio of each trace.
v_max (ndarray) – Peak of each trace’s Hilbert envelope, in the traces’ unit.
noise (ndarray) – Noise level of each trace (largest standard deviation over the three axes of the trace’s last samples), in the same unit.
- get_extended_traces()[source]
Return extended traces to time interval of event
Compute and return traces extended to the entire duration of the event with common time
- Returns:
Traces padded onto a common time axis, and that axis.
- Return type:
tuple of ndarray
- plot_psd_trace_idx(idx, to_draw='012')[source]
Draw power spectrum for 3 traces associated to DU at index idx
grand.basis.signal
This module contains several signal processing functionalities to be applied to simulation/data
operations are meant to be on the signal traces for individual antennas, suitable to be used both in Grandlib format/ read from hdf5 files
expects signal traces to be of the size (3,lengthoftrace)
- grand.basis.signal.find_max_with_parabola_interp_3pt(x_trace, y_trace, idx_max)[source]
Returns the interpolated maximum of a trace, from a parabola through 3 points.
Fits \(ax^2 + bx + c\) through the largest sample and its two neighbors, offset to \((x_0, y_0)\), and returns the vertex:
\[x_m = x_0 - \frac{b}{2a}, \qquad y_m = y_0 - \frac{b^2}{4a}\]The trace is assumed non-negative, and
idx_maxis assumed to be a genuine local maximum with a sample on either side.- Parameters:
x_trace (ndarray) – Sample positions.
y_trace (ndarray) – Sample values.
idx_max (int) – Index of the largest sample.
- Returns:
Position and value of the interpolated maximum, from a parabola through three points.
- Return type:
Examples
A peak between two samples is recovered more precisely than the sample grid allows.
import numpy as np from grand.basis.signal import find_max_with_parabola_interp_3pt t = np.arange(512) * 0.5 true_peak = 100.25 # deliberately between samples y = np.exp(-((t - true_peak) ** 2) / (2 * 6.0 ** 2)) idx = int(np.argmax(y)) t_interp, v = find_max_with_parabola_interp_3pt(t, y, idx) print("nearest sample : %.4f ns" % t[idx]) print("interpolated : %.4f ns" % t_interp)
nearest sample : 100.0000 ns interpolated : 100.2500 ns
- grand.basis.signal.find_max_with_parabola_interp(x_trace, y_trace, idx_max, factor_hill=0.96)[source]
Parabolic interpolation of the maximum with more than 3 points
trace : all values >= 0
- algo:
find begin idx, ie trace[–idx_max] > v_max*factor_hill
find end idx, ie trace[idx_max++] > v_max*factor_hill
if nb idx <= 2 : mode pic else mode hill
Mode pic : 3 values and the middle one is max 4.1 offset of (x0, v0) 4.2 solve coef a, b => x_m = offset - b/2a ; v_m=offset - b^2/4a
Mode hill: 5.0 offset of (x, y) of first sample 5.1 solve overdetermined linear system with a, b, c 5.2 x_m =offset - b/2a ; v_m=offset - b^2/4a + c
- Parameters:
- Returns:
Position and value of the interpolated maximum.
- Return type:
- grand.basis.signal.get_filter(time, trace, fr_min, fr_max)[source]
Filter signal in given bandwidth
- @note
At present Butterworth filter only is implemented, others: what is close to hardware filter?
- Parameters:
time (ndarray) – Time axis, in nanoseconds.
trace (ndarray) – Trace to filter.
fr_min (float) – Lower band edge, in Hz. Note the unit: most frequency arguments in this package are in MHz, and this one is not – passing 50 rather than 50e6 silently returns an all-zero trace.
fr_max (float) – Upper band edge, in Hz.
- Returns:
The band-passed trace.
- Return type:
ndarray
Examples
The band edges are in hertz. Passing megahertz does not raise; it silently returns an empty trace, which is the failure most easily mistaken for a quiet event.
import numpy as np from grand.basis.signal import get_filter t = np.arange(1024) * 0.5 # ns tone = np.sin(2 * np.pi * 100.0 * t * 1e-3) # a 100 MHz tone kept = get_filter(t, tone, 50e6, 200e6) # edges in Hz print("in band, kept: %.3f" % np.std(kept)) # The same numbers read as MHz pass nothing at all -- no error, just zeros. print("edges given in MHz: %.3f" % np.std(get_filter(t, tone, 50.0, 200.0)))
in band, kept: 0.701 edges given in MHz: 0.000
- grand.basis.signal.get_peakamptime_norm_hilbert(a2_time, a3_trace)[source]
Get peak Hilbert amplitude norm of trace (v_max) and its time t_max without interpolation
- Parameters:
a2_time (ndarray) – Time axis per trace.
a3_trace (ndarray, shape (n_du, 3, n_samples)) – Traces.
- Returns:
Peak time, peak amplitude, the norm, and its Hilbert envelope.
- Return type:
tuple of ndarray
- grand.basis.signal.get_fastest_size_fft(sig_size, f_samp_mhz, padding_fact=1)[source]
Returns an FFT-friendly transform length and its frequency axis.
The same as
grand.sim.efield2voltage.get_fastest_size_fft(), whose padding argument is namedpadding_factor.
- grand.basis.signal.interpol_at_new_x(a_x, a_y, new_x)[source]
#RK: This function is copied to galaxy.py and rf_chain.py where it is used. Remove it from here if it is not used anywhere else.
Interpolation of discreet function F defined by set of point F(a_x)=a_y for new_x value and set to zero outside interval definition a_x
- Parameters:
a_x (ndarray) – Sample positions.
a_y (ndarray) – Sample values.
new_x (ndarray) – Positions to interpolate onto.
- Returns:
Interpolated values, zero outside the range of a_x.
- Return type:
ndarray
Examples
Values outside the measured range come back as zero rather than extrapolated: the tabulated antenna and RF-chain data have no meaning beyond 30-250 MHz.
import numpy as np from grand.basis.signal import interpol_at_new_x a_x = np.linspace(30.0, 250.0, 100) # the measured band, in MHz a_y = np.ones_like(a_x) print(interpol_at_new_x(a_x, a_y, np.array([10.0, 140.0, 400.0])))
[0. 1. 0.]
grand.basis.type_trace
The two kinds of trace a detector unit carries.
ElectricField is the field at the antenna, before the instrument
touches it; Voltage is what comes out of it. Both are dataclasses
over a time axis, and both cache their own transform.
They are not symmetric, which is worth knowing before reaching for one:
ElectricField carries the antenna position and the coordinate frame its
three components are expressed in, and Voltage carries neither – only
t and V. A voltage trace therefore cannot say what frame it is in,
and the caller has to keep track.
- class grand.basis.type_trace.ElectricField(a_time: ndarray, e_xyz: CartesianRepresentation, pos_xyz: CartesianRepresentation | None = None, frame: LTP | GRANDCS | None = None)[source]
Bases:
objectA three-component electric field trace, with its time axis and frame.
Holds the samples for one detection unit: three components against a shared time axis, together with the coordinate frame they are expressed in.
- Parameters:
a_time (np.ndarray) – Time of each sample, in s (the voltage chain passes ns times 1e-9)
e_xyz (CartesianRepresentation) – The three components of the field, shape (3, n_samples), in µV/m
pos_xyz (Union[CartesianRepresentation, None]) – Position of the detection unit, in m, in frame
frame (Union[LTP, GRANDCS, None]) – Frame in which e_xyz and pos_xyz are expressed
- e_xyz: CartesianRepresentation
The three components of the field, shape (3, n_samples), in µV/m
- pos_xyz: CartesianRepresentation | None = None
Position of the detection unit, in m, in frame
- get_fft(size_sig_pad)[source]
- Parameters:
size_sig_pad (int) – Padded transform length.
- Returns:
The one-sided spectrum of the three components.
- Return type:
ndarray, shape (3, n_freq)
- class grand.basis.type_trace.Voltage(t: ndarray, V: ndarray)[source]
Bases:
objectA three-component voltage trace and its time axis.
Holds the samples for one detection unit:
Vagainstt. UnlikeElectricFieldit carries no position and no coordinate frame – the fields aretandVand nothing else – so what frame the components are in is the caller’s to remember.- Parameters:
t (np.ndarray) – Time of each sample
V (np.ndarray) – The three components, shape (3, n_samples), in the unit of the caller’s input (µV in the voltage chain)
- V: ndarray
The three components, shape (3, n_samples), in the unit of the caller’s input (µV in the voltage chain)
- get_fft(size_sig_pad)[source]
- Parameters:
size_sig_pad (int) – Padded transform length.
- Returns:
The one-sided spectrum of the three components.
- Return type:
ndarray, shape (3, n_freq)
grand.basis.du_network
Handling Detector Unit (DU) network, footprint plots
- grand.basis.du_network.closest_node(node, nodes)[source]
Simple computation of distance mouse DU
- Parameters:
node (array_like) – Reference position.
nodes (ndarray) – Positions to search.
- Returns:
Index of the nearest of nodes.
- Return type:
- class grand.basis.du_network.DetectorUnitNetwork(name='NotDefined')[source]
Bases:
objectHandling a DU network
Public attributs:
name str: name of the set of trace
du_pos float(nb_du, 3): position of DU
idx2idt int(nb_du): array of identifier of DU
- init_pos_id(du_pos, du_id=None)[source]
Init object with array DU position and identifier
- Parameters:
du_pos (ndarray, shape (n_du, 3)) – Position of each unit, in meters.
du_id (sequence, optional) – Identifier of each unit.
- keep_only_du_with_index(l_idx)[source]
Keep DU at index defined in list <l_idx>
- Parameters:
l_idx (sequence of int) – Indices to keep.
- reduce_nb_du(new_nb_du)[source]
Feature to debug and reduce computation
- Parameters:
new_nb_du (int) – Number of units to keep, taken from the start.
- get_sub_network(l_id)[source]
Reduce networh to DU in list <l_id>
- Parameters:
l_id (sequence) – Identifiers to keep.
- Returns:
A network holding only those units.
- Return type:
- get_surface()[source]
Return suface in km2
- Returns:
Area covered by the array, in square kilometres.
- Return type:
- get_max_dist_du()[source]
Return the largest distance between two DUs of the network.
- Returns:
The distance, in kilometres; 0 for fewer than two DUs.
- Return type:
- plot_footprint_1d(a_values, title='', traces=None, scale='log', unit='')[source]
Interactive footprint double click on DU draw trace associated and power spectrum
- Parameters:
a_values (ndarray) – Per-unit value to color by.
title (str, optional) – Plot title.
traces (Handling3dTraces, optional) – Traces to show when a unit is clicked.
scale (str, optional) –
"log"or"lin".unit (str, optional) – Unit of a_values, for the color bar.
- plot_footprint_4d(o_tr, v_plot, title='', same_scale=True, unit='')[source]
Plot footprint of time max by DU and value max by component
- Parameters:
o_tr (Handling3dTraces) – Traces to animate.
v_plot (str, optional) – Quantity to plot.
title (str, optional) – Plot title.
same_scale (bool, optional) – Use one color scale for all components.
unit (str, optional) – Unit for the color bar.
- Returns:
Draws the figure.
- Return type:
None
- plot_footprint_time(a_time, a3_values, title='')[source]
Interactive plot, footprint max value for time defined by user with slider widget
- Parameters:
a_time (ndarray) – Time axis, in nanoseconds.
a3_values (ndarray) – Per-unit, per-time values.
title (str, optional) – Plot title.
- Returns:
Draws the figure.
- Return type:
None
grand.basis.pipeline
Functions used in the pipline execution.
- RK TODO: implement techniques used in LP’s execute_pipeline where output_trees
and time-traces are saved on event-by-event basis.
@GRAND Collaboration, 2023
- grand.basis.pipeline.logger = <Logger grand.basis.pipeline (WARNING)>
Final usage: from grand.io.pipeline import Pipeline
pipeline = Pipeline() pipeline.Add(“reader”,f_input=”f.root”) pipeline.Add(“coreas2root”, …) pipeline.Add(“efield2voltage”, …..) pipeline.Add(“some_reco”, ….) pipeline.Add(“something_else”, …) pipeline.Add(“writer”, f_output=”out.root”)