Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev_sim2root' into dev_update_tr…
Browse files Browse the repository at this point in the history
…ace_ci32
  • Loading branch information
luckyjim committed Mar 1, 2024
2 parents 3f49a78 + b24373a commit 8b62454
Show file tree
Hide file tree
Showing 11 changed files with 1,084 additions and 16 deletions.
2 changes: 2 additions & 0 deletions grand/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def grand_add_path_data(s_file):
from grand.sim.detector.antenna_model import tabulated_antenna_model, AntennaModel
from grand.sim.detector.process_ant import AntennaProcessing
from grand.sim.detector.rf_chain import RFChain
from grand.sim.detector.adc import ADC
from grand.sim.noise.galaxy import galactic_noise
from grand.sim.shower.gen_shower import ShowerEvent
from grand.sim.shower.pdg import ParticleCode
Expand All @@ -74,6 +75,7 @@ def grand_add_path_data(s_file):
"efield2voltage", "Efield2Voltage",
"tabulated_antenna_model", "AntennaModel", "AntennaProcessing",
"RFChain",
"adc", "ADC",
"galactic_noise",
"ShowerEvent",
"ParticleCode",
Expand Down
125 changes: 125 additions & 0 deletions grand/sim/detector/adc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
Master module for the ADC in GRAND
"""
import numpy as np
import logging

logger = logging.getLogger(__name__)

class ADC:
'''
Class that represents the analog-to-digital converter (ADC) of GRAND.
The ADC digitizes an analog voltage that has been processed through the entire RF chain.
For GRAND, the ADC has:
- a sampling rate of 500 MHz
- 14 bits centered around 0 V <-> 0 ADC counts, with 13 positive and 13 negative bits
- a saturation at an input voltage of +/- 0.9 V
'''

def __init__(self):
self.sampling_rate = 500 # [MHz]
self.max_bit_value = 8192 # 14 bit ADC; 2 x 2^13 bits for negative and positive ADC values
self.max_voltage = 9e5 # [µV]; saturation voltage of ADC (absolute value)


def _digitize(self,
voltage_trace):
'''
Performs the digitization of voltage traces at the ADC input:
- converts voltage to ADC counts
- quantizes the values
Arguments
---------
`voltage_trace`
type : np.ndarray[float]
units : µV
description : Array of voltage traces at the ADC level, with shape (N_du,3,N_samples)
Returns
-------
`adc_trace`
type : np.ndarray[int]
units : ADC counts (least significant bits)
description : The digitized array of ADC traces, with shape (N_du,3,N_samples)
'''

# Convert voltage to ADC
adc_trace = voltage_trace * self.max_bit_value / self.max_voltage

# Quantize the trace
adc_trace = np.trunc(adc_trace).astype(int)

return adc_trace


def _saturate(self,
adc_trace):
'''
Simulates the saturation of the ADC
Arguments
---------
`adc_trace`
type : np.ndarray[int]
units : ADC counts (least significant bits)
description : Array of ADC traces, with shape (N_du,3,N_samples)
Returns
-------
`saturated_adc_trace`
type : np.ndarray[int]
units : ADC counts (least significant bits)
description : Array of saturated ADC traces, with shape (N_du,3,N_samples)
'''

saturated_adc_trace = np.where(np.abs(adc_trace)<self.max_bit_value,
adc_trace,
np.sign(adc_trace)*self.max_bit_value)

return saturated_adc_trace


def process(self,
voltage_trace,
noise_trace=None):
'''
Processes an analog voltage trace to a digital ADC trace,
with an option to add measured noise
Arguments
---------
`voltage_trace`
type : np.ndarray[float]
units : µV
description : Array of voltage traces at the ADC level, with shape (N_du,3,N_samples)
`noise_trace` (optional)
type : np.ndarray[int]
units : ADC counts (least significant bits)
description : Array of measured noise traces, with shape (N_du,3,N_samples)
Returns
-------
`adc_trace`
type : np.ndarray[int]
units : ADC counts (least significant bits)
description : Array of ADC traces with shape (N_du,3,N_samples)
'''

assert isinstance(voltage_trace,np.ndarray)

adc_trace = self._digitize(voltage_trace)

# Add measured noise to the trace if requested
if noise_trace is not None:
assert isinstance(noise_trace,np.ndarray)
assert noise_trace.shape == adc_trace.shape
assert noise_trace.dtype == adc_trace.dtype
adc_trace += noise_trace
logger.info('Noise added to ADC trace')

# Make sure the saturation occurs AFTER adding noise
adc_trace = self._saturate(adc_trace)

return adc_trace
Loading

0 comments on commit 8b62454

Please sign in to comment.