Skip to content

Commit

Permalink
First step at including the ADC in the detector simulation chain
Browse files Browse the repository at this point in the history
  • Loading branch information
Pablo Correa committed Feb 21, 2024
1 parent cec0dcc commit 38050fe
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 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
122 changes: 122 additions & 0 deletions grand/sim/detector/adc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
Master module for the ADC in GRAND
"""
import numpy as np


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 voltage 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

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

return adc_trace
38 changes: 38 additions & 0 deletions scripts/convert_voltage2adc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#! /usr/bin/env python3

import numpy as np
np.set_printoptions(threshold = 3000)
from grand import ADC
import grand.dataio.root_trees as rt

'''
This will be an equivalent file to `convert_efield2voltage.py`. For now it's just a sandbox to test the ADC module.
Goal of this script:
- read voltage simulation file, containing a TVoltage tree with voltage traces processed through the RF chain
- convert analog voltage traces to digital ADC traces
- include an option to add measured noise to the ADC traces
- save the (noisy) ADC traces in a TADC tree
'''

f_input = '../sim2root/Common/sim_Xiaodushan_20221026_180000_RUN0_CD_DC2Alpha_0000/voltage_-1_L0_0000_with-rf_no-noise.root'
#f_input = '/sps/grand/tueros/DC2Alpha/GP300_Xi_Sib_Proton_1.53_82.1_101.7_8550/tvoltage_8550-8550_L0_0000_with-rf_no-noise.root'

df = rt.DataFile(f_input)
tvoltage = df.tvoltage
tvoltage.get_entry(0)

voltage_trace = np.array(tvoltage.trace)

adc = ADC()

adc_trace = adc.process(voltage_trace)

adc_trace_noise = adc.process(voltage_trace,noise_trace=adc_trace)
# adc_trace = adc.digitize(voltage_trace)
# print(adc_trace[10][0])
# adc_trace = adc.saturate(adc_trace)
# print(adc_trace[10][0])


print('done')

0 comments on commit 38050fe

Please sign in to comment.