ZH: move airshower beacon simulation to top folder

This commit is contained in:
Eric-Teunis de Boone 2023-03-27 17:02:18 +02:00
parent 7a7647f231
commit dbb0352229
37 changed files with 2 additions and 2 deletions

View file

@ -1,3 +0,0 @@
config.mk
figures
data

View file

@ -1,82 +0,0 @@
.PHONY: all
FIG_DIR ?= ./figures
-include config.mk
MAX_K ?= 3
CLK_DEV ?= 20
TRACE_N ?= 4096
TRACE_PRE ?= 500
NOISE_SIGMA ?= 1e4
BEAC_AMP ?= 1e3 0 0
BEAC_F ?= 51.53e-3
BEAC_DECAY ?= 0
PB_LOW ?= 0.03
PB_HIGH ?= 0.08
REF_ANTS ?=
N_MASK ?= 500
DATA_DIR ?= ./data
INPUT_DIR ?= ./ZH_airshower/
SEED ?= 12345
all: beacon clocks phases findks vary-fixes reconstruct
beacon: generate-beacon signal-to-noise
phases: beacon-phase clock-phase baseline-phase antenna-phase
######
generate-beacon:
./aa_generate_beacon.py -N ${TRACE_N} -P ${TRACE_PRE} -n ${NOISE_SIGMA} -a ${BEAC_AMP} -f ${BEAC_F} -l ${PB_LOW} -u ${PB_HIGH} -d ${BEAC_DECAY} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR} | tee ${FIG_DIR}/aa.log
./ab_modify_clocks.py 0 --no-save-clocks --data-dir ${DATA_DIR} | tee ${FIG_DIR}/ab.log
signal-to-noise:
./ac_show_signal_to_noise.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
./view_beaconed_antenna.py 72 -p x -p y -p z -p n -p b --ft --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
#
new-clocks:
./ab_modify_clocks.py ${CLK_DEV} --seed ${SEED} --gaussian --data-dir ${DATA_DIR}
clocks:
./ab_modify_clocks.py ${CLK_DEV} --read-clocks-file --seed ${SEED} --gaussian --data-dir ${DATA_DIR}
reset-clocks:
./ab_modify_clocks.py 0 --data-dir ${DATA_DIR}
#
beacon-phase:
./ba_measure_beacon_phase.py --N-mask ${N_MASK} --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
clock-phase:
./bb_measure_clock_phase.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
baseline-phase:
./bc_baseline_phase_deltas.py ${REF_ANTS} --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
antenna-phase:
./bd_antenna_phase_deltas.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
#
findks:
./ca_period_from_shower.py --input-fname ${INPUT_DIR} --max-k ${MAX_K} --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} -l ${PB_LOW} -u ${PB_HIGH}
./cb_report_measured_antenna_time_offsets.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
#
vary-fixes:
./dc_grid_power_time_fixes.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR}
#
reconstruct:
./da_reconstruction.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR}
./db_longitudinal_figure.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
dist-clean:
rm -vf ${DATA_DIR}/
rm -vf ${FIG_DIR}/

View file

@ -1,33 +0,0 @@
# Airshower + Beacon simulation
Simulate receiving an airshower and beacon in an array.
Using the beacon, the clocks can be calibrated.
The ZHaires simulated airshower is stored in [./ZH_airshower](./ZH_airshower).
The produced files can be read using [./earsim](./earsim).
All quantities are stored in HDF5 files with the airshower data.
Steps:
1. Setup
1. Beacon ([./aa_generate_beacon.py])
1. Define tx position
2. Read in antennas
3. Sample beacon at each antenna
4. Add to relevant polarisation
5. Save antenna traces
2. Timeoffset ([./ab_modify_clocks.py])
1. Generate timeoffsets for each antenna
2. Modify time samples
2. Beacon analysis
1. Find beacon frequency and phase in antenna traces ([./ba_measure_beacon_phase.py])
2. Remove phase due to distances
3. Analyse the sigma between antenna pairs
4. Remove 0j part and take the mean of (:,j)
5. Assign mean as clock shift phase of antenna
3. Find k\*2\\pi phase offsets (periods) ([./ca_periods_from_showers.py])
4. Rewrite clocks and do interferometric reconstruction ([./da_reconstruction.py])

View file

@ -1,2 +0,0 @@
*
!.gitignore

View file

@ -1,440 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=marker ts=4
"""
Add a beacon measurement on top of the
simulated airshower.
"""
import numpy as np
import json
import h5py
import os.path as path
from copy import deepcopy
from earsim import REvent, Antenna, block_filter
import lib
# {{{ vim marker
tx_fname = 'tx.json'
antennas_fname = 'antennas.hdf5'
snr_fname = 'snr.json'
c_light = lib.c_light
def read_antenna_clock_repair_offsets(antennas, mode='all', freq_name=None):
valid_modes = ['orig', 'ks', 'phases', 'all']
time_offsets = []
for i, ant in enumerate(antennas):
_clock_delta = 0
# original timing
if mode == 'orig':
_clock_delta = -1*ant.attrs['clock_offset']
# phase
if mode in ['all', 'phases']:
clock_phase = ant.beacon_info[freq_name]['clock_phase_mean']
f_beacon = ant.beacon_info[freq_name]['freq']
clock_phase_time = clock_phase/(2*np.pi*f_beacon)
_clock_delta += clock_phase_time
# ks
if mode in ['all', 'ks']:
best_k_time = ant.beacon_info[freq_name]['best_k_time']
_clock_delta += best_k_time
time_offsets.append(_clock_delta)
return time_offsets
def write_snr_file(fname, snrs):
with open(fname, 'w') as fp:
return json.dump(
{'mean': np.mean(snrs), 'std': np.std(snrs), 'values': snrs},
fp
)
def read_snr_file(fname):
with open(fname, 'r') as fp:
return json.load(fp)
def write_tx_file(fname, tx, f_beacon, **kwargs):
with open(fname, 'w') as fp:
return json.dump(
{
**kwargs,
**dict(
f_beacon=f_beacon,
tx=dict(
x=tx.x,
y=tx.y,
z=tx.z,
name=tx.name
)
)
},
fp
)
def read_tx_file(fname):
with open(fname, 'r') as fp:
data = json.load(fp)
f_beacon = data['f_beacon']
tx = Antenna(**data['tx'])
del data['f_beacon']
del data['tx']
return tx, f_beacon, data
def read_beacon_hdf5(fname, **h5ant_kwargs):
with h5py.File(fname, 'r') as h5:
tx = Antenna_from_h5ant(h5['tx'], traces_key=None)
f_beacon = tx.attrs['f_beacon']
antennas = []
for k, h5ant in h5['antennas'].items():
ant = Antenna_from_h5ant(h5ant, **h5ant_kwargs)
antennas.append(ant)
return f_beacon, tx, antennas
def Antenna_from_h5ant(h5ant, traces_key='filtered_traces', raise_exception=True, read_AxB=True, read_beacon_info=True):
mydict = { k:h5ant.attrs.get(k) for k in ['x', 'y', 'z', 'name'] }
ant = Antenna(**mydict)
if h5ant.attrs:
ant.attrs = {**h5ant.attrs}
# Traces
if traces_key is None:
pass
elif traces_key not in h5ant:
if raise_exception:
raise ValueError("Traces_key not in file")
else:
ant.t = deepcopy(h5ant[traces_key][0])
ant.Ex = deepcopy(h5ant[traces_key][1])
ant.Ey = deepcopy(h5ant[traces_key][2])
ant.Ez = deepcopy(h5ant[traces_key][3])
if len(h5ant[traces_key]) > 4:
ant.beacon = deepcopy(h5ant[traces_key][4])
if len(h5ant[traces_key]) > 5:
ant.noise = deepcopy(h5ant[traces_key][5])
# E_AxB
if read_AxB and 'E_AxB' in h5ant:
ant.t_AxB = deepcopy(h5ant['E_AxB'][0])
ant.E_AxB = deepcopy(h5ant['E_AxB'][1])
# Beacons
if read_beacon_info and 'beacon_info' in h5ant:
h5beacon = h5ant['beacon_info']
beacon_info = {}
for name in h5beacon.keys():
beacon_info[name] = dict(h5beacon[name].attrs)
ant.beacon_info = beacon_info
return ant
def init_antenna_hdf5(fname, tx = None, f_beacon = None):
with h5py.File(fname, 'w') as fp:
if tx is not None or f_beacon is not None:
tx_group = fp.create_group('tx')
tx_attrs = tx_group.attrs
if f_beacon is not None:
tx_attrs['f_beacon'] = f_beacon
if tx is not None:
tx_attrs['x'] = tx.x
tx_attrs['y'] = tx.y
tx_attrs['z'] = tx.z
tx_attrs['name'] = tx.name
return fname
def append_antenna_hdf5(fname, antenna, columns = [], name='traces', prepend_time=True, overwrite=True, attrs_dict={}):
if not overwrite:
raise NotImplementedError
with h5py.File(fname, 'a') as fp:
if 'antennas' in fp.keys():
if not overwrite:
raise NotImplementedError
group = fp['antennas']
else:
group = fp.create_group('antennas')
if antenna.name in group:
if not overwrite:
raise NotImplementedError
h5ant = group[antenna.name]
else:
h5ant = group.create_group(antenna.name)
h5ant_attrs = h5ant.attrs
h5ant_attrs['x'] = antenna.x
h5ant_attrs['y'] = antenna.y
h5ant_attrs['z'] = antenna.z
h5ant_attrs['name'] = antenna.name
for k,v in attrs_dict.items():
h5ant_attrs[k] = v
if name in h5ant:
if not overwrite:
raise NotImplementedError
del h5ant[name]
dset = h5ant.create_dataset(name, (len(columns) + 1*prepend_time, len(columns[0])), dtype='f')
if prepend_time:
dset[0] = antenna.t
for i, col in enumerate(columns, 1*prepend_time):
dset[i] = col
def read_baseline_time_diffs_hdf5(fname):
"""
Read Baseline Time Diff information from HDF5 storage.
"""
with h5py.File(fname, 'r') as fp:
group_name = 'baseline_time_diffs'
base_dset_name = 'baselines'
dset_name = 'time_diffs'
group = fp[group_name]
names = group[base_dset_name][:].astype(str)
dset = group[dset_name]
time_diffs = dset[:,0]
f_beacon = dset[:,1]
clock_phase_diffs = dset[:,2]
k_periods = dset[:,3]
return names, time_diffs, f_beacon, clock_phase_diffs, k_periods
def write_baseline_time_diffs_hdf5(fname, baselines, clock_phase_diffs, k_periods, f_beacon, time_diffs=None, overwrite=True):
"""
Write a combination of baselines, phase_diff, k_period and f_beacon to file.
Note that f_beacon is allowed to broadcast, but the others are not.
"""
if not hasattr(baselines[0], '__len__'):
# this is a single baseline
N_baselines = 1
baselines = [baselines]
clock_phase_diffs = [clock_phase_diffs]
k_periods = [k_periods]
f_beacon = np.array([f_beacon])
else:
N_baselines = len(baselines)
# Expand the f_beacon list
if not hasattr(f_beacon, '__len__'):
f_beacon = np.array([f_beacon]*N_baselines)
if time_diffs is None:
time_diffs = k_periods/f_beacon + clock_phase_diffs/(2*np.pi*f_beacon)
assert len(baselines) == len(clock_phase_diffs) == len(k_periods) == len(f_beacon)
with h5py.File(fname, 'a') as fp:
group_name = 'baseline_time_diffs'
base_dset_name = 'baselines'
dset_name = 'time_diffs'
group = fp.require_group(group_name)
if base_dset_name in group:
if not overwrite:
raise NotImplementedError
del group[base_dset_name]
if dset_name in group:
if not overwrite:
raise NotImplementedError
del group[dset_name]
# save baselines list
basenames = np.array([ [b[0].name, b[1].name] for b in baselines ], dtype='S')
base_dset = group.create_dataset(base_dset_name, data=basenames)
data = np.vstack( (time_diffs, f_beacon, clock_phase_diffs, k_periods) ).T
dset = group.create_dataset(dset_name, data=data)
# }}} vim marker
if __name__ == "__main__":
from os import path
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-n', '--noise-sigma', type=float, default=1e3, help='in [muV/m] (Default: %(default)d)')
parser.add_argument('-f', '--beacon-frequency', type=float, default=51.53e-3, help='The beacon\'s frequency [GHz] (Default: %(default)d)')
# Beacon Properties
parser.add_argument('-a', '--beacon-amplitudes', type=float, nargs=3, default=[1e3, 0, 0], help='in [muV/m] (Default: %(default)s)')
parser.add_argument('-d', '--beacon-rsq-decay', type=bool, default=True, help='Let the beacon amplitude fall of with distance. Uses Beacon amplitudes at 0,0,0 (Default: %(default)d)')
# Bandpass
parser.add_argument('-p', '--use-passband', type=bool, default=True, help='(Default: %(default)d)')
parser.add_argument('-l', '--passband-low', type=float, default=30e-3, help='Lower frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)g)')
parser.add_argument('-u', '--passband-high', type=float, default=80e-3, help='Upper frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)g)')
# Trace length modification
parser.add_argument('-N', '--new-trace-length', type=float, help='resize airshower trace (Default: %(default)d)', default=1e4)
parser.add_argument('-P', '--pre-trace-length', type=float, help='amount of trace to prepend the airshower when resizing (Default: %(default)d)', default=2e3)
# Input directory
parser.add_argument('--input-fname', type=str, default=None, help='Path to mysim.sry, either directory or path. If empty it takes DATA_DIR and appends mysim.sry. (Default: %(default)s)')
parser.add_argument('--data-dir', type=str, default="./ZH_airshower", help='Path to Data Directory. (Default: %(default)s)')
args = parser.parse_args()
if not args.input_fname:
args.input_fname = args.data_dir
if path.isdir(args.input_fname):
args.input_fname = path.join(args.input_fname, "mysim.sry")
##
## End of ArgumentParsing
##
rng = np.random.default_rng()
# Noise properties
noise_sigma = args.noise_sigma # mu V/m set to None to ignore
unique_noise_realisations = True # a new noise realisation per antenna vs. single noise realisation shared across antennas
# Beacon properties
beacon_amplitudes = np.array(args.beacon_amplitudes) # mu V/m
beacon_radiate_rsq = args.beacon_rsq_decay # beacon_amplitude is repaired for distance to 0,0,0
# Beacon properties
f_beacon = args.beacon_frequency # GHz
# Transmitter
remake_tx = True
tx = Antenna(x=0,y=0,z=0,name='tx') # m
if True:
# Move tx out a long way
tx.x, tx.y = -75e3, 75e3 # m
elif False:
# Move it to 0,0,0 (among the antennas)
tx.x, tx.y = 0, 0 #m
# modify beacon power to be beacon_amplitude at 0,0,0
if beacon_radiate_rsq:
dist = lib.distance(tx, Antenna(x=0, y=0, z=0))
ampl = max(1, dist**2)
beacon_amplitudes *= ampl
# Bandpass for E field blockfilter
low_bp = args.passband_low if args.passband_low >= 0 else np.inf # GHz
high_bp = args.passband_high if args.passband_high >= 0 else np.inf # GHz
# Enable/Disable block_filter
if not args.use_passband:
block_filter = lambda x, dt, low, high: x
####
fname_dir = args.data_dir
tx_fname = path.join(fname_dir, tx_fname)
antennas_fname = path.join(fname_dir, antennas_fname)
# read/write tx properties
if not path.isfile(tx_fname) or remake_tx:
write_tx_file(tx_fname, tx, f_beacon, amplitudes=beacon_amplitudes.tolist(), radiate_rsq=beacon_radiate_rsq)
else:
tx, f_beacon, _ = read_tx_file(tx_fname)
print("Beacon amplitude at tx [muV/m]:", beacon_amplitudes)
print("Beacon amplitude at 0,0,0 [muV/m]:", beacon_amplitudes/ampl)
print("Tx location:", [tx.x, tx.y, tx.z])
print("Noise sigma [muV/m]:", noise_sigma)
# read in antennas
ev = REvent(args.input_fname)
N_antennas = len(ev.antennas)
# initialize hdf5 file
init_antenna_hdf5(antennas_fname, tx, f_beacon)
# make beacon per antenna
noise_realisation = np.array([0])
for i, antenna in enumerate(ev.antennas):
#TODO: allow to change the samplerate (2, 4, 8 ns)
if i%10 == 0:
print(f"Beaconed antenna {i} out of", len(ev.antennas))
if args.new_trace_length: # modify trace lengths
N_samples = len(antenna.t)
new_N = int(args.new_trace_length)
pre_N = int(args.pre_trace_length)
after_N = new_N - pre_N
dt = antenna.t[1] - antenna.t[0]
new_t = np.arange(-pre_N, after_N)*dt + antenna.t[0]
antenna.t = new_t
# TODO:trace extrapolation?
antenna.Ex = np.pad(antenna.Ex, (pre_N, after_N-N_samples), mode='constant', constant_values=0)
antenna.Ey = np.pad(antenna.Ey, (pre_N, after_N-N_samples), mode='constant', constant_values=0)
antenna.Ez = np.pad(antenna.Ez, (pre_N, after_N-N_samples), mode='constant', constant_values=0)
if i%10 == 0:
print(f"Modified trace lengths by {pre_N},{after_N-N_samples}")
beacon = 1e-6 * lib.beacon_from(tx, antenna, f_beacon, antenna.t, c_light=c_light, radiate_rsq=beacon_radiate_rsq) # mu V/m
# noise realisation
if unique_noise_realisations or (noise_realisation == 0).all(): # either create one for every antenna, or generate a single one
print("Noise realisation!")
noise_realisation = 1e-6 * rng.normal(0, noise_sigma or 0, size=len(antenna.t)) # mu V/m
# Collect all data to be saved (with the first 3 values the E fields)
traces = np.array([antenna.Ex, antenna.Ey, antenna.Ez, beacon, noise_realisation])
# add beacon and noise to relevant polarisations
for j, amp in enumerate(beacon_amplitudes):
traces[j] = traces[j] + amp*beacon + noise_realisation
append_antenna_hdf5( antennas_fname, antenna, traces, name='prefiltered_traces', prepend_time=True)
# .. and apply block_filter to every trace
dt = antenna.t[1] - antenna.t[0]
for j in range(len(traces)):
traces[j] = block_filter(traces[j], dt, low_bp, high_bp)
append_antenna_hdf5( antennas_fname, antenna, traces, name='filtered_traces', prepend_time=True)
# Save filtered E field in E_AxB
E_AxB = [np.dot(ev.uAxB,[ex,ey,ez]) for ex,ey,ez in zip(traces[0], traces[1], traces[2])]
t_AxB = antenna.t
append_antenna_hdf5( antennas_fname, antenna, [t_AxB, E_AxB], name='E_AxB', prepend_time=False)
print("Antenna HDF5 file written as " + str(antennas_fname))

View file

@ -1,122 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Add a uniformly sampled time offset
to the clock of each antenna.
"""
import numpy as np
import json
import h5py
import aa_generate_beacon as beacon
clocks_fname = 'clocks.csv'
if __name__ == "__main__":
from os import path
import sys
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('max_clock_offset', nargs='?', type=float, default=25, help='(Default: %(default)d)')
parser.add_argument('-s', '--seed', type=int, nargs='?', default=None, help='Fix seed if supplied.')
parser.add_argument('--uniform', action='store_const', const='uniform', dest='dist_type')
parser.add_argument('--gaussian', action='store_const', const='gauss', dest='dist_type')
parser.add_argument('-r','--read-clocks-file', action='store_true', dest='read_clocks_file')
parser.add_argument('--no-save-clocks', action='store_false', dest='save_clocks')
parser.set_defaults(dist_type='gauss')
parser.add_argument('--data-dir', type=str, default="./data", help='Path to Data Directory. (Default: %(default)s)')
args = parser.parse_args()
max_clock_offset = args.max_clock_offset # ns
remake_clock_offsets = True
seed = args.seed
rng = np.random.default_rng(seed)
####
fname_dir = args.data_dir
clocks_fname = path.join(fname_dir, clocks_fname)
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
if path.isfile(clocks_fname) and not remake_clock_offsets:
print("Clock offsets previously generated")
sys.exit()
if not path.isfile(antennas_fname):
print("Antenna file cannot be found, did you try generating a beacon?")
sys.exit(1)
# read in antennas
with h5py.File(antennas_fname, 'a') as fp:
if 'antennas' not in fp.keys():
print("Antenna file corrupted? no antennas")
sys.exit(1)
group = fp['antennas']
N_antennas = len(group.keys())
if args.read_clocks_file and path.isfile(clocks_fname): # read clock deviations from file
print(f"Reading clocks from {clocks_fname}.")
clock_offsets = np.loadtxt(clocks_fname)
elif True: # random clock deviations
print(f"Modifying clocks upto {max_clock_offset}ns.")
clock_offsets = np.zeros( N_antennas )
if args.dist_type == 'uniform': # uniform
print("Uniform distribution")
clock_offsets = max_clock_offset * (2*rng.uniform(size=N_antennas) - 1)
else: # normal
print("Gaussian distribution")
clock_offsets = max_clock_offset * rng.normal(0, 1, size=N_antennas)
else: # Hardcoded offsets
print("Hardcoded offsets")
f_beacon = 51.53e-3 # GHz
clock_offsets = np.zeros( N_antennas )
if False:
clock_offsets[59] = np.pi/2 / (2*np.pi*f_beacon)
elif True:
clock_offsets += 1/8 * 1/f_beacon
print(clock_offsets)
# modify time values of each antenna
trace_key = 'filtered_traces'
for i, name in enumerate(group.keys()):
h5ant = group[name]
clk_offset = clock_offsets[i]
if trace_key not in h5ant.keys():
print(f"Antenna file corrupted? no 'traces' in {name}")
sys.exit(1)
h5ant_attrs = h5ant.attrs
if 'clock_offset' in h5ant_attrs:
tmp_offset = h5ant_attrs['clock_offset']
if remake_clock_offsets:
h5ant[trace_key][0, :] -= tmp_offset
h5ant['E_AxB'][0, :] -= tmp_offset
else:
clock_offsets[i] = tmp_offset
continue
h5ant_attrs['clock_offset'] = clk_offset
h5ant[trace_key][0, :] += clk_offset
h5ant['E_AxB'][0, :] += clk_offset
# save to simple csv
if args.save_clocks:
np.savetxt(clocks_fname, clock_offsets)
print("Antenna clocks modified in " + str(antennas_fname))

View file

@ -1,138 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Show Signal to noise for the original simulation signal,
the beacon signal and the combined signal for each antenna
"""
import numpy as np
import h5py
import matplotlib.pyplot as plt
import numpy as np
from earsim import REvent, block_filter
import aa_generate_beacon as beacon
import lib
if __name__ == "__main__":
from os import path
import sys
import matplotlib
import os
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
# Bandpass
parser.add_argument('-p', '--use-passband', type=bool, default=True, help='(Default: %(default)d)')
parser.add_argument('-l', '--passband-low', type=float, default=30e-3, help='Lower frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)d)')
parser.add_argument('-u', '--passband-high', type=float, default=80e-3, help='Upper frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)d)')
args = parser.parse_args()
figsize = (12,8)
fig_dir = args.fig_dir
show_plots = args.show_plots
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
tx_fname = path.join(fname_dir, beacon.tx_fname)
snr_fname = path.join(fname_dir, beacon.snr_fname)
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
# Read in antennas from file
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname, traces_key='filtered_traces')
_, __, txdata = beacon.read_tx_file(tx_fname)
# general properties
dt = antennas[0].t[1] - antennas[0].t[0] # ns
beacon_pb = lib.passband(f_beacon, None) # GHz
beacon_amp = np.max(txdata['amplitudes'])# mu V/m
# General Bandpass
low_bp = args.passband_low if args.passband_low >= 0 else np.inf # GHz
high_bp = args.passband_high if args.passband_high >= 0 else np.inf # GHz
pb = lib.passband(low_bp, high_bp) # GHz
noise_pb = pb
if args.use_passband: # Apply filter to raw beacon/noise to compare with Filtered Traces
myfilter = lambda x: block_filter(x, dt, pb[0], pb[1])
else: # Compare raw beacon/noise with Filtered Traces
myfilter = lambda x: x
##
## Debug plot of Beacon vs Noise SNR
##
if True:
ant = antennas[0]
fig, ax = plt.subplots(figsize=figsize)
_ = lib.signal_to_noise(myfilter(beacon_amp*ant.beacon), myfilter(ant.noise), samplerate=1/dt, signal_band=beacon_pb, noise_band=noise_pb, debug_ax=ax)
ax.set_title("Spectra and passband")
ax.set_xlabel("Frequency")
ax.set_ylabel("Amplitude")
low_x, high_x = min(beacon_pb[0], noise_pb[0]), max(beacon_pb[1] or 0, noise_pb[1])
ax.set_xlim(low_x, high_x)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".debug_plot.pdf"))
##
## Beacon vs Noise SNR
##
if True:
N_samples = len(antennas[0].beacon)
beacon_snrs = [ lib.signal_to_noise(myfilter(beacon_amp*ant.beacon), myfilter(ant.noise), samplerate=1/dt, signal_band=beacon_pb, noise_band=noise_pb) for i, ant in enumerate(antennas) ]
# write mean and std to file
beacon.write_snr_file(snr_fname, beacon_snrs)
fig, ax = plt.subplots(figsize=figsize)
ax.set_title(f"Maximum Beacon/Noise SNR (N_samples:{N_samples:.1e})")
ax.set_xlabel("Antenna no.")
ax.set_ylabel("SNR")
ax.plot([ int(ant.name) for ant in antennas], beacon_snrs, 'o', ls='none')
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".beacon_vs_noise_snr.pdf"))
##
## Beacon vs Total SNR
##
if True:
beacon_snrs = [ lib.signal_to_noise(myfilter(beacon_amp*ant.beacon), ant.E_AxB, samplerate=1/dt, signal_band=beacon_pb, noise_band=pb) for ant in antennas ]
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Maximum Beacon/Total SNR")
ax.set_xlabel("Antenna no.")
ax.set_ylabel("SNR")
ax.plot([ int(ant.name) for ant in antennas], beacon_snrs, 'o', ls='none')
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".beacon_vs_total_snr.pdf"))
##
## Airshower signal vs Noise SNR
##
if True:
shower_snrs = [ lib.signal_to_noise(ant.E_AxB, myfilter(ant.noise), samplerate=1/dt, signal_band=pb, noise_band=pb) for ant in antennas ]
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Total (Signal+Beacon+Noise)/Noise SNR")
ax.set_xlabel("Antenna no.")
ax.set_ylabel("SNR")
ax.plot([ int(ant.name) for ant in antennas], shower_snrs, 'o', ls='none')
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".total_snr.pdf"))
if show_plots:
plt.show()

View file

@ -1,327 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Find beacon phases in antenna traces
And save these to a file
"""
import h5py
import matplotlib.pyplot as plt
import numpy as np
import aa_generate_beacon as beacon
import lib
from lib import figlib
if __name__ == "__main__":
from os import path
import sys
import matplotlib
import os
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
group1 = parser.add_mutually_exclusive_group()
group1.add_argument('--AxB', dest='use_AxB_trace', action='store_true', help='Only use AxB trace, if both AxB and beacon are not used, we use the antenna polarisations.')
group1.add_argument('--beacon', dest='use_beacon_trace', action='store_true', help='Only use the beacon trace')
parser.add_argument('--N-mask', type=float, default=500, help='Mask N_MASK samples around the absolute maximum of the trace. (Default: %(default)d)')
args = parser.parse_args()
f_beacon_band = (49e-3,55e-3) #GHz
allow_frequency_fitting = False
read_frequency_from_file = True
N_mask = int(args.N_mask)
use_only_AxB_trace = args.use_AxB_trace
use_only_beacon_trace = args.use_beacon_trace # only applicable if AxB = False
show_plots = args.show_plots
figsize = (12,8)
print("use_only_AxB_trace:", use_only_AxB_trace, "use_only_beacon_trace:", use_only_beacon_trace)
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
snr_fname = path.join(fname_dir, beacon.snr_fname)
fig_dir = args.fig_dir # set None to disable saving
if not path.isfile(antennas_fname):
print("Antenna file cannot be found, did you try generating a beacon?")
sys.exit(1)
# read in antennas
with h5py.File(antennas_fname, 'a') as fp:
if 'antennas' not in fp.keys():
print("Antenna file corrupted? no antennas")
sys.exit(1)
group = fp['antennas']
f_beacon = None
if read_frequency_from_file and 'tx' in fp:
tx = fp['tx']
if 'f_beacon' in tx.attrs:
f_beacon = tx.attrs['f_beacon']
else:
print("No frequency found in file.")
sys.exit(2)
f_beacon_estimate_band = 0.01*f_beacon
elif allow_frequency_fitting:
f_beacon_estimate_band = (f_beacon_band[1] - f_beacon_band[0])/2
f_beacon = f_beacon_band[1] - f_beacon_estimate_band
else:
print("Not allowed to fit frequency and no tx group found in file.")
sys.exit(2)
N_antennas = len(group.keys())
# just for funzies
found_data = np.zeros((N_antennas, 3)) # freq, phase, amp
noise_data = np.zeros((N_antennas, 2)) # phase, amp
# Determine frequency and phase
for i, name in enumerate(group.keys()):
h5ant = group[name]
# use E_AxB only instead of polarisations
if use_only_AxB_trace:
traces_key = 'E_AxB'
if traces_key not in h5ant.keys():
print(f"Antenna does not have '{traces_key}' in {name}")
sys.exit(1)
traces = h5ant[traces_key]
t_trace = traces[0]
test_traces = [ traces[1] ]
orients = ['E_AxB']
# Only beacon
elif use_only_beacon_trace:
traces_key = 'filtered_traces'
if traces_key not in h5ant.keys():
print(f"Antenna file corrupted? no '{traces_key}' in {name}")
sys.exit(1)
traces = h5ant[traces_key]
t_trace = traces[0]
test_traces = [traces[4]]
orients = ['B']
# use separate polarisations
else:
traces_key = 'filtered_traces'
if traces_key not in h5ant.keys():
print(f"Antenna file corrupted? no '{traces_key}' in {name}")
sys.exit(1)
traces = h5ant[traces_key]
t_trace = traces[0]
test_traces = [traces[i] for i in range(1,4)]
orients = ['Ex', 'Ey', 'Ez']
# Really only select the first component
if True:
test_traces = [test_traces[0]]
orients = [orients[0]]
# TODO: refine masking
# use beacon but remove where E_AxB-Beacon != 0
# Uses the first traces as reference
t_mask = 0
if N_mask and orients[0] != 'B':
N_pre, N_post = N_mask//2, N_mask//2
max_idx = np.argmax(test_traces[0])
low_idx = max(0, max_idx-N_pre)
high_idx = min(len(t_trace), max_idx+N_post)
t_mask = np.ones(len(t_trace), dtype=bool)
t_mask[low_idx:high_idx] = False
t_trace = t_trace[t_mask]
for j, t in enumerate(test_traces):
test_traces[j] = t[t_mask]
orients[j] = orients[j] + ' masked'
# Do Fourier Transforms
# to find phases and amplitudes
if True:
freqs, beacon_phases, amps = lib.find_beacon_in_traces(
test_traces, t_trace,
f_beacon_estimate=f_beacon,
frequency_fit=allow_frequency_fitting,
f_beacon_estimate_band=f_beacon_estimate_band
)
else:
# Testing
freqs = [f_beacon]
t0 = h5ant.attrs['t0']
beacon_phases = [ 2*np.pi*t0*f_beacon ]
amps = [ 3e-7 ]
# Also try to find the phase from the noise trace if available
if len(h5ant[traces_key]) > 4:
noise_trace = h5ant[traces_key][5]
if np.any(t_mask): # Mask the same area
noise_trace = noise_trace[t_mask]
real, imag = lib.direct_fourier_transform(f_beacon, t_trace, noise_trace)
noise_phase = np.arctan2(imag, real)
noise_amp = (real**2 + imag**2)**0.5
noise_data[i] = noise_phase, noise_amp
# choose highest amp
idx = np.argmax(amps)
if False and len(beacon_phases) > 1:
#idx = np.argmax(amplitudes, axis=-1)
raise NotImplementedError
frequency = freqs[idx]
beacon_phase = beacon_phases[idx]
amplitude = amps[idx]
orientation = orients[idx]
# Correct for phase by t_trace[0]
corr_phase = lib.phase_mod(2*np.pi*f_beacon*t_trace[0])
if False:
# Subtract phase due to not starting at t=0
# This is already done in beacon_find_traces
beacon_phase = lib.phase_mod(beacon_phase + corr_phase)
# for reporting using plots
found_data[i] = frequency, beacon_phase, amplitude
if (show_plots or fig_dir) and (i == 0 or i == 72):
p2t = lambda phase: phase/(2*np.pi*f_beacon)
fig, ax = plt.subplots(figsize=figsize)
ax.set_title(f"Beacon at antenna {h5ant.attrs['name']}\nF:{frequency:.2e}, P:{beacon_phase:.4f}, A:{amplitude:.1e}")
ax.set_xlabel("t [ns]")
ax.set_ylabel("Amplitude")
if True:
# let the trace start at t=0
t_0 = min(t_trace)
extra_phase = corr_phase
else:
t_0 = 0
extra_phase = -1*corr_phase
for j, trace in enumerate(test_traces):
ax.plot(t_trace - t_0, test_traces[j], marker='.', label='trace '+orients[j])
myt = np.linspace(min(t_trace), max(t_trace), 10*len(t_trace)) - t_0
ax.plot(myt, lib.sine_beacon(frequency, myt, amplitude=amplitude, t0=0, phase=beacon_phase+extra_phase), ls='dotted', label='simulated beacon')
ax.axvline( p2t(lib.phase_mod(-1*(beacon_phase+extra_phase), low=0)), color='r', ls='dashed', label='$t_\\varphi$')
ax.axvline(0,color='grey',alpha=0.5)
ax.axhline(0,color='grey',alpha=0.5)
ax.legend()
if fig_dir:
old_xlims = ax.get_xlim()
ax.set_xlim(min(t_trace)-t_0-10,min(t_trace)-t_0+40)
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".A{h5ant.attrs['name']}.zoomed.pdf"))
ax.set_xlim(*old_xlims)
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".A{h5ant.attrs['name']}.pdf"))
# save to file
h5beacon_info = h5ant.require_group('beacon_info')
# only take n_sig significant digits into account
# for naming in hdf5 file
n_sig = 3
decimal = int(np.floor(np.log10(abs(frequency))))
freq_name = str(np.around(frequency, n_sig-decimal))
# delete previous values
if freq_name in h5beacon_info:
del h5beacon_info[freq_name]
h5beacon_freq_info = h5beacon_info.create_group(freq_name)
h5attrs = h5beacon_freq_info.attrs
h5attrs['freq'] = frequency
h5attrs['beacon_phase'] = beacon_phase
h5attrs['amplitude'] = amplitude
h5attrs['orientation'] = orientation
if noise_phase:
h5attrs['noise_phase'] = noise_phase
h5attrs['noise_amp'] = noise_amp
print("Beacon Phases, Amplitudes and Frequencies written to", antennas_fname)
# show histogram of found frequencies
if show_plots or fig_dir:
snrs = beacon.read_snr_file(snr_fname)
if True or allow_frequency_fitting:
fig, ax = plt.subplots(figsize=figsize)
ax.set_xlabel("Frequency")
ax.set_ylabel("Counts")
ax.axvline(f_beacon, ls='dashed', color='g')
ax.hist(found_data[:,0], bins='sqrt', density=False)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".hist_freq.pdf"))
if True:
fig, _ = figlib.fitted_histogram_figure(found_data[:,2], fit_distr=[], mean_snr=snrs['mean'])
ax = fig.axes[0]
ax.set_xlabel("Amplitude")
ax.set_ylabel("Counts")
ax.hist(found_data[:,2], bins='sqrt', density=False)
ax.legend()
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".hist_amp.pdf"))
if (noise_data[0] != 0).any():
if True:
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Noise Phases")
ax.set_xlabel("Phase")
ax.set_ylabel("#")
ax.hist(noise_data[:,0], bins='sqrt', density=False)
ax.legend()
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".noise.hist_phase.pdf"))
if True:
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Noise Phase vs Amplitude")
ax.set_xlabel("Phase")
ax.set_ylabel("Amplitude [a.u.]")
ax.plot(noise_data[:,0], noise_data[:,1], ls='none', marker='x')
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".noise.phase_vs_amp.pdf"))
if True:
fig, _ = figlib.fitted_histogram_figure(noise_data[:,1], fit_distr=['rice', 'rayleigh'], mean_snr=snrs['mean'])
ax = fig.axes[0]
ax.set_title("Noise Amplitudes")
ax.set_xlabel("Amplitude [a.u.]")
ax.set_ylabel("#")
ax.legend()
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".noise.hist_amp.pdf"))
if show_plots:
plt.show()

View file

@ -1,197 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
import h5py
from itertools import combinations, zip_longest
import matplotlib.pyplot as plt
import numpy as np
import aa_generate_beacon as beacon
import lib
from lib import figlib
if __name__ == "__main__":
from os import path
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
args = parser.parse_args()
figsize = (12,8)
c_light = lib.c_light
show_plots = args.show_plots
remove_absolute_phase_offset_first_antenna = True # takes precedence
remove_absolute_phase_offset_minimum = True
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
snr_fname = path.join(fname_dir, beacon.snr_fname)
fig_dir = args.fig_dir # set None to disable saving
if not path.isfile(antennas_fname):
print("Antenna file cannot be found, did you try generating a beacon?")
sys.exit(1)
# Read in antennas from file
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
# Make sure at least one beacon has been identified
if not hasattr(antennas[0], 'beacon_info') or len(antennas[0].beacon_info) == 0:
print(f"No analysed beacon found for {antennas[0].name}, try running the beacon phase analysis script first.")
sys.exit(1)
#
N_beacon_freqs = len(antennas[0].beacon_info)
for freq_name in antennas[0].beacon_info.keys():
beacon_phases = np.empty( (len(antennas)) )
for i, ant in enumerate(antennas):
beacon_phases[i] = ant.beacon_info[freq_name]['beacon_phase']
f_beacon = antennas[0].beacon_info[freq_name]['freq']
clock_phases = lib.remove_antenna_geometry_phase(tx, antennas, f_beacon, beacon_phases, c_light=c_light)
# Remove the phase from one antenna
# this is a free parameter
# (only required for absolute timing)
if remove_absolute_phase_offset_first_antenna or remove_absolute_phase_offset_minimum:
if remove_absolute_phase_offset_first_antenna: # just take the first phase
minimum_phase = clock_phases[0]
else: # take the minimum
minimum_phase = np.min(clock_phases, axis=-1)
clock_phases -= minimum_phase
clock_phases = lib.phase_mod(clock_phases)
# Save to antennas in file
with h5py.File(antennas_fname, 'a') as fp:
h5group = fp['antennas']
for i, ant in enumerate(antennas):
h5ant = fp['antennas'][ant.name]
h5beacon_freq = h5ant['beacon_info'][freq_name]
h5beacon_freq.attrs['clock_phase'] = clock_phases[i]
# Plot True Phases at their locations
if show_plots or fig_dir:
actual_clock_phases = lib.phase_mod(np.array([ -2*np.pi*a.attrs['clock_offset']*f_beacon for a in antennas ]))
for i in range(2):
plot_residuals = i == 1
spatial_unit='m'
antenna_locs = list(zip(*[(ant.x, ant.y) for ant in antennas]))
scatter_kwargs = {}
scatter_kwargs['cmap'] = 'inferno'
# Measurements
if not plot_residuals:
title='Clock phases'
color_label='$\\varphi(\\sigma_t)$ [rad]'
fname_extra='measured.'
#scatter_kwargs['vmin'] = -np.pi
#scatter_kwargs['vmax'] = +np.pi
# Plot Clock Phases - True Clock Phases at their location
else:
title='Clock phase Residuals'
color_label='$\\Delta\\varphi(\\sigma_t) = \\varphi_{meas} - \\varphi_{true}$ [rad]'
fname_extra='residuals.'
# Modify actual_clock_phases, the same way as clock_phases
# was modified
if remove_absolute_phase_offset_first_antenna or remove_absolute_phase_offset_minimum:
if remove_absolute_phase_offset_first_antenna: # just take the first phase
minimum_phase = actual_clock_phases[0]
else: # take the minimum
minimum_phase = np.min(actual_clock_phases, axis=-1)
actual_clock_phases -= minimum_phase
actual_clock_phases = lib.phase_mod(actual_clock_phases)
clock_phase_residuals = lib.phase_mod(clock_phases - actual_clock_phases)
if not plot_residuals:
loc_c = clock_phases
else:
loc_c = clock_phase_residuals
##
## Geometrical Plot
##
fig, ax = plt.subplots(figsize=figsize)
ax.set_xlabel('x' if spatial_unit is None else 'x [{}]'.format(spatial_unit))
ax.set_ylabel('y' if spatial_unit is None else 'y [{}]'.format(spatial_unit))
fig.suptitle(title+'\nf_beacon= {:2.0f}MHz'.format(f_beacon*1e3))
sc = ax.scatter(*antenna_locs, c=loc_c, **scatter_kwargs)
fig.colorbar(sc, ax=ax, label=color_label)
if False:
for i, ant in enumerate(antennas):
ax.text(ant.x, ant.y, ant.name)
if not True:
ax.plot(tx.x, tx.y, 'X', color='k', markeredgecolor='white')
if fig_dir:
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".geom.{fname_extra}F{freq_name}.pdf"))
##
## Histogram
##
snrs = beacon.read_snr_file(snr_fname)
fig = figlib.phase_comparison_figure(
loc_c,
actual_clock_phases,
plot_residuals=plot_residuals,
f_beacon=f_beacon,
figsize=figsize,
fit_gaussian=plot_residuals,
mean_snr=snrs['mean']
)
if plot_residuals:
fig.suptitle("Difference between Measured and True Clock phases")
else:
fig.suptitle("Comparison Measured and True Clock Phases")
axs = fig.get_axes()
axs[-1].set_xlabel(f'Antenna {title} {color_label}')
#
i=0
secax = axs[i].child_axes[0]
secax.set_xlabel('Time $\\Delta\\varphi/(2\\pi f_{beac})$ [ns]')
#
i=1
axs[i].set_ylabel("Antenna no.")
# Save figure
if fig_dir:
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".{fname_extra}F{freq_name}.pdf"))
print(f"True phases written to", antennas_fname)
if show_plots:
plt.show()

View file

@ -1,156 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
import h5py
from itertools import combinations, product
import matplotlib.pyplot as plt
import numpy as np
import aa_generate_beacon as beacon
import lib
from lib import figlib
if __name__ == "__main__":
from os import path
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
parser.add_argument('ref_ant_idx', default=None, nargs='*', type=int, help='Reference Antenna Indices for Baselines(ref_ant_idx, *). Leave empty to use all available antennas. (Default: %(default)s) ')
args = parser.parse_args()
figsize = (12,8)
c_light = 3e8*1e-9
show_plots = args.show_plots
ref_ant_id = args.ref_ant_idx # leave None for all baselines
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
time_diffs_fname = 'time_diffs.hdf5' if False else antennas_fname
fig_dir = args.fig_dir # set None to disable saving
if not path.isfile(antennas_fname):
print("Antenna file cannot be found, did you try generating a beacon?")
sys.exit(1)
# Read in antennas from file
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
# run over all baselines
if not ref_ant_id:
print("Doing all baselines")
baselines = combinations(antennas,2)
# use ref_ant
else:
ref_ants = [antennas[i] for i in ref_ant_id]
print("Doing all baselines with {}".format([int(a.name) for a in ref_ants]))
baselines = product(ref_ants, antennas)
# For now, only one beacon_frequency is supported
freq_names = antennas[0].beacon_info.keys()
if len(freq_names) > 1:
raise NotImplementedError
freq_name = next(iter(freq_names))
# Collect baselines from optional generators
baselines = list(baselines)
# Get phase difference per baseline
phase_diffs = np.empty( (len(baselines), 2) )
for i, base in enumerate(baselines):
if i%1000==0:
print(i, "out of", len(baselines))
# read f_beacon from the first antenna
f_beacon = base[0].beacon_info[freq_name]['freq']
# Get true phase diffs
try:
clock_phases = np.array([ant.beacon_info[freq_name]['clock_phase'] for ant in base])
clock_phases_diff = lib.phase_mod(lib.phase_mod(clock_phases[1]) - lib.phase_mod(clock_phases[0]))
except IndexError:
# clock_phase not determined yet
print(f"Missing clock_phases for {freq_name} in baseline {base[0].name},{base[1].name}")
clock_phases_diff = np.nan
# save phase difference with antenna names
phase_diffs[i] = [f_beacon, clock_phases_diff]
beacon.write_baseline_time_diffs_hdf5(time_diffs_fname, baselines, phase_diffs[:,1], [0]*len(phase_diffs), phase_diffs[:,0])
##############################
# Compare actual time shifts #
##############################
actual_antenna_clock_phases = { a.name: -2*np.pi*a.attrs['clock_offset']*f_beacon for a in sorted(antennas, key=lambda a: int(a.name)) }
# Compare actual time shifts
my_phase_diffs = []
for i,b in enumerate(baselines):
actual_clock_phase_diff = lib.phase_mod( lib.phase_mod(actual_antenna_clock_phases[b[1].name]) - lib.phase_mod(actual_antenna_clock_phases[b[0].name]))
this_actual_clock_phase_diff = lib.phase_mod( actual_clock_phase_diff )
my_phase_diffs.append(this_actual_clock_phase_diff)
# Make a plot
if True:
N_base = len(baselines)
N_ant = len(antennas)
for i in range(2):
plot_residuals = i == 1
true_phases = my_phase_diffs
measured_phases = phase_diffs[:,1]
hist_kwargs = {}
if plot_residuals:
measured_phases = lib.phase_mod(measured_phases - true_phases)
hist_kwargs['histtype'] = 'stepfilled'
fig = figlib.phase_comparison_figure(
measured_phases,
true_phases,
plot_residuals=plot_residuals,
f_beacon=f_beacon,
figsize=figsize,
hist_kwargs=hist_kwargs,
fit_gaussian=plot_residuals,
)
axs = fig.get_axes()
if plot_residuals:
fig.suptitle("Difference between Measured and Actual phase difference\n for Baselines (i,j" + (')' if not ref_ant_id else '='+str([ int(a.name) for a in ref_ants])+')'))
axs[-1].set_xlabel("Baseline Phase Residual $\\Delta\\varphi_{ij_{meas}} - \\Delta\\varphi_{ij_{true}}$ [rad]")
else:
fig.suptitle("Comparison Measured and Actual phase difference\n for Baselines (i,j" + (')' if not ref_ant_id else '='+str([ int(a.name) for a in ref_ants])+')'))
axs[-1].set_xlabel("Baseline Phase $\\Delta\\varphi_{ij}$ [rad]")
#
i=0
secax = axs[i].child_axes[0]
secax.set_xlabel('Time $\\Delta\\varphi/(2\\pi f_{beac})$ [ns]')
#
i=1
axs[i].set_ylabel("Baseline no.")
if fig_dir:
extra_name = "measured"
if plot_residuals:
extra_name = "residuals"
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".{extra_name}.F{freq_name}.pdf"))
if show_plots:
plt.show()

View file

@ -1,293 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
import h5py
from itertools import combinations, zip_longest
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import matplotlib as mpl
import numpy as np
import json
import aa_generate_beacon as beacon
import lib
from lib import figlib
if __name__ == "__main__":
from os import path
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
args = parser.parse_args()
figsize = (12,8)
show_plots = args.show_plots
ref_ant_id = None # leave None for all baselines
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
time_diffs_fname = 'time_diffs.hdf5' if False else antennas_fname
fig_dir = args.fig_dir # set None to disable saving
basenames, time_diffs, f_beacons, clock_phase_diffs, k_periods = beacon.read_baseline_time_diffs_hdf5(time_diffs_fname)
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
# TODO: allow multiple frequencies
if (f_beacon != f_beacons).any():
raise NotImplementedError
N_base = len(basenames)
N_ant = len(antennas)
# reshape time_diffs into N_ant x N_ant matrix
clock_phase_matrix = np.full( (N_ant, N_ant), np.nan, dtype=float)
## set i=i terms to 0
for i in range(N_ant):
clock_phase_matrix[i,i] = 0
## fill matrix
name2idx = lambda name: int(name)-1
for i, b in enumerate(basenames):
idx = (name2idx(b[0]), name2idx(b[1]))
clock_phase_matrix[(idx[0], idx[1])] = lib.phase_mod(clock_phase_diffs[i])
clock_phase_matrix[(idx[1], idx[0])] = lib.phase_mod(-1*clock_phase_diffs[i])
mat_kwargs = dict(
norm = Normalize(vmin=-np.pi, vmax=+np.pi),
cmap = mpl.cm.get_cmap('Spectral_r')
)
# Show Matrix as figure
if True:
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Measured clock phase differences Baseline i,j")
ax.set_ylabel("Antenna no. i")
ax.set_xlabel("Antenna no. j")
im = ax.imshow(clock_phase_matrix, interpolation='none', **mat_kwargs)
fig.colorbar(im, ax=ax)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".matrix.original.pdf"))
plt.close(fig)
# Modify the matrix to let each column represent multiple
# measurements of the same baseline (j,0) phase difference
if True:
# for each row j subtract the 0,j element from the whole row
# and apply phase_mod
first_row = -1*(clock_phase_matrix[0,:] * np.ones_like(clock_phase_matrix)).T
# Show subtraction Matrix as figure
if True:
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Clock Phase Subtraction matrix i,j")
ax.set_ylabel("Antenna no. i")
ax.set_xlabel("Antenna no. j")
im = ax.imshow(first_row, interpolation='none', **mat_kwargs)
fig.colorbar(im, ax=ax)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".matrix.first_row.pdf"))
plt.close(fig)
clock_phase_matrix = clock_phase_matrix - first_row
clock_phase_matrix = lib.phase_mod(clock_phase_matrix)
# Except for the first row, these are all separate measurements
# Condense into phase offset per antenna
if True: # do not use the first row
my_mask = np.arange(1, len(clock_phase_matrix), dtype=int)
else:
my_mask = np.arange(0, len(clock_phase_matrix), dtype=int)
mean_clock_phase = np.nanmean(clock_phase_matrix[my_mask], axis=0)
std_clock_phase = np.nanstd( clock_phase_matrix[my_mask], axis=0)
# Remove the mean from the matrix
if False:
clock_phase_matrix = clock_phase_matrix - np.mean(mean_clock_phase)
mean_clock_phase = np.nanmean(clock_phase_matrix[my_mask], axis=0)
std_clock_phase = np.nanstd( clock_phase_matrix[my_mask], axis=0)
# Show resulting matrix as figure
if True:
fig, axs = plt.subplots(2,1, sharex=True, figsize=figsize)
axs[0].set_title("Modified clock phase differences Baseline 0,j")
axs[0].set_ylabel("Antenna no. 0")
axs[-1].set_xlabel("Antenna no. j")
im = axs[0].imshow(clock_phase_matrix, interpolation='none', **mat_kwargs)
fig.colorbar(im, ax=axs)
axs[0].set_aspect('auto')
colours = [mat_kwargs['cmap'](mat_kwargs['norm'](x)) for x in mean_clock_phase]
axs[1].set_ylabel("Mean Baseline Phase (0, j)[rad]")
axs[1].errorbar(np.arange(N_ant), mean_clock_phase, yerr=std_clock_phase, ls='none')
axs[1].scatter(np.arange(N_ant), mean_clock_phase, c=colours,s=4)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".matrix.modified.pdf"))
plt.close(fig)
# write into antenna hdf5
with h5py.File(antennas_fname, 'a') as fp:
group = fp['antennas']
freq_name = None
for i, ant in enumerate(antennas):
h5ant = group[ant.name]
h5beacon_info = h5ant['beacon_info']
# find out freq_name
if freq_name is None:
freq_name = [ k for k in h5beacon_info.keys() if np.isclose(h5beacon_info[k].attrs['freq'], f_beacon)][0]
h5attrs = h5beacon_info[freq_name].attrs
idx = name2idx(ant.name)
h5attrs['clock_phase_mean'] = mean_clock_phase[idx]
h5attrs['clock_phase_std'] = std_clock_phase[idx]
##############################
# Compare actual time shifts #
##############################
actual_antenna_time_shifts = { a.name: a.attrs['clock_offset'] for a in sorted(antennas, key=lambda a: int(a.name)) }
if True:
actual_antenna_phase_shifts = [ -1*lib.phase_mod(2*np.pi*f_beacon*v) for k,v in actual_antenna_time_shifts.items() ]
antenna_names = [int(k)-1 for k,v in actual_antenna_time_shifts.items() ]
# Make sure to shift all antennas by a global phase
global_phase_shift = actual_antenna_phase_shifts[0] - mean_clock_phase[0]
actual_antenna_phase_shifts = lib.phase_mod(actual_antenna_phase_shifts - global_phase_shift )
fit_info = {}
for i in range(2):
plot_residuals = i == 1
true_phases = actual_antenna_phase_shifts
measured_phases = mean_clock_phase
hist_kwargs = {}
if plot_residuals:
measured_phases = lib.phase_mod(measured_phases - actual_antenna_phase_shifts)
fig, _fit_info = figlib.phase_comparison_figure(
measured_phases,
true_phases,
plot_residuals=plot_residuals,
f_beacon=f_beacon,
figsize=figsize,
hist_kwargs=hist_kwargs,
fit_gaussian=plot_residuals,
return_fit_info = True,
)
axs = fig.get_axes()
if plot_residuals:
fig.suptitle("Difference between Measured and Actual phases (minus global phase)\n for Antenna $i$")
axs[-1].set_xlabel("Antenna Mean Phase Residual $\\Delta_\\varphi$")
else:
fig.suptitle("Comparison Measured and Actual phases (minus global phase)\n for Antenna $i$")
axs[-1].set_xlabel("Antenna Mean Phase $\\varphi$")
i=1
axs[i].set_ylabel("Antenna no.")
#axs[i].errorbar(mean_clock_phase, np.arange(N_ant), yerr=std_clock_phase, marker='4', alpha=0.7, ls='none', color=colors[0], label='Measured')
fig.tight_layout()
if fig_dir:
extra_name = "measured"
if plot_residuals:
extra_name = "residuals"
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".phase.{extra_name}.pdf"))
# Save fit_info to data file
if fname_dir and plot_residuals:
with open(path.join(fname_dir, 'phase_time_residuals.json'), 'w') as fp:
json.dump(
{
'mean': np.mean(measured_phases),
'std': np.std(measured_phases),
'values': measured_phases.tolist(),
},
fp)
##########################
##########################
##########################
actual_baseline_time_shifts = []
for i,b in enumerate(basenames):
actual_baseline_time_shift = actual_antenna_time_shifts[b[0]] - actual_antenna_time_shifts[b[1]]
actual_baseline_time_shifts.append(actual_baseline_time_shift)
# unpack mean_clock_phase back into a list of time diffs
measured_baseline_time_diffs = []
for i,b in enumerate(basenames):
phase0, phase1 = mean_clock_phase[name2idx(b[0])], mean_clock_phase[name2idx(b[1])]
measured_baseline_time_diffs.append(lib.phase_mod(phase1 - phase0)/(2*np.pi*f_beacon))
# Make a plot
if True:
for i in range(2):
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Baseline Time difference reconstruction" + ( '' if i == 0 else ' (wrapped time)'))
ax.set_xlabel("Baseline no.")
ax.set_ylabel("Time $\\Delta t$ [ns]")
if True:
forward = lambda x: x/(2*np.pi*f_beacon)
inverse = lambda x: 2*np.pi*x*f_beacon
secax = ax.secondary_yaxis('right', functions=(inverse, forward))
secax.set_ylabel('Phase $\\Delta \\varphi$ [rad]')
if True: # indicate single beacon period span
ax.plot((-1, -1), (-1/(2*f_beacon), 1/(2*f_beacon)), marker='3', ms=10, label='1/f_beacon')
if i == 0:
ax.plot(np.arange(N_base), actual_baseline_time_shifts, ls='none', marker='h', alpha=0.8, label='actual time shifts')
else:
ax.plot(np.arange(N_base), (actual_baseline_time_shifts+1/(2*f_beacon))%(1/f_beacon) - 1/(2*f_beacon), ls='none', marker='h', label='actual time shifts', alpha=0.8)
ax.plot(np.arange(N_base), measured_baseline_time_diffs, ls='none', alpha=0.8, marker='x', label='calculated')
ax.legend()
if fig_dir:
extra_name = ''
if i == 1:
extra_name = '.wrapped'
old_lims = (ax.get_xlim(), ax.get_ylim())
for j in range(2):
if j == 1:
ax.set_xlim(-5, 50)
extra_name += '.n50'
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".time_comparison{extra_name}.pdf"))
ax.set_xlim(*old_lims[0])
ax.set_ylim(*old_lims[1])
if show_plots:
plt.show()

View file

@ -1,543 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Find the best integer multiple to shift
antennas to be able to resolve
"""
import h5py
from itertools import combinations, zip_longest, product
import matplotlib.pyplot as plt
import numpy as np
from os import path
import os
from scipy.interpolate import interp1d
from earsim import REvent
from atmocal import AtmoCal
import aa_generate_beacon as beacon
import lib
from lib import rit
def find_best_sample_shifts_summing_at_location(test_loc, antennas, allowed_sample_shifts, dt=None, sample_shift_first_trace=0, plot_iteration_with_shifted_trace=None, fig_dir=None, fig_distinguish=None):
"""
Find the best sample_shift for each antenna by summing the antenna traces
and seeing how to get the best alignment.
"""
a_ = []
t_ = []
t_min = 1e9
t_max = -1e9
a_maxima = []
N_ant = len(antennas)
if dt is None:
dt = antennas[0].t_AxB[1] - antennas[0].t_AxB[0]
if not hasattr(plot_iteration_with_shifted_trace, '__len__'):
if plot_iteration_with_shifted_trace:
plot_iteration_with_shifted_trace = [ plot_iteration_with_shifted_trace ]
else:
plot_iteration_with_shifted_trace = []
# propagate to test location
for i, ant in enumerate(antennas):
aloc = [ant.x, ant.y, ant.z]
delta, dist = atm.light_travel_time(test_loc, aloc)
delta = delta*1e9
t__ = np.subtract(ant.t_AxB, delta)
t_.append(t__)
a_.append(ant.E_AxB)
a_maxima.append(max(ant.E_AxB))
if t__[0] < t_min:
t_min = t__[0]
if t__[-1] > t_max:
t_max = t__[-1]
# sort traces with descending maxima
sort_idx = np.argsort(a_maxima)[::-1]
t_ = [ t_[i] for i in sort_idx ]
a_ = [ a_[i] for i in sort_idx ]
# Interpolate and find best sample shift
max_neg_shift = 0 #np.min(allowed_sample_shifts) * dt
max_pos_shift = 0 #np.max(allowed_sample_shifts) * dt
t_sum = np.arange(t_min+max_neg_shift, t_max+max_pos_shift, dt)
a_sum = np.zeros(len(t_sum))
best_sample_shifts = np.zeros( (len(antennas)) ,dtype=int)
for i, (t_r, E_) in enumerate(zip(t_, a_)):
f = interp1d(t_r, E_, assume_sorted=True, bounds_error=False, fill_value=0)
a_int = f(t_sum)
if i == 0:
a_sum += a_int
best_sample_shifts[i] = sample_shift_first_trace
continue
# init figure
if i in plot_iteration_with_shifted_trace:
fig, ax = plt.subplots(figsize=figsize)
ax.set_title("Traces at ({:.1f},{:.1f},{:.1f}) i={i}/{tot}".format(*test_loc, i=i, tot=N_ant))
ax.set_xlabel("Time [ns]")
ax.set_ylabel("Amplitude")
ax.plot(t_sum, a_sum)
shift_maxima = np.zeros( len(allowed_sample_shifts) )
for j, shift in enumerate(allowed_sample_shifts):
augmented_a = np.roll(a_int, shift)
shift_maxima[j] = np.max(augmented_a + a_sum)
if i in plot_iteration_with_shifted_trace:
ax.plot(t_sum, augmented_a, alpha=0.7, ls='dashed', label=f'{shift}')
# transform maximum into best_sample_shift
best_idx = np.argmax(shift_maxima)
best_sample_shifts[i] = allowed_sample_shifts[best_idx]
best_augmented_a = np.roll(a_int, best_sample_shifts[i])
a_sum += best_augmented_a
# cleanup figure
if i in plot_iteration_with_shifted_trace:
if True: # plot best k again
ax.plot(t_sum, augmented_a, alpha=0.8, label=f'best k={best_sample_shifts[i]}', lw=2)
ax.legend( ncol=5 )
if fig_dir:
fig.tight_layout()
fname = path.join(fig_dir, path.basename(__file__) + f'.{fig_distinguish}i{i}' + '.loc{:.1f}-{:.1f}-{:.1f}'.format(*test_loc))
if True:
old_xlim = ax.get_xlim()
if True: # zoomed on part without peak of this trace
wx = 100
x = max(t_r) - wx
ax.set_xlim(x-wx, x+wx)
fig.savefig(fname + ".zoomed.beacon.pdf")
if True: # zoomed on peak of this trace
x = t_r[np.argmax(E_)]
wx = 50 + (max(best_sample_shifts) - min(best_sample_shifts))*dt
ax.set_xlim(x-wx, x+wx)
fig.savefig(fname + ".zoomed.peak.pdf")
ax.set_xlim(*old_xlim)
fig.savefig(fname + ".pdf")
plt.close(fig)
# sort by antenna (undo sorting by maximum)
undo_sort_idx = np.argsort(sort_idx)
best_sample_shifts = best_sample_shifts[undo_sort_idx]
# Return ks
return best_sample_shifts, np.max(a_sum)
if __name__ == "__main__":
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
atm = AtmoCal()
from scriptlib import MyArgumentParser
parser = MyArgumentParser(default_fig_dir="./figures/periods_from_shower_figures/")
parser.add_argument('--quick_run', action='store_true', help='Use a very coarse grid (6x6)')
parser.add_argument('--max-k', type=float, default=2, help='Maximum abs(k) allowed to be shifted. (Default: %(default)d)')
parser.add_argument('-N', '--N_runs', type=int, default=5, help='Maximum amount of iterations to grid search. (Default: %(default)d)')
parser.add_argument('-l', '--passband-low', type=float, default=30e-3, help='Lower frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)g)')
parser.add_argument('-u', '--passband-high', type=float, default=80e-3, help='Upper frequency [GHz] of the passband filter. (set -1 for np.inf) (Default: %(default)g)')
parser.add_argument('--input-fname', type=str, default=None, help='Path to mysim.sry, either directory or path. If empty it takes DATA_DIR and appends mysim.sry. (Default: %(default)s)')
args = parser.parse_args()
if not args.input_fname:
args.input_fname = args.data_dir
if path.isdir(args.input_fname):
args.input_fname = path.join(args.input_fname, "mysim.sry")
figsize = (12,8)
fig_dir = args.fig_dir
fig_subdir = path.join(fig_dir, 'shifts/')
show_plots = args.show_plots
max_k = int(args.max_k)
allowed_ks = np.arange(-max_k, max_k+1, dtype=int)
Xref = 400
N_runs = args.N_runs
remove_beacon_from_trace = True
apply_signal_window_from_max = True
low_bp = args.passband_low if args.passband_low >= 0 else np.inf # GHz
high_bp = args.passband_high if args.passband_high >= 0 else np.inf # GHz
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
time_diffs_fname = 'time_diffs.hdf5' if not True else antennas_fname
tx_fname = path.join(fname_dir, beacon.tx_fname)
## This is a file indicating whether the k-finding algorithm was
## stopped early. This happens when the ks do not change between
## two consecutive iterations.
run_break_fname = path.join(fname_dir, 'ca_breaked_run')
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
if fig_subdir:
os.makedirs(fig_subdir, exist_ok=True)
# Read in antennas from file
_, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
_, __, txdata = beacon.read_tx_file(tx_fname)
# Read original REvent
ev = REvent(args.input_fname)
# .. patch in our antennas
ev.antennas = antennas
# For now only implement using one freq_name
freq_names = antennas[0].beacon_info.keys()
if len(freq_names) > 1:
raise NotImplementedError
freq_name = next(iter(freq_names))
f_beacon = ev.antennas[0].beacon_info[freq_name]['freq']
##
## Manipulate time and traces of each antenna
##
### Remove time due to true phase
### and optionally remove the beacon
### Note: there is no use in changing *_AxB variables here (except for plotting),
### they're recomputed by the upcoming rit.set_pol_and_bp call.
measured_repair_offsets = beacon.read_antenna_clock_repair_offsets(ev.antennas, mode='phases', freq_name=freq_name)
for i, ant in enumerate(ev.antennas):
ev.antennas[i].orig_t = ev.antennas[i].t
ev.antennas[i].t += measured_repair_offsets[i]
# t_AxB will be set by the rit.set_pol_and_bp function
ev.antennas[i].t_AxB += measured_repair_offsets[i]
if apply_signal_window_from_max:
N_pre, N_post = 250, 250 # TODO: make this configurable
# Get max idx from all the traces
# and select the strongest
max_idx = []
maxs = []
for trace in [ant.Ex, ant.Ey, ant.Ez]:
idx = np.argmax(np.abs(trace))
max_idx.append(idx)
maxs.append( np.abs(trace[idx]) )
idx = np.argmax(maxs)
max_idx = max_idx[idx]
# Create window around max_idx
low_idx = max(0, max_idx-N_pre)
high_idx = min(len(ant.t), max_idx+N_post)
ev.antennas[i].orig_t = ant.orig_t[low_idx:high_idx]
ev.antennas[i].t = ant.t[low_idx:high_idx]
ev.antennas[i].Ex = ant.Ex[low_idx:high_idx]
ev.antennas[i].Ey = ant.Ey[low_idx:high_idx]
ev.antennas[i].Ez = ant.Ez[low_idx:high_idx]
ev.antennas[i].t_AxB = ant.t_AxB[low_idx:high_idx]
ev.antennas[i].E_AxB = ant.E_AxB[low_idx:high_idx]
# .. and remove the beacon from the traces
# Note: ant.E_AxB is recalculated by rit.set_pol_and_bp
if remove_beacon_from_trace:
clock_phase = measured_repair_offsets[i]*2*np.pi*f_beacon
beacon_phase = ant.beacon_info[freq_name]['beacon_phase']
f = ant.beacon_info[freq_name]['freq']
ampl = ant.beacon_info[freq_name]['amplitude']
calc_beacon = lib.sine_beacon(f, ev.antennas[i].t, amplitude=ampl, phase=beacon_phase-clock_phase)
tx_amps = txdata['amplitudes']
tx_amps_sum = np.sum(tx_amps)
# Split up contribution to the various polarisations
for j, amp in enumerate(tx_amps):
if j == 0:
ev.antennas[i].Ex -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 1:
ev.antennas[i].Ey -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 2:
ev.antennas[i].Ez -= amp*(1/tx_amps_sum)*calc_beacon
# Subtract the beacon from E_AxB
ev.antennas[i].E_AxB -= calc_beacon
# Make a figure of the manipulated traces
if i == 72:
orig_beacon_amplifier = ampl/max(ant.beacon)
fig, ax = plt.subplots(figsize=figsize)
ax.set_title(f"Signal and Beacon traces Antenna {ant.name}")
ax.set_xlabel("Time [ns]")
ax.set_ylabel("Amplitude [$\\mu V/m$]")
ax.plot(ant.t_AxB, ant.E_AxB + calc_beacon, alpha=0.6, ls='dashed', label='Signal') # calc_beacon was already removed
ax.plot(ant.t_AxB, calc_beacon, alpha=0.6, ls='dashed', label='Calc Beacon')
ax.plot(ant.t_AxB, ant.E_AxB, alpha=0.6, label="Signal - Calc Beacon")
ax.legend()
# save
if fig_dir:
fig.tight_layout()
if True: # zoom
old_xlim = ax.get_xlim()
if True: # zoomed on part without peak of this trace
wx, x = 200, min(ant.t_AxB)
ax.set_xlim(x-5, x+wx)
fig.savefig(path.join(fig_dir, __file__+f'.traces.A{ant.name}.zoomed.beacon.pdf'))
if True: # zoomed on peak of this trace
idx = np.argmax(ev.antennas[i].E_AxB)
x = ev.antennas[i].t_AxB[idx]
wx = 300
ax.set_xlim(x-wx//2, x+wx//2)
fig.savefig(path.join(fig_dir, __file__+f".traces.A{ant.name}.zoomed.peak.pdf"))
ax.set_xlim(*old_xlim)
fig.savefig(path.join(fig_dir, __file__+f'.traces.A{ant.name}.pdf'))
if show_plots:
plt.show()
# Prepare polarisation and passbands
rit.set_pol_and_bp(ev, low=low_bp, high=high_bp)
# determine allowable ks per location
dt = ev.antennas[0].t_AxB[1] - ev.antennas[0].t_AxB[0]
allowed_sample_shifts = np.rint(allowed_ks/f_beacon /dt).astype(int)
print("Checking:", allowed_ks, ": shifts :", allowed_sample_shifts)
##
## Determine grid positions
##
dXref = atm.distance_to_slant_depth(np.deg2rad(ev.zenith),Xref,0)
scale2d = dXref*np.tan(np.deg2rad(2.))
scale4d = dXref*np.tan(np.deg2rad(4.))
if args.quick_run: #quicky
x_coarse = np.linspace(-scale2d, scale2d, 6)
y_coarse = np.linspace(-scale2d, scale2d, 6)
x_fine = x_coarse/4
y_fine = y_coarse/4
else: # long
x_coarse = np.linspace(-scale4d, scale4d, 40)
y_coarse = np.linspace(-scale4d, scale4d, 40)
x_fine = np.linspace(-scale2d, scale2d, 40)
y_fine = np.linspace(-scale2d, scale2d, 40)
## Remove run_break_fname if it exists
try:
os.remove(run_break_fname)
except OSError:
pass
##
## Do calculations on the grid
##
for r in range(N_runs):
# Setup Plane grid to test
if r == 0:
xoff, yoff = 0,0
x = x_coarse
y = y_coarse
else:
# zooming in
# best_idx is defined at the end of the loop
old_ks_per_loc = ks_per_loc[best_idx]
xoff, yoff = locs[best_idx]
if r == 1:
x = x_fine
y = y_fine
else:
x /= 4
y /= 4
print(f"Testing grid run {r} centered on ({xoff}, {yoff})")
ks_per_loc = np.zeros( (len(x)*len(y), len(ev.antennas)) , dtype=int)
maxima_per_loc = np.zeros( (len(x)*len(y)))
## Check each location on grid
xx = []
yy = []
N_loc = len(maxima_per_loc)
for i, (x_, y_) in enumerate(product(x,y)):
tmp_fig_subdir = None
if i % 10 ==0:
print(f"Testing location {i} out of {N_loc}")
tmp_fig_subdir = fig_subdir
test_loc = (x_+xoff)* ev.uAxB + (y_+yoff)*ev.uAxAxB + dXref *ev.uA
xx.append(x_+xoff)
yy.append(y_+yoff)
# Find best k for each antenna
shifts, maximum = find_best_sample_shifts_summing_at_location(test_loc, ev.antennas, allowed_sample_shifts, dt=dt, fig_dir=tmp_fig_subdir, plot_iteration_with_shifted_trace=[ 5, len(ev.antennas)-1], fig_distinguish=f"run{r}.")
# Translate sample shifts back into period multiple k
ks = np.rint(shifts*f_beacon*dt)
ks_per_loc[i] = ks
maxima_per_loc[i] = maximum
xx = np.array(xx)
yy = np.array(yy)
locs = list(zip(xx, yy))
## Save maxima to file
np.savetxt(path.join(fig_dir, path.basename(__file__)+f'.maxima.run{r}.txt'), np.column_stack((locs, maxima_per_loc, ks_per_loc)) )
if True: #plot maximum at test locations
fig, axs = plt.subplots(figsize=figsize)
axs.set_title(f"Optimizing signal strength by varying $k$ per antenna,\n Grid Run {r}")
axs.set_ylabel("vxvxB [km]")
axs.set_xlabel(" vxB [km]")
axs.set_aspect('equal', 'datalim')
sc = axs.scatter(xx/1e3, yy/1e3, c=maxima_per_loc, cmap='Spectral_r', alpha=0.6)
fig.colorbar(sc, ax=axs, label='Max Amplitude [$\\mu V/m$]')
# indicate maximum value
idx = np.argmax(maxima_per_loc)
axs.plot(xx[idx]/1e3, yy[idx]/1e3, 'bx', ms=30)
if fig_dir:
old_xlims = axs.get_xlim()
old_ylims = axs.get_ylim()
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__)+f'.maxima.run{r}.pdf'))
if False:
axs.plot(tx.x/1e3, tx.y/1e3, marker='X', color='k')
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__)+f'.maxima.run{r}.with_tx.pdf'))
axs.set_xlim(*old_xlims)
axs.set_ylim(*old_ylims)
fig.tight_layout()
##
best_idx = np.argmax(maxima_per_loc)
best_k = ks_per_loc[best_idx]
print("Max at location: ", locs[best_idx])
print('Best k:', best_k)
## Save best ks to file
np.savetxt(path.join(fig_dir, path.basename(__file__)+f'.bestk.run{r}.txt'), best_k )
## Do a small reconstruction of the shower for best ks
if True:
print("Reconstructing for best k")
for j in range(2):
power_reconstruction = j==1
if power_reconstruction: # Do power reconstruction
# backup antenna times
backup_times = [ ant.t_AxB for ant in ev.antennas ]
# incorporate ks into timing
for i, ant in enumerate(ev.antennas):
ev.antennas[i].t_AxB = ant.t_AxB - best_k[i] * 1/f_beacon
xx, yy, p, ___ = rit.shower_plane_slice(ev, X=Xref, Nx=len(x), Ny=len(y), wx=x[-1]-x[0], wy=y[-1]-y[0], xoff=xoff, yoff=yoff, zgr=0)
# repair antenna times
for i, backup_t_AxB in enumerate(backup_times):
ev.antennas[i].t_AxB = backup_t_AxB
else: # get maximum amplitude at each location
maxima = np.empty( len(locs) )
for i, loc in enumerate(locs):
test_loc = loc[0]* ev.uAxB + loc[1]*ev.uAxAxB + dXref *ev.uA
P, t_, a_, a_sum, t_sum = rit.pow_and_time(test_loc, ev, dt=dt)
maxima[i] = np.max(a_sum)
fig, axs = plt.subplots(figsize=figsize)
axs.set_title(f"Shower slice for best k, Grid Run {r}")
axs.set_ylabel("vxvxB [km]")
axs.set_xlabel(" vxB [km]")
axs.set_aspect('equal', 'datalim')
if power_reconstruction:
sc = axs.scatter(xx/1e3, yy/1e3, c=p, cmap='Spectral_r', alpha=0.6)
fig.colorbar(sc, ax=axs, label='Power')
else:
sc = axs.scatter(xx/1e3, yy/1e3, c=maxima, cmap='Spectral_r', alpha=0.6)
fig.colorbar(sc, ax=axs, label='Max Amplitude [$\\mu V/m$]')
if fig_dir:
if power_reconstruction:
fname_extra = "power"
else:
fname_extra = "max_amp"
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__)+f'.reconstruction.run{r}.{fname_extra}.pdf'))
# Abort if no improvement
if ( r!= 0 and (old_ks_per_loc == ks_per_loc[best_idx]).all() ):
print(f"No changes from previous grid, breaking at iteration {r} out of {N_runs}")
try:
with open(run_break_fname, 'wt', encoding='utf-8') as fp:
fp.write(f"Breaked at grid iteration {r} out of {N_runs}")
except:
pass
break
old_ks_per_loc = ks_per_loc[best_idx]
# Save best ks to hdf5 antenna file
with h5py.File(antennas_fname, 'a') as fp:
group = fp.require_group('antennas')
for i, ant in enumerate(antennas):
h5ant = group[ant.name]
h5beacon_info = h5ant['beacon_info']
# find out freq_name
if freq_name is None:
freq_name = [ k for k in h5beacon_info.keys() if np.isclose(h5beacon_info[k].attrs['freq'], f_beacon)][0]
h5attrs = h5beacon_info[freq_name].attrs
h5attrs['best_k'] = old_ks_per_loc[i]
h5attrs['best_k_time'] = old_ks_per_loc[i]/f_beacon
if show_plots:
plt.show()

View file

@ -1,118 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Report best time offset per frequency for each antenna
"""
import matplotlib.pyplot as plt
import numpy as np
from os import path
import aa_generate_beacon as beacon
from lib import figlib
if __name__ == "__main__":
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
args = parser.parse_args()
figsize = (12,8)
fig_dir = args.fig_dir # set None to disable saving
show_plots = args.show_plots
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
time_diffs_fname = 'time_diffs.hdf5' if not True else antennas_fname
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
# Read in antennas from file
_, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
# For now only implement using one freq_name
freq_names = antennas[0].beacon_info.keys()
if len(freq_names) > 1:
raise NotImplementedError
freq_name = next(iter(freq_names))
f_beacon = antennas[0].beacon_info[freq_name]['freq']
# TODO: redo matrix sweeping for new timing??
measured_antenna_time_shifts = {}
for i, ant in enumerate(antennas):
clock_phase_time = ant.beacon_info[freq_name]['clock_phase_mean']/(2*np.pi*f_beacon)
best_k_time = ant.beacon_info[freq_name]['best_k_time']
total_clock_time = best_k_time + clock_phase_time
measured_antenna_time_shifts[ant.name] = -1*total_clock_time
###
# Compare actual vs measured time shifts
###
actual_antenna_time_shifts = { a.name: a.attrs['clock_offset'] for a in sorted(antennas, key=lambda a: int(a.name)) }
N_ant = len(antennas)
if True:
# keep dataset in the same ordering
antenna_names = [int(k)-1 for k,v in actual_antenna_time_shifts.items()]
actual_time_shifts = np.array([ v for k,v in actual_antenna_time_shifts.items()])
measured_time_shifts = np.array([ measured_antenna_time_shifts[k] for k,v in actual_antenna_time_shifts.items() ])
# remove global shift
global_shift = actual_time_shifts[0] - measured_time_shifts[0]
actual_time_shifts -= global_shift
for i in range(2):
plot_residuals = i == 1
true_phases = actual_time_shifts
measured_phases = measured_time_shifts
hist_kwargs = {}
if plot_residuals:
measured_phases = measured_phases - true_phases
hist_kwargs['histtype'] = 'stepfilled'
fig = figlib.phase_comparison_figure(
measured_phases,
true_phases,
plot_residuals=plot_residuals,
f_beacon=f_beacon,
figsize=figsize,
hist_kwargs=hist_kwargs,
secondary_axis='phase',
fit_gaussian=True,
)
axs = fig.get_axes()
if plot_residuals:
time_shift_residuals = measured_time_shifts - actual_time_shifts
fig.suptitle("Difference between Measured and Actual clock offsets")
axs[-1].set_xlabel("Antenna Time Offset Residual $\\Delta_t$ [ns]")
else:
fig.suptitle("Comparison Measured and Actual clock offset")
axs[-1].set_xlabel("Antenna Time Offset $t_c = \\left(\\frac{\\Delta\\varphi}{2\\pi} + k\\right) / f_{beac}$ [ns]")
if fig_dir:
extra_name = "comparison"
if plot_residuals:
extra_name = "residuals"
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".time.{extra_name}.pdf"))
if show_plots:
plt.show()

View file

@ -1,208 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Do a reconstruction of airshower after correcting for the
clock offsets.
"""
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # required for projection='3d' on old matplotliblib versions
import numpy as np
from os import path
import pickle
import joblib
from earsim import REvent
from atmocal import AtmoCal
import aa_generate_beacon as beacon
import lib
from lib import rit
if __name__ == "__main__":
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
atm = AtmoCal()
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
parser.add_argument('--input-fname', type=str, default=None, help='Path to mysim.sry, either directory or path. If empty it takes DATA_DIR and appends mysim.sry. (Default: %(default)s)')
args = parser.parse_args()
if not args.input_fname:
args.input_fname = args.data_dir
if path.isdir(args.input_fname):
args.input_fname = path.join(args.input_fname, "mysim.sry")
figsize = (12,8)
fig_dir = args.fig_dir
fig_subdir = path.join(fig_dir, 'reconstruction')
show_plots = args.show_plots
apply_signal_window_from_max = True
remove_beacon_from_traces = True
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
pickle_fname = path.join(fname_dir, 'res.pkl')
tx_fname = path.join(fname_dir, beacon.tx_fname)
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
if fig_subdir:
os.makedirs(fig_subdir, exist_ok=True)
# Read in antennas from file
_, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
_, __, txdata = beacon.read_tx_file(tx_fname)
# Read original REvent
ev = REvent(args.input_fname)
# .. patch in our antennas
ev.antennas = antennas
# For now only implement using one freq_name
freq_names = antennas[0].beacon_info.keys()
if len(freq_names) > 1:
raise NotImplementedError
freq_name = next(iter(freq_names))
f_beacon = ev.antennas[0].beacon_info[freq_name]['freq']
# Repair clock offsets with the measured offsets
measured_repair_offsets = beacon.read_antenna_clock_repair_offsets(ev.antennas, mode='phases', freq_name=freq_name)
for i, ant in enumerate(ev.antennas):
# t_AxB will be set by the rit.set_pol_and_bp function
ev.antennas[i].t += measured_repair_offsets[i]
ev.antennas[i].t_AxB += measured_repair_offsets[i]
if apply_signal_window_from_max:
N_pre, N_post = 250, 250 # TODO: make this configurable
# Get max idx from all the traces
# and select the strongest
max_idx = []
maxs = []
for trace in [ant.Ex, ant.Ey, ant.Ez]:
idx = np.argmax(np.abs(trace))
max_idx.append(idx)
maxs.append( np.abs(trace[idx]) )
idx = np.argmax(maxs)
max_idx = max_idx[idx]
# Create window around max_idx
low_idx = max(0, max_idx-N_pre)
high_idx = min(len(ant.t), max_idx+N_post)
ev.antennas[i].orig_t = ant.orig_t[low_idx:high_idx]
ev.antennas[i].t = ant.t[low_idx:high_idx]
ev.antennas[i].Ex = ant.Ex[low_idx:high_idx]
ev.antennas[i].Ey = ant.Ey[low_idx:high_idx]
ev.antennas[i].Ez = ant.Ez[low_idx:high_idx]
ev.antennas[i].t_AxB = ant.t_AxB[low_idx:high_idx]
ev.antennas[i].E_AxB = ant.E_AxB[low_idx:high_idx]
# .. and remove the beacon from the traces
# Note: ant.E_AxB is recalculated by rit.set_pol_and_bp
if remove_beacon_from_traces:
clock_phase = measured_repair_offsets[i]*2*np.pi*f_beacon
beacon_phase = ant.beacon_info[freq_name]['beacon_phase']
f = ant.beacon_info[freq_name]['freq']
ampl_AxB = ant.beacon_info[freq_name]['amplitude']
calc_beacon = lib.sine_beacon(f, ev.antennas[i].t, amplitude=ampl_AxB, phase=beacon_phase-clock_phase)
tx_amps = txdata['amplitudes']
tx_amps_sum = np.sum(tx_amps)
# Split up contribution to the various polarisations
for j, amp in enumerate(tx_amps):
if j == 0:
ev.antennas[i].Ex -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 1:
ev.antennas[i].Ey -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 2:
ev.antennas[i].Ez -= amp*(1/tx_amps_sum)*calc_beacon
# Subtract the beacon from E_AxB
ev.antennas[i].E_AxB -= calc_beacon
##
## Make a figure of the manipulated traces
##
if i == 72:
orig_beacon_amplifier = ampl_AxB/max(ant.beacon)
for k in range(2):
if k == 0:
time = ant.t_AxB
trace = ant.E_AxB
tmp_beacon = calc_beacon
fname_extra = ""
else:
time = ant.t
trace = ant.Ex
tmp_beacon = tx_amps[0]/tx_amps_sum * calc_beacon
fname_extra = ".Ex"
fig, ax = plt.subplots(figsize=figsize)
ax.set_title(f"Signal and Beacon traces Antenna {ant.name}")
ax.set_xlabel("Time [ns]")
ax.set_ylabel("Amplitude [$\\mu V/m$]")
ax.plot(time, trace + tmp_beacon, alpha=0.6, ls='dashed', label='Signal') # calc_beacon was already removed
ax.plot(time, tmp_beacon, alpha=0.6, ls='dashed', label='Calc Beacon')
ax.plot(time, trace, alpha=0.6, label="Signal - Calc Beacon")
if k == 0:
ax.legend()
else:
ax.legend(title="Ex")
# save
if fig_dir:
fig.tight_layout()
if True: # zoom
old_xlim = ax.get_xlim()
if True: # zoomed on part without peak of this trace
wx, x = 100, 100
ax.set_xlim(x-wx, x+wx)
fig.savefig(path.join(fig_dir, __file__+f'.traces.A{ant.name}.zoomed.beacon{fname_extra}.pdf'))
if True: # zoomed on peak of this trace
idx = np.argmax(ev.antennas[i].E_AxB)
x = ev.antennas[i].t_AxB[idx]
wx = 100
ax.set_xlim(x-wx, x+wx)
fig.savefig(path.join(fig_dir, __file__+f".traces.A{ant.name}.zoomed.peak{fname_extra}.pdf"))
ax.set_xlim(*old_xlim)
fig.savefig(path.join(fig_dir, __file__+f'.traces.A{i}.pdf'))
N_X, Xlow, Xhigh = 23, 100, 1200
with joblib.parallel_backend("loky"):
res = rit.reconstruction(ev, outfile=fig_subdir+'/fig.pdf', slice_outdir=fig_subdir+'/', Xlow=Xlow, N_X=N_X, Xhigh=Xhigh, disable_pol_and_bp=True)
## Save a pickle
with open(pickle_fname, 'wb') as fp:
pickle.dump(res,fp)
if show_plots:
plt.show()

View file

@ -1,56 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Do a reconstruction of airshower after correcting for the
clock offsets.
"""
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # required for projection='3d' on old matplotliblib versions
import numpy as np
from os import path
import pickle
import aa_generate_beacon as beacon
import lib
from lib import rit
if __name__ == "__main__":
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
args = parser.parse_args()
fig_dir = args.fig_dir
show_plots = args.show_plots
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
pickle_fname = path.join(fname_dir, 'res.pkl')
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
# Load res from pickle
with open(pickle_fname, 'rb') as f:
res = pickle.load(f)
##
# Longitudinal figures
##
for i in range(2):
mode = ['grammage', 'distance'][i]
fig = rit.longitudinal_figure(res.dl[0], res.dX[0], res.profile_rit[0], mode=mode)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".{mode}.pdf"))

View file

@ -1,315 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
"""
Show how the Power changes when incorporating the
various clock offsets by plotting on a grid.
"""
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # required for projection='3d' on old matplotliblib versions
import numpy as np
from os import path
import joblib
from earsim import REvent
from atmocal import AtmoCal
import aa_generate_beacon as beacon
import lib
from lib import rit
def save_overlapping_traces_figure(test_location, ev, N_plot = 30, wx=200, title_extra=None, fname_distinguish='', fig_dir=None, **fig_kwargs):
P, t_, a_, a_sum, t_sum = rit.pow_and_time(test_location, ev, dt=1)
fig, axs = plt.subplots(**fig_kwargs)
axs.set_title("Antenna traces" + (("\n" + title_extra) if title_extra is not None else '') )
axs.set_xlabel("Time [ns]")
axs.set_ylabel("Amplitude [$\\mu V/m$]")
if False:
text_loc = (0.02, 0.95)
axs.text(*text_loc, '[' + ', '.join(['{:.2e}'.format(x) for x in test_location]) + ']', ha='left', transform=axs.transAxes)
a_max = [ np.amax(ant.E_AxB) for ant in ev.antennas ]
power_sort_idx = np.argsort(a_max)
for i, idx in enumerate(reversed(power_sort_idx)):
if i >= N_plot:
break
alpha = max(0.4, 1/N_plot)
axs.plot(t_[idx], a_[idx], color='r', alpha=alpha, lw=2)
if fig_dir:
if fname_distinguish:
fname_distinguish = "." + fname_distinguish
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'{fname_distinguish}.trace_overlap.{case}.pdf'))
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'{fname_distinguish}.trace_overlap.{case}.png'), transparent=True)
# Take center between t_low and t_high
if True:
orig_xlims = axs.get_xlim()
if not True: # t_high and t_low from strongest signal
t_low = np.min(t_[power_sort_idx[-1]])
t_high = np.max(t_[power_sort_idx[-1]])
else: # take t_high and t_low from plotted signals
a = [np.min(t_[idx]) for idx in power_sort_idx[-N_plot:]]
t_low = np.nanmin(a)
b = [np.max(t_[idx]) for idx in power_sort_idx[-N_plot:]]
t_high = np.nanmax(b)
if False:
axs.plot(a, [0]*N_plot, 'gx', ms=10)
axs.plot(b, [0]*N_plot, 'b+', ms=10)
center_x = (t_high - t_low)/2 + t_low
low_xlim = max(orig_xlims[0], center_x - wx)
high_xlim = min(orig_xlims[1], center_x + wx)
axs.set_xlim(low_xlim, high_xlim)
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'{fname_distinguish}.trace_overlap.zoomed.{case}.pdf'))
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'{fname_distinguish}.trace_overlap.zoomed.{case}.png'), transparent=True)
return fig
if __name__ == "__main__":
valid_cases = ['no_offset', 'repair_none', 'repair_phases', 'repair_all']
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
atm = AtmoCal()
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
parser.add_argument('--input-fname', type=str, default=None, help='Path to mysim.sry, either directory or path. If empty it takes DATA_DIR and appends mysim.sry. (Default: %(default)s)')
group = parser.add_argument_group('figures')
for case in valid_cases:
group.add_argument('--'+case.replace('_','-'), dest='figures', action='append_const', const=case)
args = parser.parse_args()
if not args.input_fname:
args.input_fname = args.data_dir
if path.isdir(args.input_fname):
args.input_fname = path.join(args.input_fname, "mysim.sry")
wanted_cases = args.figures
if not wanted_cases or 'all' in wanted_cases:
wanted_cases = valid_cases
figsize = (12,8)
fig_dir = args.fig_dir
show_plots = args.show_plots
remove_beacon_from_traces = True
apply_signal_window_from_max = True
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
pickle_fname = path.join(fname_dir, 'res.pkl')
tx_fname = path.join(fname_dir, beacon.tx_fname)
# create fig_dir
if fig_dir:
os.makedirs(fig_dir, exist_ok=True)
# Read in antennas from file
_, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
_, __, txdata = beacon.read_tx_file(tx_fname)
# Read original REvent
ev = REvent(args.input_fname)
bak_ants = ev.antennas
# .. patch in our antennas
ev.antennas = antennas
##
## Setup grid
##
X = 400
zgr = 0 #not exact
dXref = atm.distance_to_slant_depth(np.deg2rad(ev.zenith),750,zgr+ev.core[2])
scale2d = dXref*np.tan(np.deg2rad(2.))
scale4d = dXref*np.tan(np.deg2rad(4.))
scale02d = dXref*np.tan(np.deg2rad(0.2))
Nx, Ny = 21, 21
scales = {
'scale2d': scale2d,
'scale4d': scale4d,
'scale02d': scale02d,
}
N_plot = 30
trace_zoom_wx = 100
plot_titling = {
'no_offset': "no clock offset",
'repair_none': "unrepaired clock offset",
'repair_phases': "phase resolved clock offsets repaired",
'repair_all': "final measured clock offsets repaired"
}
# For now only implement using one freq_name
freq_names = ev.antennas[0].beacon_info.keys()
if len(freq_names) > 1:
raise NotImplementedError
freq_name = next(iter(freq_names))
# Pre remove the beacon from the traces
#
# We need to remove it here, so we do not shoot ourselves in
# the foot when changing to the various clock offsets.
#
# Note that the bandpass filter is applied only after E_AxB is
# reconstructed so we have to manipulate the original traces.
if remove_beacon_from_traces:
tx_amps = txdata['amplitudes']
tx_amps_sum = np.sum(tx_amps)
for i, ant in enumerate(ev.antennas):
beacon_phase = ant.beacon_info[freq_name]['beacon_phase']
f = ant.beacon_info[freq_name]['freq']
ampl_AxB = ant.beacon_info[freq_name]['amplitude']
calc_beacon = lib.sine_beacon(f, ev.antennas[i].t, amplitude=ampl_AxB, phase=beacon_phase)
# Split up contribution to the various polarisations
for j, amp in enumerate(tx_amps):
if j == 0:
ev.antennas[i].Ex -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 1:
ev.antennas[i].Ey -= amp*(1/tx_amps_sum)*calc_beacon
elif j == 2:
ev.antennas[i].Ez -= amp*(1/tx_amps_sum)*calc_beacon
# Subtract the beacon from E_AxB
ev.antennas[i].E_AxB -= calc_beacon
# Slice the traces to a small part around the peak
if apply_signal_window_from_max:
N_pre, N_post = 250, 250 # TODO: make this configurable
for i, ant in enumerate(ev.antennas):
# Get max idx from all the traces
# and select the strongest
max_idx = []
maxs = []
for trace in [ant.Ex, ant.Ey, ant.Ez]:
idx = np.argmax(np.abs(trace))
max_idx.append(idx)
maxs.append( np.abs(trace[idx]) )
idx = np.argmax(maxs)
max_idx = max_idx[idx]
low_idx = max(0, max_idx-N_pre)
high_idx = min(len(ant.t), max_idx+N_post)
ev.antennas[i].t = ant.t[low_idx:high_idx]
ev.antennas[i].t_AxB = ant.t_AxB[low_idx:high_idx]
ev.antennas[i].Ex = ant.Ex[low_idx:high_idx]
ev.antennas[i].Ey = ant.Ey[low_idx:high_idx]
ev.antennas[i].Ez = ant.Ez[low_idx:high_idx]
ev.antennas[i].E_AxB = ant.E_AxB[low_idx:high_idx]
## Apply polarisation and bandpass filter
rit.set_pol_and_bp(ev)
# backup antenna times
backup_antenna_t = [ ant.t for ant in ev.antennas ]
backup_antenna_t_AxB = [ ant.t_AxB for ant in ev.antennas ]
fig = save_overlapping_traces_figure([0,0,0], ev, N_plot=1, wx=trace_zoom_wx, title_extra = plot_titling[case], fname_distinguish=f'single', fig_dir=fig_dir, figsize=figsize)
plt.close(fig)
with joblib.parallel_backend("loky"):
for case in wanted_cases:
print(f"Starting {case} figure")
# Repair clock offsets with the measured offsets
transl_modes = {'no_offset':'orig', 'repair_phases':'phases', 'repair_all':'all'}
if case in transl_modes:
transl_mode = transl_modes[case]
measured_offsets = beacon.read_antenna_clock_repair_offsets(antennas, mode=transl_mode, freq_name=freq_name)
else:
measured_offsets = [0]*len(ev.antennas)
for i, ant in enumerate(ev.antennas):
total_clock_offset = measured_offsets[i]
ev.antennas[i].t = backup_antenna_t[i] + total_clock_offset
ev.antennas[i].t_AxB = backup_antenna_t_AxB[i] + total_clock_offset
if i == 0:
# Specifically compare the times
print("backup time, time with measured_offset, true clock offset, measured clock offset")
print(bak_ants[i].t[0], ev.antennas[i].t[0], ev.antennas[i].attrs['clock_offset'], measured_offsets[i])
#
# Plot overlapping traces at 0,0,0
#
fig = save_overlapping_traces_figure([0,0,0], ev, N_plot=N_plot, wx=trace_zoom_wx, title_extra = plot_titling[case], fname_distinguish=f'{case}.0', fig_dir=fig_dir, figsize=figsize)
plt.close(fig)
# Measure power on grid
# and plot overlapping traces at position with highest power
for scalename, scale in scales.items():
wx, wy = scale, scale
print(f"Starting grid measurement for figure {case} with {scalename}")
xx, yy, p, maxp_loc = rit.shower_plane_slice(ev, X=X, Nx=Nx, Ny=Nx, wx=wx, wy=wy, zgr=zgr)
fig, axs = rit.slice_figure(ev, X, xx, yy, p, mode='sp', scatter_kwargs=dict(
vmax=1e5,
vmin=0,
s=150,
cmap='inferno',
# edgecolor='black',
))
suptitle = fig._suptitle.get_text()
fig.suptitle("")
axs.set_title(suptitle +"\n" +plot_titling[case])
#axs.set_aspect('equal', 'datalim')
if fig_dir:
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'.X{X}.{case}.{scalename}.pdf'))
plt.close(fig)
#
# Plot overlapping traces at highest power of each scale
#
fig = save_overlapping_traces_figure(maxp_loc, ev, N_plot=N_plot, wx=trace_zoom_wx, title_extra = plot_titling[case] + ', ' + scalename + ' best', fname_distinguish=scalename+'.best', fig_dir=fig_dir, figsize=figsize)
#
# and plot overlapping traces at two other locations
#
if True:
for dist in [ 0.5, 5, 10, 50, 100]:
# only add distance horizontally
location = maxp_loc + np.sqrt(dist*1e3)*np.array([1,1,0])
fig = save_overlapping_traces_figure(location, ev, N_plot=N_plot, wx=wx, title_extra = plot_titling[case] + ', ' + scalename + f', x + {dist}km', fname_distinguish=f'{scalename}.{dist}', fig_dir=fig_dir, figsize=figsize)
plt.close(fig)
if args.show_plots:
plt.show()

View file

@ -1,3 +0,0 @@
from .lib import *
from . import rit
from .snr import *

@ -1 +0,0 @@
Subproject commit cd903a30f99e7f640ed068c4393cd38c23c316e2

@ -1 +0,0 @@
Subproject commit a5abe360fa5210be6ab423ecf2b5f783ae45b675

View file

@ -1,219 +0,0 @@
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
from itertools import zip_longest
def phase_comparison_figure(
measured_phases,
true_phases,
plot_residuals=True,
f_beacon=None,
hist_kwargs={},
sc_kwargs={},
text_kwargs={},
colors=['blue', 'orange'],
legend_on_scatter=True,
secondary_axis='time',
fit_gaussian=False,
mean_snr=None,
return_fit_info=False,
**fig_kwargs
):
"""
Create a figure comparing measured_phase against true_phase
by both plotting the values, and the residuals.
"""
default_fig_kwargs = dict(sharex=True)
default_hist_kwargs = dict(bins='sqrt', density=False, alpha=0.8, histtype='step')
default_text_kwargs = dict(fontsize=14, verticalalignment='top')
default_sc_kwargs = dict(alpha=0.6, ls='none')
fig_kwargs = {**default_fig_kwargs, **fig_kwargs}
hist_kwargs = {**default_hist_kwargs, **hist_kwargs}
text_kwargs = {**default_text_kwargs, **text_kwargs}
sc_kwargs = {**default_sc_kwargs, **sc_kwargs}
do_hist_plot = hist_kwargs is not False
do_scatter_plot = sc_kwargs is not False
fig, axs = plt.subplots(0+do_hist_plot+do_scatter_plot, 1, **fig_kwargs)
if not hasattr(axs, '__len__'):
axs = [axs]
if f_beacon and secondary_axis in ['phase', 'time']:
phase2time = lambda x: x/(2*np.pi*f_beacon)
time2phase = lambda x: 2*np.pi*x*f_beacon
if secondary_axis == 'time':
functions = (phase2time, time2phase)
label = 'Time $\\varphi/(2\\pi f_{beac})$ [ns]'
else:
functions = (time2phase, phase2time)
label = 'Phase $2\\pi t f_{beac}$ [rad]'
secax = axs[0].secondary_xaxis('top', functions=functions)
# Histogram
fit_info = {}
if do_hist_plot:
i=0
axs[i].set_ylabel("#")
this_kwargs = dict(
ax = axs[i],
text_kwargs=text_kwargs,
hist_kwargs={**hist_kwargs, **dict(label='Measured', color=colors[0], ls='solid')},
mean_snr=mean_snr,
)
if fit_gaussian:
this_kwargs['fit_distr'] = 'gaussian'
_, fit_info = fitted_histogram_figure(
measured_phases,
**this_kwargs
)
if not plot_residuals: # also plot the true clock phases
_bins = fit_info['bins']
axs[i].hist(true_phases, color=colors[1], label='Actual', ls='dashed', **{**hist_kwargs, **dict(bins=_bins)})
# Scatter plot
if do_scatter_plot:
i=1
axs[i].set_ylabel("Antenna no.")
axs[i].plot(measured_phases, np.arange(len(measured_phases)), marker='x' if plot_residuals else '3', color=colors[0], label='Measured', **sc_kwargs)
if not plot_residuals: # also plot the true clock phases
axs[i].plot(true_phases, np.arange(len(true_phases)), marker='4', color=colors[1], label='Actual', **sc_kwargs)
if not plot_residuals and legend_on_scatter:
axs[i].legend()
fig.tight_layout()
if return_fit_info:
return fig, fit_info
return fig
def fitted_histogram_figure(
amplitudes,
fit_distr = None,
calc_chisq = True,
text_kwargs={},
hist_kwargs={},
mean_snr = None,
ax = None,
**fig_kwargs
):
"""
Create a figure showing $amplitudes$ as a histogram.
If fit_distr is a (list of) string, also fit the respective
distribution function and show the parameters on the figure.
"""
default_hist_kwargs = dict(bins='sqrt', density=False, alpha=0.8, histtype='step', label='hist')
default_text_kwargs = dict(fontsize=14, verticalalignment='top')
if isinstance(fit_distr, str):
fit_distr = [fit_distr]
hist_kwargs = {**default_hist_kwargs, **hist_kwargs}
text_kwargs = {**default_text_kwargs, **text_kwargs}
if ax is None:
fig, ax = plt.subplots(1,1, **fig_kwargs)
else:
fig = ax.get_figure()
text_kwargs['transform'] = ax.transAxes
counts, bins, _patches = ax.hist(amplitudes, **hist_kwargs)
fit_info = []
if fit_distr:
min_x = min(amplitudes)
max_x = max(amplitudes)
dx = bins[1] - bins[0]
scale = len(amplitudes) * dx
xs = np.linspace(min_x, max_x)
for distr in fit_distr:
fit_params2text_params = lambda x: x
if 'rice' == distr:
name = "Rice"
param_names = [ "$\\nu$", "$\\sigma$" ]
distr_func = stats.rice
fit_params2text_params = lambda x: (x[0]*x[1], x[1])
elif 'gaussian' == distr:
name = "Norm"
param_names = [ "$\\mu$", "$\\sigma$" ]
distr_func = stats.norm
elif 'rayleigh' == distr:
name = "Rayleigh"
param_names = [ "$\\sigma$" ]
distr_func = stats.rayleigh
fit_params2text_params = lambda x: (x[0]+x[1]/2,)
else:
raise ValueError('Unknown distribution function '+distr)
label = name +"(" + ','.join(param_names) + ')'
fit_params = distr_func.fit(amplitudes)
fit_ys = distr_func.pdf(xs, *fit_params)
ax.plot(xs, fit_ys*scale, label=label)
chisq_strs = []
if calc_chisq:
ct = np.diff(distr_func.cdf(bins, *fit_params))*np.sum(counts)
c2t = stats.chisquare(counts, ct, ddof=len(fit_params))
chisq_strs = [
f"$\\chi^2$/dof = {c2t[0]: .2g}/{len(fit_params)}"
]
# change parameters if needed
text_fit_params = fit_params2text_params(fit_params)
text_str = "\n".join(
[label]
+
[ f"{param} = {value: .2e}" for param, value in zip_longest(param_names, text_fit_params, fillvalue='?') ]
+
chisq_strs
)
this_info = {
'name': name,
'param_names': param_names,
'param_values': text_fit_params,
'text_str': text_str,
}
if chisq_strs:
this_info['chisq'] = c2t[0]
this_info['dof'] = len(fit_params)
fit_info.append(this_info)
loc = (0.02, 0.95)
ax.text(*loc, "\n\n".join([info['text_str'] for info in fit_info]), **{**text_kwargs, **dict(ha='left')})
if mean_snr:
text_str = f"$\\langle SNR \\rangle$ = {mean_snr: .1e}"
loc = (0.98, 0.95)
ax.text(*loc, text_str, **{**text_kwargs, **dict(ha='right')})
return fig, dict(fit_info=fit_info, counts=counts, bins=bins)

View file

@ -1,258 +0,0 @@
# vim: fdm=indent ts=4
"""
Library for this simulation
"""
import numpy as np
from numpy.polynomial import Polynomial
from earsim import Antenna
c_light = 3e8*1e-9 # m/ns
""" Beacon utils """
def sine_beacon(f, t, t0=0, amplitude=1, baseline=0, phase=0, peak_at_t0=0):
"""
Return a sine appropriate as a beacon
"""
baseline = baseline*np.ones_like(t)
if peak_at_t0: # add peak near t0
idx = (np.abs(t-t0)).argmin()
baseline[ max(0, idx-1):min(len(t), idx+1) ] += peak_at_t0 + amplitude
return amplitude * np.cos(2*np.pi*f*(t+t0) + phase) + baseline
def phase_mod(phase, low=np.pi):
"""
Modulo phase such that it falls within the
interval $[-low, 2\pi - low)$.
"""
return (phase + low) % (2*np.pi) - low
def distance(x1, x2):
"""
Calculate the Euclidean distance between two locations x1 and x2
"""
assert type(x1) in [Antenna]
x1 = np.array([x1.x, x1.y, x1.z])
assert type(x2) in [Antenna]
x2 = np.array([x2.x, x2.y, x2.z])
return np.sqrt( np.sum( (x1-x2)**2 ) )
def geometry_time(dist, x2=None, c_light=c_light):
if x2 is not None:
dist = distance(dist, x2)
return dist/c_light
def beacon_from(tx, rx, f, t=0, t0=0, c_light=c_light, radiate_rsq=True, amplitude=1,**kwargs):
dist = distance(tx,rx)
# suppress extra time delay from distance
if c_light is not None and np.isfinite(c_light):
t0 = t0 + dist/c_light
if radiate_rsq:
if np.isclose(dist, 0):
dist = 1
amplitude *= 1/(dist**2)
return sine_beacon(f, t, t0=t0, amplitude=amplitude,**kwargs)
def remove_antenna_geometry_phase(tx, antennas, f_beacon, measured_phases=None, c_light=c_light):
"""
Remove the geometrical phase from the measured antenna phase.
"""
if not hasattr(antennas, '__len__'):
antennas = [antennas]
if not hasattr(measured_phases, '__len__'):
measured_phases = [measured_phases]
clock_phases = np.empty( (len(antennas)) )
for i, ant in enumerate(antennas):
measured_phase = measured_phases[i]
geom_time = geometry_time(tx, ant, c_light=c_light)
geom_phase = geom_time * 2*np.pi*f_beacon
clock_phases[i] = phase_mod(measured_phase - geom_phase)
return clock_phases
""" Fourier """
def ft_corr_vectors(freqs, time):
"""
Get the cosine and sine terms for freqs at time.
Takes the outer product of freqs and time.
"""
freqtime = np.outer(freqs, time)
c_k = np.cos(2*np.pi*freqtime)
s_k = -1*np.sin(2*np.pi*freqtime)
return c_k, s_k
def direct_fourier_transform(freqs, time, samplesets_iterable):
"""
Determine the fourier transform of each sampleset in samplesets_iterable at freqs.
The samplesets are expected to have the same time vector.
Returns either a generator to return the fourier transform for each sampleset
if samplesets_iterable is a generator
or a numpy array.
"""
c_k, s_k = ft_corr_vectors(freqs, time)
if not hasattr(samplesets_iterable, '__len__') and hasattr(samplesets_iterable, '__iter__'):
# samplesets_iterable is an iterator
# return a generator containing (real, imag) amplitudes
return ( (np.dot(c_k, samples), np.dot(s_k, samples)) for samples in samplesets_iterable )
# Numpy array
return np.dot(c_k, samplesets_iterable), np.dot(s_k, samplesets_iterable)
def phase_field_from_tx(x, y, tx, f_beacon, c_light=c_light, t0=0, wrap_phase=True, return_meshgrid=True):
"""
"""
assert type(tx) in [Antenna]
xs, ys = np.meshgrid(x, y, sparse=True)
x_distances = (tx.x - xs)**2
y_distances = (tx.y - ys)**2
dist = np.sqrt( x_distances + y_distances )
phase = (dist/c_light + t0) * f_beacon*2*np.pi
if wrap_phase:
phase = phase_mod(phase)
if return_meshgrid:
return phase, (xs, ys)
else:
return phase, (np.repeat(xs, len(ys), axis=0), np.repeat(ys, len(xs[0]), axis=1))
def find_beacon_in_traces(
traces,
t_trace,
f_beacon_estimate = 50e6,
frequency_fit = False,
N_test_freqs = 5e2,
f_beacon_estimate_band = 0.01,
amp_cut = 0.8
):
"""
f_beacon_band is inclusive
traces is [trace, trace, trace, .. ]
"""
amplitudes = np.zeros(len(traces))
phases = np.zeros(len(traces))
frequencies = np.zeros(len(traces))
if frequency_fit: # fit frequency
test_freqs = f_beacon_estimate + f_beacon_estimate_band * np.linspace(-1, 1, int(N_test_freqs)+1)
ft_amp_gen = direct_fourier_transform(test_freqs, t_trace, (x for x in traces))
n_samples = len(t_trace)
for i, ft_amp in enumerate(ft_amp_gen):
real, imag = ft_amp
amps = 1/n_samples * ( real**2 + imag**2)**0.5
# find frequency peak and surrounding bins
# that are valid for the parabola fit
max_amp_idx = np.argmax(amps)
max_amp = amps[max_amp_idx]
if True:
frequencies[i] = test_freqs[max_amp_idx]
continue
valid_mask = amps >= amp_cut*max_amp
if True: # make sure not to use other peaks
lower_mask = valid_mask[0:max_amp_idx]
upper_mask = valid_mask[max_amp_idx:]
if any(lower_mask):
lower_end = np.argmin(lower_mask[::-1])
else:
lower_end = max_amp_idx
if any(upper_mask):
upper_end = np.argmin(upper_mask)
else:
upper_end = 0
valid_mask[0:(max_amp_idx - lower_end)] = False
valid_mask[(max_amp_idx + upper_end):] = False
if all(~valid_mask):
frequencies[i] = np.nan
continue
# fit Parabola
parafit = Polynomial.fit(test_freqs[valid_mask], amps[valid_mask], 2)
func = parafit.convert()
# find frequency where derivative is 0
deriv = func.deriv(1)
freq = deriv.roots()[0]
frequencies[i] = freq
else: # no frequency finding
frequencies[:] = f_beacon_estimate
n_samples = len(t_trace)
# evaluate fourier transform at freq for each trace
for i, freq in enumerate(frequencies):
if freq is np.nan:
phases[i] = np.nan
amplitudes[i] = np.nan
continue
real, imag = direct_fourier_transform(freq, t_trace, traces[i])
phases[i] = np.arctan2(imag, real)
amplitudes[i] = 2/n_samples * (real**2 + imag**2)**0.5
return frequencies, phases, amplitudes
def coherence_sum_maxima(ref_x, y, k_step=1, k_start=0, k_end=None, periodic=True):
"""
Use the maximum of a coherent sum to determine
the best number of samples to move
"""
N_samples = int( len(ref_x) )
k_end = N_samples if k_end is None or k_end > max_k else k_end
ks = np.arange(k_start, k_end, step=k_step)
maxima = np.empty(len(ks))
if periodic is False:
# prepend zeros
N_zeros = N_samples
preshift = 0 # only required for testing purposes
ref_x = np.pad(ref_x, (N_zeros-0,0), 'constant')
y = np.pad(y, (N_zeros-preshift,preshift), 'constant')
for i,k in enumerate(ks, 0):
augmented_y = np.roll(y, k)
maxima[i] = max(ref_x + augmented_y)
return ks, maxima

View file

@ -1,394 +0,0 @@
# from wappy import *
from earsim import *
from atmocal import *
import matplotlib.pyplot as plt
from scipy.signal import hilbert
from scipy import signal
from scipy.interpolate import interp1d
from scipy.optimize import curve_fit,minimize
import pandas as pd
import os
try:
from joblib import Parallel, delayed
except:
Parallel = None
delayed = lambda x: x
plt.rcParams.update({'font.size': 16})
atm = AtmoCal()
from matplotlib import cm
def set_pol_and_bp(e,low=0.03,high=0.08):
for ant in e.antennas:
E = [np.dot(e.uAxB,[ex,ey,ez]) for ex,ey,ez in zip(ant.Ex,ant.Ey,ant.Ez)]
dt = ant.t[1] -ant.t[0]
E = block_filter(E,dt,low,high)
ant.E_AxB = E
ant.t_AxB = ant.t
def pow_and_time(test_loc,ev,dt=1.0):
t_ = []
a_ = []
t_min = 1e9
t_max = -1e9
for ant in ev.antennas:
#propagate to test location
aloc = [ant.x,ant.y,ant.z]
delta,dist = atm.light_travel_time(test_loc,aloc)
delta = delta*1e9
t__ = np.subtract(ant.t_AxB,delta)
t_.append(t__)
a_.append(ant.E_AxB)
if t__[0] < t_min:
t_min = t__[0]
if t__[-1] > t_max:
t_max = t__[-1]
t_sum = np.arange(t_min+1,t_max-1,dt)
a_sum = np.zeros(len(t_sum))
#interpolation
for t_r,E_ in zip (t_,a_):
f = interp1d(t_r,E_,assume_sorted=True,bounds_error=False,fill_value=0.)
a_int = f(t_sum)
a_sum = np.add(a_sum,a_int)
if len(a_sum) != 0:
P = np.sum(np.square(np.absolute(np.fft.fft(a_sum))))
# normalise P with the length of the traces
P = P/( t_sum[-1] - t_sum[0])
else:
print("ERROR, a_sum lenght = 0",
"tmin ",t_min,
"t_max ",t_max,
"dt",dt)
P = 0
return P,t_,a_,a_sum,t_sum
def shower_axis_slice(e,Xb=200,Xe=1200,dX=2,zgr=0):
zgr = zgr + e.core[2]
N = int((Xe-Xb)/dX)
Xs = np.array(np.linspace(Xb,Xe,N+1))
ds = np.array([atm.distance_to_slant_depth(np.deg2rad(e.zenith),X,zgr) for X in Xs])
locs = []
for d_ in ds:
xc = np.sin(np.deg2rad(e.zenith))*np.cos(np.deg2rad(e.azimuth))* d_
yc = np.sin(np.deg2rad(e.zenith))*np.sin(np.deg2rad(e.azimuth))* d_
zc = np.cos(np.deg2rad(e.zenith))* d_
locs.append([xc,yc,zc])
p = []
for loc in locs:
P,t_,pulses_,wav,twav = pow_and_time(loc,e)
p.append(P)
p = np.asanyarray(p)
return ds,Xs,locs,p
def shower_plane_slice(e,X=750.,Nx=10,Ny=10,wx=1e3,wy=1e3,xoff=0,yoff=0,zgr=0,n_jobs=None):
zgr = zgr + e.core[2]
dX = atm.distance_to_slant_depth(np.deg2rad(e.zenith),X,zgr)
x = np.linspace(-wx,wx,Nx)
y = np.linspace(-wy,wy,Ny)
def loop_func(x_, y_, xoff=xoff, yoff=yoff):
loc = (x_+xoff)* e.uAxB + (y_+yoff)*e.uAxAxB + dX *e.uA
P,t_,pulses_,wav,twav = pow_and_time(loc,e)
return x_+xoff, y_+yoff, P, loc
res = ( delayed(loop_func)(x_, y_) for x_ in x for y_ in y)
if Parallel:
#if n_jobs is None change with `with parallel_backend`
res = Parallel(n_jobs=n_jobs)(res)
# unpack loop results
xx, yy, p, locs = zip(*res)
xx = np.asarray(xx)
yy = np.asarray(yy)
p = np.asanyarray(p)
return xx,yy,p,locs[np.argmax(p)]
def slice_figure(e,X,xx,yy,p,mode='horizontal', scatter_kwargs={}, colorbar_kwargs={'label':'Power'}):
scatter_kwargs = {
**dict(
cmap='Spectral_r',
alpha=0.9,
s=30
),
**scatter_kwargs
}
fig, axs = plt.subplots(1,figsize=(10,8))
fig.suptitle(r'E = %.1f EeV, $\theta$ = %.1f$^\circ$, $\phi$ = %.1f$^\circ$ X = %.f'%(e.energy,e.zenith,e.azimuth,X))
sc = axs.scatter(xx/1e3,yy/1e3,c=p,**scatter_kwargs)
fig.colorbar(sc,ax=axs, **colorbar_kwargs)
zgr = 0 + e.core[2]
dX = atm.distance_to_slant_depth(np.deg2rad(e.zenith),X,zgr)
xc = np.sin(np.deg2rad(e.zenith))*np.cos(np.deg2rad(e.azimuth))* dX
yc = np.sin(np.deg2rad(e.zenith))*np.sin(np.deg2rad(e.azimuth))* dX
if mode == 'horizontal':
axs.plot(xc/1e3,yc/1e3,'r+',ms=30)
axs.set_xlabel('x (km)')
axs.set_ylabel('y (km)')
elif mode == "sp":
axs.plot(0,0,'r+',ms=30)
axs.set_xlabel('-v x B (km)')
axs.set_ylabel(' vxvxB (km)')
im = np.argmax(p)
axs.plot(xx[im]/1e3,yy[im]/1e3,'bx',ms=30)
fig.tight_layout()
return fig,axs
def dist_to_line(xp,core,u):
xp = np.array(xp)
xp_core = xp-core
c2 = np.dot(xp_core,xp_core)
a2 = np.dot((np.dot(xp_core,u)*u),(np.dot(xp_core,u)*u))
d = (np.abs(c2 - a2))**0.5
return d
def dist_to_line_sum(param,data,weights):
#distance line point: a = xp-core is D= | (a)^2-(a dot n)n |
#where ux is direction of line and x0 is a point in the line (like t = 0)
x0 = param[0]
y0 = param[1]
theta = param[2]
phi = param[3]
core = np.array([x0, y0, 0.])
u = np.array([np.cos(phi)*np.sin(theta),np.sin(phi)*np.sin(theta),np.cos(theta)])
dsum = 0
for xp,w in zip(data,weights):
dsum += dist_to_line(xp,core,u)*w**2
# print('%.2e %.2e %.2e %.2e %.2e'%(x0,y0,theta,phi,dsum))
return dsum/len(data)
def get_axis_points(e,savefig=True,path="",zgr=0,Xlow=300, Xhigh=1000, N_X=15, n_jobs=None):
Xsteps = np.linspace(Xlow, Xhigh, N_X)
zgr=zgr+e.core[2] #not exact
dXref = atm.distance_to_slant_depth(np.deg2rad(e.zenith),750,zgr)
scale2d = dXref*np.tan(np.deg2rad(2.))
scale4d = dXref*np.tan(np.deg2rad(4.))
scale0_2d=dXref*np.tan(np.deg2rad(0.2))
def loop_func(X):
print("Starting", X)
x,y,p,loc_max = shower_plane_slice(e,X,21,21,scale2d,scale2d)
if savefig:
fig,axs = slice_figure(e,X,x,y,p,'sp')
fig.savefig(path+'X%d_a.pdf'%(X))
plt.close(fig)
im = np.argmax(p)
if np.abs(x[im]) == np.max(x) or np.abs(y[im]) == (np.max(y)):
x,y,p,loc_max = shower_plane_slice(e,X,21,21,scale4d,scale4d)
if savefig:
fig,axs = slice_figure(e,X,x,y,p,'sp')
fig.savefig(path+'X%d_c.pdf'%(X))
plt.close(fig)
im = np.argmax(p)
x,y,p,loc_max = shower_plane_slice(e,X,21,21,scale0_2d,scale0_2d,x[im],y[im])
if savefig:
fig,axs = slice_figure(e,X,x,y,p,'sp')
fig.savefig(path+'X%d_b.pdf'%(X))
plt.close(fig)
print("Finished", X)
return np.max(p), loc_max
res = (delayed(loop_func)(X) for X in Xsteps)
if Parallel:
#if n_jobs is None change with `with parallel_backend`
res = Parallel(n_jobs=n_jobs)(res)
# unpack loop results
max_vals, axis_points = zip(*res)
return Xsteps,axis_points,max_vals
def fit_track(e,axis_points,vals,nscale=1e0):
weights = vals/np.max(vals)
data=axis_points[:]
data = [d/nscale for d in data] #km, to have more comparable step sizes
x0=[0,0,np.deg2rad(e.zenith),np.deg2rad(e.azimuth)]
res = minimize(dist_to_line_sum,args=(data,weights),x0=x0)
zen_r = np.rad2deg(res.x[2])
azi_r = np.rad2deg(res.x[3])
print(res,zen_r,e.zenith,azi_r,e.azimuth)
return zen_r,azi_r,[res.x[0]*nscale,res.x[1]*nscale,0]
def update_event(e,core,theta,phi,axp=None):
#recalculate
e.zenith = theta
e.azimuth = phi
theta = np.deg2rad(theta)
phi = np.deg2rad(phi)
e.core = e.core+core
e.uA = np.array([np.cos(phi)*np.sin(theta),np.sin(phi)*np.sin(theta),np.cos(theta)])
e.uAxB = np.cross(e.uA,e.uB)
e.uAxB = e.uAxB/(np.dot(e.uAxB,e.uAxB))**0.5
e.uAxAxB = np.cross(e.uA,e.uAxB)
#antenna position
for a in e.antennas:
a.x -= core[0]
a.y -= core[1]
a.z -= core[2]
if axp != None:
for ap in axp:
ap[0] -= core[0]
ap[1] -= core[1]
ap[2] -= core[2]
def longitudinal_figure(dist,Xs,p,mode='grammage'):
fig, axs = plt.subplots(1,figsize=(6,5))
if mode=='grammage':
axs.plot(Xs,p/np.max(p),'o-')
axs.set_xlabel('X (g/cm$^2$)')
if mode=='distance':
axs.plot(dist/1e3,p/np.max(p),'o-')
axs.set_xlabel('distance from ground (km)')
axs.grid()
fig.tight_layout()
return fig
def time_residuals(e,tlable=True):
ds,tp,nsum,ssum,swidth,azi,x,y,sid = lateral_parameters(e,True,[0,0,0])
fig, axs = plt.subplots(1,figsize=(6,5),sharex=True)
tp = tp-np.min(tp)
cut_outlier = ~((ds<200)&(tp > 10))
axs.plot(ds,tp,'o')
if tlable:
for d,t,s in zip(ds,tp,sid):
plt.text(d,t,s)
# axs.text(ds,tp,sid)
axs.set_xlabel('distance (m)')
axs.set_ylabel('$\Delta t (ns)$')
axs.grid()
z = np.polyfit(ds[cut_outlier],tp[cut_outlier],3)
pfit = np.poly1d(z)
xfit = np.linspace(np.min(ds),np.max(ds),100)
yfit = pfit(xfit)
tres = tp - pfit(ds)
sigma =np.std(tres[cut_outlier])
axs.plot(xfit,yfit,label=r'pol3 fit, $\sigma=%.2f$ (ns)'%(sigma))
axs.legend()
fig.tight_layout()
return fig,tres
def figure_3D(axis_points,max_vals,zen,azi,core,res = 0):
fig = plt.figure(figsize=(5,9))
# fig, axs = plt.subplots(1,2,figsize=(12,8))
ax = fig.add_subplot(2,1,1,projection='3d')
xp = [ap[0]/1e3 for ap in axis_points]
yp = [ap[1]/1e3 for ap in axis_points]
zp = [ap[2]/1e3 for ap in axis_points]
max_vals = np.asarray(max_vals)
ax.scatter(xp, yp, zp,c=max_vals,s=150*(max_vals/np.max(max_vals))**2,cmap='Spectral_r')
ax = fig.add_subplot(2,1,2)
core = np.array(core)
theta = np.deg2rad(zen)
phi = np.deg2rad(azi)
u = np.array([np.cos(phi)*np.sin(theta),np.sin(phi)*np.sin(theta),np.cos(theta)])
residuals = [dist_to_line(ap,core,u) for ap in axis_points]
dist = [np.sum((ap-core)**2)**0.5 for ap in axis_points]
ax.scatter(dist,residuals,c=max_vals,cmap='Spectral_r')
ax.grid()
# ax.plot(xl,yl,zl,'-')
# ax.set_zlim(0,18)
# ax.view_init(15, 10)
fig.tight_layout()
if res != 0:
res.track_dis.append(dist)
res.track_res.append(residuals)
res.track_val.append(max_vals)
return fig
class RITResult():
"""docstring for RITResult."""
def __init__(self):
super(RITResult, self).__init__()
self.xmax_rit = []
self.xmax = []
self.profile_rit = []
self.dX = []
self.dl = []
self.zenith_ini = []
self.azimuth_ini = []
self.core_ini = []
self.dcore_rec = []
self.zenith_rec = []
self.azimuth_rec = []
self.index = []
self.isMC = []
self.track_dis = []
self.track_res =[]
self.track_val =[]
self.station_ids =[]
self.station_x =[]
self.station_y =[]
self.station_z =[]
self.station_maxE = []
self.has_pulse = []
def fill_stations_propeties(e,res):
x = np.array([a.x for a in e.antennas])
y = np.array([a.y for a in e.antennas])
z = np.array([a.z for a in e.antennas])
ids = [a.name for a in e.antennas]
maxE = np.array([np.max(a.E_AxB) for a in e.antennas])
#has_pulse = np.array([np.max(a.has_pulse) for a in e.antennas])
res.station_x.append(x)
res.station_y.append(y)
res.station_z.append(z)
res.station_ids.append(ids)
#res.has_pulse.append(has_pulse)
def reconstruction(e,outfile='', slice_outdir=None, Xlow=300, Xhigh=1000, N_X=15, disable_pol_and_bp=False):
res = RITResult()
res.isMC.append(True)
res.zenith_ini.append(e.zenith)
res.azimuth_ini.append(e.azimuth)
res.core_ini.append(e.core)
if not disable_pol_and_bp:
set_pol_and_bp(e, 0.03, 0.08)
#only use signal that have a signal in data
fill_stations_propeties(e,res)
Xs,axis_points,max_vals = get_axis_points(e,savefig=(slice_outdir is not None), path=slice_outdir, Xlow=Xlow, Xhigh=Xhigh, N_X=N_X)
zen,azi,core = fit_track(e,axis_points,max_vals,1e2)
fig = figure_3D(axis_points,max_vals,zen,azi,core,res)
fig.savefig(outfile)
update_event(e,core,zen,azi)
ds,Xs,locs,p = shower_axis_slice(e)
#result
res.dX.append(Xs)
res.dl.append(ds)
res.profile_rit.append(p)
res.xmax_rit.append(Xs[np.argmax(p)])
res.azimuth_rec.append(e.azimuth)
res.zenith_rec.append(e.zenith)
res.dcore_rec.append(core)
return res
if __name__ == "__main__":
file = '../ZH_airshower/mysim.sry'
ev = REvent(file)
set_pol_and_bp(ev)
X = 750
dXref = atm.distance_to_slant_depth(np.deg2rad(ev.zenith),X,0)
scale2d = dXref*np.tan(np.deg2rad(2.))
xx,yy,p,km= shower_plane_slice(ev,X,21,21,scale2d,scale2d)
slice_figure(ev,X,xx,yy,p,mode='sp')
#plt.scatter(xx,yy,c=p)
#plt.colorbar()
plt.show()

View file

@ -1,97 +0,0 @@
import numpy as np
from collections import namedtuple
from lib import direct_fourier_transform as dtft
import matplotlib.pyplot as plt # for debug plotting
passband = namedtuple("passband", ['low', 'high'], defaults=[0, np.inf])
def get_freq_spec(val,dt):
"""From earsim/tools.py"""
fval = np.fft.fft(val)[:len(val)//2]
freq = np.fft.fftfreq(len(val),dt)[:len(val)//2]
return fval, freq
def bandpass_samples(samples, samplerate, band=passband()):
"""
Bandpass the samples with this passband.
This is a hard filter.
"""
fft, freqs = get_freq_spec(samples, samplerate)
fft[ ~ self.freq_mask(freqs) ] = 0
return np.fft.irfft(fft)
def bandpass_mask(freqs, band=passband()):
low_pass = abs(freqs) <= band[1]
high_pass = abs(freqs) >= band[0]
return low_pass & high_pass
def bandpower(samples, samplerate=1, band=passband(), normalise_bandsize=True, debug_ax=False):
bins = 0
fft, freqs = get_freq_spec(samples, 1/samplerate)
bandmask = [False]*len(freqs)
if band[1] is None:
# Only a single frequency given
# use a DTFT for finding the power
time = np.arange(0, len(samples), 1/samplerate)
real, imag = dtft(band[0], time, samples)
power = np.sum(np.abs(real**2 + imag**2))
else:
bandmask = bandpass_mask(freqs, band=band)
if normalise_bandsize:
bins = np.count_nonzero(bandmask, axis=-1)
else:
bins = 1
bins = max(1, bins)
power = 1/bins * np.sum(np.abs(fft[bandmask])**2)
# Prepare plotting variables if an Axes is supplied
if debug_ax:
if any(bandmask):
min_f, max_f = min(freqs[bandmask]), max(freqs[bandmask])
else:
min_f, max_f = 0, 0
if band[1] is None:
min_f, max_f = band[0], band[0]
if debug_ax is True:
debug_ax = plt.gca()
l = debug_ax.plot(freqs, np.abs(fft), alpha=0.9)
amp = np.sqrt(power)
if min_f != max_f:
debug_ax.plot( [min_f, max_f], [amp, amp], alpha=0.7, color=l[0].get_color(), ls='dashed')
debug_ax.axvspan(min_f, max_f, color=l[0].get_color(), alpha=0.2)
else:
debug_ax.plot( min_f, amp, '4', alpha=0.7, color=l[0].get_color(), ms=10)
return power
def signal_to_noise(samples, noise, samplerate=1, signal_band=passband(), noise_band=None, debug_ax=False):
if noise_band is None:
noise_band = signal_band
if noise is None:
noise = samples
if debug_ax is True:
debug_ax = plt.gca()
noise_power = bandpower(noise, samplerate, noise_band, debug_ax=debug_ax)
signal_power = bandpower(samples, samplerate, signal_band, debug_ax=debug_ax)
return (signal_power/noise_power)**0.5

View file

@ -1,108 +0,0 @@
#!/usr/bin/env python3
"""
Test the functions in lib concerning
beacon generation and phase measuring
work correctly together.
"""
import matplotlib.pyplot as plt
import numpy as np
import lib
seed = 12345
dt = 1 # ns
frequency = 52.12345e-3 # GHz
N = 5e2
t_clock = 0
beacon_amplitude = 1
t = np.arange(0, 10*int(1e3), dt, dtype=float)
rng = np.random.default_rng(seed)
# Vary both the base time and the phase
t_extra = 0
phase_res = np.zeros(int(N))
amp_res = np.zeros(int(N))
for i in range(int(N)):
# Change timebase
t -= t_extra
t_extra = (2*rng.uniform(size=1) - 1) *1e3
t += t_extra
# Randomly phased beacon
phase = lib.phase_mod(np.pi*(2*rng.uniform(size=1) -1)) # rad
beacon = beacon_amplitude * lib.sine_beacon(frequency, t, t0=0, phase=phase, peak_at_t0=False)
if False: # blank part of the beacon
blank_low, blank_high = int(0.2*len(t)), int(0.4*len(t))
t_mask = np.ones(len(t), dtype=bool)
t_mask[blank_low:blank_high] = False
t = t[t_mask]
beacon = beacon[t_mask]
# Introduce clock errors
t += t_clock
_, measured_phases, measured_amplitudes = lib.find_beacon_in_traces([beacon], t, frequency, frequency_fit=False)
t -= t_clock
# Save residuals
phase_res[i] = lib.phase_mod(measured_phases[0] - phase)
amp_res[i] = measured_amplitudes[0] - beacon_amplitude
###
## Present figures
###
phase2time = lambda x: x/(2*np.pi*frequency)
time2phase = lambda x: 2*np.pi*x*frequency
fig, ax = plt.subplots()
ax.set_title("Sine beacon phase determination\nwith random time shifts")
ax.set_xlabel("$\\varphi_{meas} - \\varphi_{true}$ [rad]")
ax.set_ylabel("#")
ax.hist(phase_res, bins='sqrt')
if True:
ax.axvline( -1*lib.phase_mod(t_clock*frequency*2*np.pi), color='red', lw=5, alpha=0.8, label='true t_clock')
if True:
secax = ax.secondary_xaxis(1.0, functions=(phase2time, time2phase))
secax.set_xlabel('Time [ns]')
ax.legend(title='N={:.1e}'.format(len(t)))
phase_xlims = ax.get_xlim()
fig.tight_layout()
amp_xlims = None
if True:
fig, ax = plt.subplots()
ax.set_title("Amplitude")
ax.set_xlabel("$A_{meas} - 1$")
ax.set_ylabel("#")
ax.legend(title='N={:.1e}'.format(len(t)))
ax.hist(amp_res, bins='sqrt')
fig.tight_layout()
amp_xlims = ax.get_xlim()
if True:
fig, ax = plt.subplots()
ax.grid()
ax.set_xlabel("Phase Res [rad]")
ax.set_ylabel("Amplitude Res")
sc = ax.scatter( phase_res, amp_res )
#fig.colorbar(sc, ax=ax)
ax.set_xlim(phase_xlims)
if amp_xlims is not None:
ax.set_ylim(amp_xlims)
if True:
ax.axvline( -1*lib.phase_mod(t_clock*frequency*2*np.pi), color='red', lw=5, alpha=0.8, label='true t_clock')
if True:
secax = ax.secondary_xaxis(1.0, functions=(phase2time, time2phase))
secax.set_xlabel('Time [ns]')
ax.legend(title='N={:.1e}'.format(len(t)))
fig.tight_layout()
plt.show()

View file

@ -1,119 +0,0 @@
#!/usr/bin/env python3
"""
Test the functions in lib concerning
beacon generation and removing the geometrical phase
work correctly together.
"""
import matplotlib.pyplot as plt
import numpy as np
from earsim import Antenna
import lib
seed = 12345
dt = 1 # ns
frequency = 52.12345e-3 # GHz
N = 5e2
t_clock = 0
beacon_amplitude = 1
c_light = lib.c_light
t = np.arange(0, 10*int(1e3), dt, dtype=float)
rng = np.random.default_rng(seed)
tx = Antenna(x=0,y=0,z=0,name='tx')
rx = Antenna(x=tx.x,y=tx.y,z=tx.z,name='rx')
# Vary both the base time and the phase
t_extra = 0
phase_res = np.zeros(int(N))
amp_res = np.zeros(int(N))
for i in range(int(N)):
# Change timebase
t -= t_extra
t_extra = (2*rng.uniform(size=1) - 1) *1e3
t += t_extra
# Randomise Antenna Location
if True:
rx.x, rx.y, rx.z = (2*rng.uniform(size=3) -1) * 1e4
# Randomly phased beacon
# at Antenna
phase = lib.phase_mod(np.pi*(2*rng.uniform(size=1) -1)) # rad
beacon = beacon_amplitude * lib.beacon_from(tx, rx, frequency, t, t0=0, phase=phase, peak_at_t0=False, c_light=c_light, radiate_rsq=False)
if False: # blank part of the beacon
blank_low, blank_high = int(0.2*len(t)), int(0.4*len(t))
t_mask = np.ones(len(t), dtype=bool)
t_mask[blank_low:blank_high] = False
t = t[t_mask]
beacon = beacon[t_mask]
# Introduce clock errors
t += t_clock
_, measured_phases, measured_amplitudes = lib.find_beacon_in_traces([beacon], t, frequency, frequency_fit=False)
t -= t_clock
calculated_phases = lib.remove_antenna_geometry_phase(tx, rx, frequency, measured_phases, c_light=c_light)
# Save residuals
phase_res[i] = lib.phase_mod(calculated_phases[0] - phase)
amp_res[i] = measured_amplitudes[0] - beacon_amplitude
###
## Present figures
###
phase2time = lambda x: x/(2*np.pi*frequency)
time2phase = lambda x: 2*np.pi*x*frequency
fig, ax = plt.subplots()
ax.set_title("Measured phase at Antenna - geometrical phase")
ax.set_xlabel("$\\varphi_{meas} - \\varphi_{true}$ [rad]")
ax.set_ylabel("#")
ax.hist(phase_res, bins='sqrt')
if True:
ax.axvline( -1*lib.phase_mod(t_clock*frequency*2*np.pi), color='red', lw=5, alpha=0.8, label='true t_clock')
if True:
secax = ax.secondary_xaxis(1.0, functions=(phase2time, time2phase))
secax.set_xlabel('Time [ns]')
ax.legend(title='N={:.1e}'.format(len(t)))
phase_xlims = ax.get_xlim()
fig.tight_layout()
amp_xlims = None
if True:
fig, ax = plt.subplots()
ax.set_title("Amplitude")
ax.set_xlabel("$A_{meas} - 1$")
ax.set_ylabel("#")
ax.legend(title='N={:.1e}'.format(len(t)))
ax.hist(amp_res, bins='sqrt')
fig.tight_layout()
amp_xlims = ax.get_xlim()
if True:
fig, ax = plt.subplots()
ax.grid()
ax.set_xlabel("Phase Res [rad]")
ax.set_ylabel("Amplitude Res")
sc = ax.scatter( phase_res, amp_res )
#fig.colorbar(sc, ax=ax)
ax.set_xlim(phase_xlims)
if amp_xlims is not None:
ax.set_ylim(amp_xlims)
if True:
ax.axvline( -1*lib.phase_mod(t_clock*frequency*2*np.pi), color='red', lw=5, alpha=0.8, label='true t_clock')
if True:
secax = ax.secondary_xaxis(1.0, functions=(phase2time, time2phase))
secax.set_xlabel('Time [ns]')
ax.legend(title='N={:.1e}'.format(len(t)))
fig.tight_layout()
plt.show()

View file

@ -1,67 +0,0 @@
#!/usr/bin/env python3
import os
import re
import tempfile
def parse_env_file(fname):
envs = {}
env_reg = re.compile('^(?:\s*export)?\s*(.*)\s*=\s*(.*)\s*$')
with open(fname, 'r') as f:
for line in f:
if '=' not in line:
continue
m = env_reg.match(line)
envs[m.group(1)] = m.group(2)
return envs
def mutilate_figure_name(fig_fname, envs):
return fig_fname
if __name__ == "__main__":
from argparse import ArgumentParser
parser = ArgumentParser()
#parser.add_argument('-f', '--figures', nargs='*')
parser.add_argument("-d", "--directories", nargs='*')
parser.add_argument('out_dir', default='./figures', type=str)
args = parser.parse_args()
os.makedirs(args.out_dir, exist_ok=True)
#with open(args.out_file, 'w') as fp:
if True:
for d in args.directories:
d = os.path.realpath(d)
fig_dir = os.path.join(d, 'figures')
env_fname = os.path.join(d, 'env.sh')
if not os.path.exists(fig_dir):
print(f"Cannot find {fig_dir}")
continue
## parse properties from env.sh
#envs = parse_env_file(env_fname)
#print(envs, fig_dir)
for f in os.listdir(fig_dir):
fname, ext = os.path.splitext(f)
dname = os.path.basename(d)
if ext not in ['.pdf', '.png']:
continue
link_name = fname + "_" + dname + ext
target = os.path.realpath(os.path.join(fig_dir, f))
tmpLink = tempfile.mktemp(dir=args.out_dir)
os.symlink(target, tmpLink)
os.rename(tmpLink, os.path.join(args.out_dir, link_name))

View file

@ -1,39 +0,0 @@
#!/usr/bin/env python3
import os
import os.path as path
from itertools import product
baselines = [ '', 72 ]
noise_sigmas = [ 0, '1e4' ]
clock_devs = [0, 20]
trace_lengths = [ 4096 ]#, 16384 ]
for options in product(baselines, clock_devs, noise_sigmas, trace_lengths):
baseline, clk_dev, noise, trace = options
dirname = f"matrix_c{clk_dev}_b{baseline}_N{trace}_noise{noise}"
print(dirname)
# Make directory
if path.exists(dirname):
print(f"{dirname} already exists! continuing anyway..")
os.makedirs(dirname, exist_ok=True)
# Soft link clock file if available
if True:
os.makedirs(path.join(dirname, 'data'), exist_ok=True)
if not path.isfile(path.join(dirname, 'data/clocks.csv')):
os.symlink(f'../../c{clk_dev}_clocks.csv', path.join(dirname, 'data/clocks.csv'))
# Setup config.mk
with open(path.join(dirname, 'env.sh'), 'w') as fp:
template = f"""
export CLK_DEV={clk_dev}
export REF_ANTS={baseline}
export NOISE_SIGMA={noise}
export TRACE_N={trace}
"""
fp.write(template)

View file

@ -1,217 +0,0 @@
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
import json
from scipy import special
# Mimic import aa_generate_beacon as beacon
beacon = lambda: None
def read_snr_file(fname):
with open(fname, 'r') as fp:
return json.load(fp)
def read_tx_file(fname):
with open(fname, 'r') as fp:
data = json.load(fp)
f_beacon = data['f_beacon']
tx = data['tx']
del data['f_beacon']
del data['tx']
return tx, f_beacon, data
beacon.snr_fname = 'snr.json'
beacon.tx_fname = 'tx.json'
beacon.read_snr_file = read_snr_file
beacon.read_tx_file = read_tx_file
def read_snr_directories(data_directories, tx_fname=beacon.tx_fname, snr_fname=beacon.snr_fname, phase_res_fname='phase_time_residuals.json'):
# Read in snr
snrs = np.zeros(len(data_directories), dtype=float)
time_residuals = np.zeros( (len(snrs), 2), dtype=float)
tx, f_beacon, _ = beacon.read_tx_file(path.join(data_directories[0], tx_fname))
for i, data_dir in enumerate(data_directories):
# Read SNR file
snr_data = read_snr_file(path.join(data_dir, snr_fname))
# Open antennas file
with open(path.join(data_dir, phase_res_fname), 'r') as fp:
time_res_data = json.load(fp)
snrs[i] = snr_data['mean']
time_residuals[i] = time_res_data['mean'], time_res_data['std']
return f_beacon, snrs, time_residuals
## Math
def expectation(x, pdfx):
dx = x[1] - x[0]
return np.sum(x*pdfx*dx)
def variance(x, pdfx):
mu = expectation(x, pdfx)
dx = x[1] - x[0]
return np.sum(x**2 *pdfx*dx) - mu**2
def phase_distribution(theta, sigma, s):
k = s/sigma
ct = np.cos(theta)
st = np.sin(theta)
pipi = 2*np.pi
return (np.exp(-k**2/2)/pipi) \
+ (
(pipi**-0.5)*k*np.exp(-(k*st)**2/2)
* ((1.+special.erf(k*ct*2**-0.5))*ct/2)
)
def phase_distribution_gauss(theta, sigma, s):
k = s/sigma
return 1/np.sqrt(2*np.pi) * k *np.exp( -(k*theta)**2/2)
if __name__ == "__main__":
from os import path
import sys
import os
import matplotlib
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from argparse import ArgumentParser
parser = ArgumentParser()
figsize = (12,8)
# Multiple Data Directories
parser.add_argument("-d", dest='data_directories', default=[], nargs='*')
# Whether to show plots
group1 = parser.add_mutually_exclusive_group(required=False)
group1.add_argument('--show-plots', action='store_true', default=True, help='Default: %(default)s')
group1.add_argument('--no-show-plots', dest='show-plots', action='store_false')
# Figures directory
parser.add_argument('--fig-dir', type=str, default='./figures', help='Set None to disable saving figures. (Default: %(default)s)')
args = parser.parse_args()
show_plots = args.show_plots
if not args.data_directories:
parser.error("At least one data directory should be specified.")
f_beacon, snrs, time_residuals = read_snr_directories(args.data_directories)
phase2time = lambda x: x/(2*np.pi*f_beacon)
time2phase = lambda x: 2*np.pi*x*f_beacon
fig, ax = plt.subplots(figsize=(8,6))
ax.set_title("Beacon ({:.2f}MHz) Simulation".format(f_beacon*1e3))
ax.set_xlabel('Signal to Noise ratio')
ax.set_ylabel('Time Accuracy $\\sigma(t)$ [ns]')
# group per directories per N
if True:
import re
dirdict = {}
N_re = re.compile(r'_N(\d+)_')
for d in args.data_directories:
m = N_re.findall(d)[0]
if m not in dirdict:
dirdict[m] = []
dirdict[m].append(d)
dirlist = dirdict.values()
# plot data directories as one group
else:
dirlist = [args.data_directories]
for dirlist in dirlist:
f_beacon, snrs, time_residuals = read_snr_directories(dirlist)
# plot data
ax.plot(snrs*np.sqrt(np.pi/2), phase2time(time_residuals[:,1]), ls='none', marker='o')
# Add secondary axis
if True:
if False and secondary_axis == 'time':
functions = (phase2time, time2phase)
label = 'Time Accuracy $\\sigma_t\\varphi/(2\\pi f_{beac})$ [ns]'
else:
functions = (time2phase, phase2time)
label = 'Phase Accuracy $\\sigma_\\varphi$ [rad]'
secax = ax.secondary_yaxis('right', functions=functions)
secax.set_ylabel(label)
# logscales
if True:
ax.set_xscale('log')
ax.set_yscale('log')
# plot phasor snr
if True:
thetas = np.linspace(-np.pi, np.pi, 500)
sigma = 1
_snr_min = min(10, min(snrs))
_snr_max = min(100, max(snrs))
if ax.get_xscale() == 'log': #log
_snrs = np.logspace(np.log10(_snr_min), np.log10(_snr_max))
else: #linear
_snrs = np.linspace(_snr_min, _snr_max)
# Phasor Rice
phasor_pdfs = np.array([phase_distribution(thetas, sigma, s) for s in _snrs ])
phasor_sigma = np.sqrt(np.array([variance(thetas, pdf) for pdf in phasor_pdfs]))
ax.plot(_snrs, phase2time(phasor_sigma), label='Rice')
if True: # plot a pdf
fig2, ax2 = plt.subplots()
for idx in [0, len(_snrs)//4, len(_snrs)//2, -1]:
ax2.plot(thetas, phasor_pdfs[idx], label='s = {:.1f}'.format(_snrs[idx]))
ax2.set_xlabel('$\\theta$')
ax2.set_ylabel('$p(\\theta)$')
ax2.legend()
# Gauss Phasor
phasor_pdfs = np.array([phase_distribution_gauss(thetas, sigma, s) for s in _snrs ])
phasor_sigma = np.sqrt(np.array([variance(thetas, pdf) for pdf in phasor_pdfs]))
ax.plot(_snrs, phase2time(phasor_sigma), label='Gauss')
ax.legend()
# Set limit on x values
if not True or ax.get_xscale() != 'log':
ax.set_xlim(0, 100)
if args.fig_dir:
fig.tight_layout()
fig.savefig(path.join(args.fig_dir, "time_res_vs_snr.pdf"))
if args.show_plots:
plt.show()

View file

@ -1,28 +0,0 @@
#!/bin/bash
MATRIXDIR='/scratch/etdeboone/matrix'
MATRIX_ELS="${@:-$MATRIXDIR/matrix*/}"
BASE=$(realpath ../base)
MAKE="make -C ${BASE} "
for d in "$MATRIX_ELS" ;
do
echo "Entering $d"
pushd "$d";
# Source constants
. env.sh
export FIG_DIR=$(realpath "./figures")
export DATA_DIR=$(realpath "./data")
mkdir -p $FIG_DIR $DATA_DIR
$MAKE beacon;
$MAKE clocks;
$MAKE phases;
$MAKE findks;
#$MAKE reconstruct;
echo "Popping"
popd
done

View file

@ -1,122 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
import h5py
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # required for projection='3d' on old matplotliblib versions
import numpy as np
from copy import deepcopy
import aa_generate_beacon as beacon
import lib
from lib import rit
from earsim import REvent, Antenna
from atmocal import AtmoCal
import pickle
if __name__ == "__main__":
from os import path
import sys
fname = "ZH_airshower/mysim.sry"
dirname = '/scratch/etdeboone/reconstructions/'+ (sys.argv[2]+'/' if 2 in sys.argv else '')
print("Dirname:", dirname)
####
fname_dir = path.dirname(fname)
max_clock_offset = None
if True: # use the original files
ev = REvent(fname)
max_clock_offset = int(sys.argv[1]) if len(sys.argv) > 1 else 5# ns
# randomise timing for antenna copies
seed = 12345
rng = np.random.default_rng(seed)
dirname += f'rand{max_clock_offset}ns'
else:
raise NotImplementedError
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
if not path.isfile(antennas_fname):
print("Antenna file cannot be found, did you try generating a beacon?")
sys.exit(1)
# Full reconstruction?
if False:
print("Full reconstruction")
if max_clock_offset:
print(f"Randomising timing: {max_clock_offset}")
clock_offsets = max_clock_offset * (2*rng.uniform(size=len(ev.antennas)) - 1)
for i, ant in enumerate(ev.antennas):
ev.antennas[i].t += clock_offsets[i]
else:
print(f"Not randomising timing")
with open(dirname + '/res.pkl', 'wb') as fp:
res = rit.reconstruction(ev, outfile=dirname+'/fig.pdf', slice_outdir=dirname+'/', Xlow=100, N_X=23, Xhigh=1200)
pickle.dump(res, fp)
sys.exit(0)
# Only a single slice
else:
print("Shower plane reconstruction")
# atm = AtmoCal()
# X = 750
# rit.set_pol_and_bp(ev)
# dXref = atm.distance_to_slant_depth(np.deg2rad(ev.zenith),750,0)
# scale2d = dXref*np.tan(np.deg2rad(2.))
if max_clock_offset is None:
print(" + randomised timing")
ants_copy = deepcopy(ev.antennas)
clock_offsets = max_clock_offset * (2*rng.uniform(size=len(ants_copy)) - 1)
for i, ant in enumerate(ants_copy):
ant.t -= clock_offsets[i]
fig, axs = plt.subplots(1,1+(max_clock_offset is not None), sharex=True, sharey=True)
fig.suptitle(f"Reconstructed Shower plane slice at X={X}")
if max_clock_offset is None:
axs = [ax]
for ax in axs:
ax.set_xlabel("-v x B (m)")
ax.set_ylabel(" vxvxB (m)")
if max_clock_offset is not None:
axs[0].set_title("Simulated timing")
axs[1].set_title(f"Randomised timing [0, {max_clock_offset}]")
try:
for i, ax in enumerate(axs):
# modify timing
if i == 1:
ev.antennas = ants_copy
xx,yy,p,km= rit.shower_plane_slice(ev,X,21,21,scale2d,scale2d)
if i == 0:
vmax, vmin = max(p), min(p)
sc = ax.scatter(xx,yy,c=p, vmax=vmax, vmin=vmin)
# Plot centers
im = np.argmax(p)
ax.plot(0, 0,'r+',ms=30)
ax.plot(xx[im],yy[im],'bx',ms=30)
fig.colorbar(sc, ax=axs[0])
# Always make sure to show the plot
except:
if True:
fig.savefig(dirname + f"/shower_plane_slice_X{X}_T{max_clock_offset}_exception.pdf")
plt.show()
if True:
fig.savefig(dirname + f"/shower_plane_slice_X{X}_T{max_clock_offset}.pdf")
plt.show()

View file

@ -1,32 +0,0 @@
"""
Some preconfigured ArgumentParser
"""
from argparse import ArgumentParser
def MyArgumentParser(
default_fig_dir='./figures',
default_show_plots=False,
default_data_dir='./ZH_airshower',
**kwargs):
"""
A somewhat preconfigured ArgumentParser to be shared across
multiple scripts.
Set show_plots=True to by default enable showing plots.
Likewise, set fig_dir=None to by default disable saving figures.
"""
parser = ArgumentParser(**kwargs)
# Whether to show plots
group1 = parser.add_mutually_exclusive_group(required=False)
group1.add_argument('--show-plots', action='store_true', default=default_show_plots, help='Default: %(default)s')
group1.add_argument('--no-show-plots', dest='show-plots', action='store_false')
# Data directory
parser.add_argument('--data-dir', type=str, default=default_data_dir, help='Path to Data Directory. (Default: %(default)s)')
# Figures directory
parser.add_argument('--fig-dir', type=str, default=default_fig_dir, help='Set None to disable saving figures. (Default: %(default)s)')
return parser

View file

@ -1,116 +0,0 @@
#!/usr/bin/env python3
# vim: fdm=indent ts=4
__doc__ = \
"""
Show the beacon amplitude per antenna.
"""
import numpy as np
import h5py
import matplotlib.pyplot as plt
import aa_generate_beacon as beacon
import lib
if __name__ == "__main__":
import os.path as path
fname = "ZH_airshower/mysim.sry"
plot_phase_field = True
plot_tx = False
####
fname_dir = path.dirname(fname)
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
pretitle = ""
if True:
freq_name = list(antennas[0].beacon_info.keys())[0]
beacon_frequencies = np.array([ant.beacon_info[freq_name]['freq'] for ant in antennas])
beacon_amplitudes = np.array([ant.beacon_info[freq_name]['amplitude'] for ant in antennas])
beacon_phases = lib.phase_mod(np.array([ant.beacon_info[freq_name]['beacon_phase'] for ant in antennas]))
if False and 'clock_phase' in antennas[0].beacon_info[freq_name]:
beacon_clock_phases = lib.phase_mod(np.array([ant.beacon_info[freq_name]['clock_phase'] for ant in antennas]))
else:
subtitle = " Phases from t0"
beacon_frequencies = np.array([ f_beacon for ant in antennas ])
beacon_amplitudes = np.array([ 1 for ant in antennas ])
beacon_phases = np.array([ lib.phase_mod(ant.attrs['t0']*beacon_frequencies[i]*2*np.pi) for i, ant in enumerate(antennas)])
#####
sizes = 64
if True:
vals = beacon_phases
colorlabel = '$\\varphi$'
sizes = 64*(beacon_amplitudes/np.max(beacon_amplitudes))**2
elif True: # True Phases
vals = beacon_clock_phases
colorlabel = '$\\sigma_\\varphi$'
plot_phase_field = False
plot_tx = False
else:
vals = beacon_amplitudes
colorlabel = "[$\\mu$V/m]"
x = [ a.x for a in antennas ]
y = [ a.y for a in antennas ]
#####
fig, axs = plt.subplots()
axs.set_title(pretitle + f"Beacon frequency at A0: {beacon_frequencies[0]:.3e}GHz")
axs.set_aspect('equal', 'datalim')
axs.set_xlabel('[m]')
axs.set_ylabel('[m]')
if True:
vmax, vmin = None, None
if plot_phase_field:
# underlie a calculate phase field
if True: # only fill for antennas
xs = np.linspace( np.min(x), np.max(x), 4*np.ceil(len(antennas)**0.5) -1 )
ys = np.linspace( np.min(y), np.max(y), 4*np.ceil(len(antennas)**0.5) -1)
else: # make field from halfway the transmitter
xs = np.linspace( (tx.x - np.min(x))/2, np.max(x), 500)
ys = np.linspace( (tx.y - np.min(y))/2, np.max(y), 500)
phases, (xs, ys) = lib.phase_field_from_tx(xs, ys, tx, f_beacon*1e9,return_meshgrid=False)
vmax, vmin = max(np.max(phases), np.max(vals)), min(np.min(phases), np.min(vals))
sc2 = axs.scatter(xs, ys, c=phases, alpha=0.5, zorder=-5, cmap='Spectral_r', vmin=vmin, vmax=vmax)
if False:
# do not share the same colours
fig.colorbar(sc2, ax=axs)
vmax, vmin = None, None
sc = axs.scatter(x, y, c=vals, s=sizes, cmap='Spectral_r', edgecolors='k', marker='X', vmin=vmin, vmax=vmax)
if plot_tx:
axs.plot(tx.x, tx.y, marker='X', color='k')
#for i, freq in enumerate(beacon_frequencies):
# axs.text(f"{freq:.2e}", (x[i], y[i]))
fig.colorbar(sc, ax=axs, label=colorlabel)
else:
phases, (xs, ys) = lib.phase_field_from_tx(x, y, tx, f_beacon*1e9, return_meshgrid=False)
phase_diffs = vals - lib.phase_mod(phases)
phase_diffs = lib.phase_mod(phase_diffs)
print(phases)
sc = axs.scatter(xs, ys, c=phase_diffs, s=sizes, cmap="Spectral_r")
axs.plot(tx.x, tx.y, marker='X', color='k')
fig.colorbar(sc, ax=axs, label=colorlabel)
if not True:
fig.savefig(__file__ + ".pdf")
plt.show()

View file

@ -1,184 +0,0 @@
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
import numpy.fft as ft
import aa_generate_beacon as beacon
from view_orig_ant0 import plot_antenna_geometry
import lib
from earsim import Antenna
if __name__ == "__main__":
from os import path
import sys
import matplotlib
import os
if os.name == 'posix' and "DISPLAY" not in os.environ:
matplotlib.use('Agg')
from scriptlib import MyArgumentParser
parser = MyArgumentParser()
parser.add_argument('ant_idx', default=[72], nargs='*', type=int, help='Antenna Indices')
parser.add_argument('-p', '--polarisations', choices=['x', 'y', 'z', 'b', 'AxB', 'n', 'b+n'], action='append', help='Default: x,y,z')
parser.add_argument('--geom', action='store_true', help='Make a figure containg the geometry from tx to antenna(s)')
parser.add_argument('--ft', action='store_true', help='Add FT strenghts of antenna traces')
args = parser.parse_args()
figsize = (12,8)
plot_ft_amplitude = args.ft
plot_geometry = args.geom
fig_dir = args.fig_dir
show_plots = args.show_plots
if not args.polarisations:
args.polarisations = ['x','y', 'z']
####
fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
tx_fname = path.join(fname_dir, beacon.tx_fname)
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
_, __, txdata = beacon.read_tx_file(tx_fname)
beacon_amp = np.max(txdata['amplitudes'])# mu V/m
idx = args.ant_idx
if not idx:
if not True:
idx = [0, 1, len(antennas)//2, len(antennas)//2+1, -2, -1]
elif not True:
idx = np.arange(1, 20, 2, dtype=int)
elif True:
# center 6 antennas
names = [55, 56, 57, 65, 66, 45, 46]
idx = [ i for i, ant in enumerate(antennas) if int(ant.name) in names ]
for i_fig in range(2):
name_dist=''
if i_fig == 1: #read in the raw_traces
_, __, antennas = beacon.read_beacon_hdf5(antennas_fname, traces_key='prefiltered_traces')
name_dist='.raw'
fig1, axs = plt.subplots(1+plot_ft_amplitude*1 +0*1, figsize=figsize)
if not plot_ft_amplitude:
axs = [axs]
axs[0].set_xlabel('t [ns]')
axs[0].set_ylabel('[$\mu$V/m]')
if i_fig == 1:
axs[0].set_title("UnFiltered traces")
else:
axs[0].set_title("Filtered traces")
if True:
axs[0].set_xlim(-250, 250)
if plot_ft_amplitude:
axs[1].set_xlabel('f [GHz]')
axs[1].set_ylabel('Power')
if len(axs) > 2:
axs[2].set_ylabel("Phase")
axs[2].set_xlabel('f [GHz]')
axs[2].set_ylim(-np.pi,+np.pi)
colorlist = []
for i in idx:
ant = antennas[i]
n_samples = len(ant.t)
samplerate = (ant.t[-1] - ant.t[0])/n_samples
axs[0].axvline(ant.t[0], color='k', alpha=0.5)
mydict = {}
for p in args.polarisations:
pattr = 'E'+str(p)
if p == 'b':
pattr = 'beacon'
elif p == 'n':
pattr = 'noise'
elif p == 'AxB':
pattr = 'E_AxB'
elif p =='b+n':
mydict[p] = getattr(ant,'noise') + beacon_amp*getattr(ant, 'beacon')
continue
mydict[p] = getattr(ant, pattr)
if 'b' in mydict:
mydict['b'] *= beacon_amp
for j, (direction, trace) in enumerate(mydict.items()):
l = axs[0].plot(ant.t, trace, label=f"$E_{{{direction}}}$ {ant.name}", alpha=0.7)
#if False and j == 0 and 't0' in ant.attrs:
# axs[0].axvline(ant.attrs['t0'], color=l[0].get_color(), alpha=0.5)
colorlist.append(l[0].get_color())
if not plot_ft_amplitude:
continue
fft, freqs = lib.get_freq_spec(trace, 1/samplerate)
axs[1].plot(freqs, np.abs(fft)**2, color=l[0].get_color())
if True:
cft = lib.direct_fourier_transform(f_beacon, ant.t, trace)
amp = (cft[0]**2 + cft[1]**2)
#axs[0].axhline(amp, color=l[0].get_color())
print(amp)
phase = np.arctan2(cft[0],cft[1])
axs[1].plot(f_beacon, amp, color=l[0].get_color(), marker='3', alpha=0.8, ms=30)
if len(axs) > 2:
axs[2].plot(f_beacon, phase, color=l[0].get_color(), marker='3', alpha=0.8, ms=30)
if plot_ft_amplitude:
fig1.legend(loc='center right', ncol=min(2, len(idx)))
else:
axs[0].legend(loc='upper right', ncol=min(3, len(idx)))
# Keep trace plot symmetric around 0
max_lim = max(np.abs(axs[0].get_ylim()))
axs[0].set_ylim(-max_lim, max_lim)
# Keep spectrum between 0 and 100 MHz
if len(axs) > 1:
xlims = axs[1].get_xlim()
axs[1].set_xlim(max(0, xlims[0]), min(0.1, xlims[1]))
if False: # extra zoom
axs[1].set_xlim(f_beacon - 0.01, f_beacon + 0.01)
if fig_dir:
fig1.savefig(path.join(fig_dir, path.basename(__file__) + f".trace{name_dist}.pdf"))
if plot_geometry:
if len(mydict) == 1:
geom_colorlist = colorlist
else:
# only take the colour belonging to mydict[0]
geom_colorlist = [ colorlist[len(mydict)*(i)] for i in range(len(colorlist)//len(mydict)) ]
fig2, axs2 = plt.subplots(1, figsize=figsize)
plot_antenna_geometry(antennas, ax=axs2, plot_max_values=False, color='grey', plot_names=False)
plot_antenna_geometry([ antennas[i] for i in idx], ax=axs2, colors=geom_colorlist, plot_max_values=False)
axs2.plot(tx.x, tx.y, marker='X', color='k')
axs2.set_title("Geometry with selected antennas")
if fig_dir:
fig2.savefig(path.join(fig_dir, path.basename(__file__) + f".geom.pdf"))
if show_plots:
plt.show()

View file

@ -1,98 +0,0 @@
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
def plot_antenna_Efields(antenna, ax=None, plot_Ex=True, plot_Ey=True, plot_Ez=True, label_append="",**kwargs):
"""Show waveforms from an antenna"""
if ax is None:
ax = plt.gca()
default_kwargs = dict(alpha=0.6)
kwargs = {**default_kwargs, **kwargs}
i = antenna.name
if plot_Ex:
ax.plot(antenna.t, antenna.Ex, label=f"E{{x}}{label_append}".format(x='x', i=i), **kwargs)
if plot_Ey:
ax.plot(antenna.t, antenna.Ey, label=f"E{{x}}{label_append}".format(x='y', i=i), **kwargs)
if plot_Ez:
ax.plot(antenna.t, antenna.Ez, label=f"E{{x}}{label_append}".format(x='z', i=i), **kwargs)
ax.set_xlabel("[ns]")
ax.set_ylabel("[$\mu$V/m]")
ax.set_title("Antenna {i}".format(i=i))
ax.legend()
return ax
def plot_antenna_geometry(antennas, ax=None, plot_names=True, plot_max_values=True, log_max=False, colors=None,**kwargs):
"""Show the max values at each antenna."""
default_kwargs = dict(
cmap='Spectral_r'
)
kwargs = {**default_kwargs, **kwargs}
x = [ a.x for a in antennas ]
y = [ a.y for a in antennas ]
if ax is None:
ax = plt.gca()
if plot_max_values:
max_vals = np.asarray([ np.max([np.max(a.Ex), np.max(a.Ey), np.max(a.Ez)]) for a in antennas ])
if log_max:
max_vals = np.log10(max_vals)
colors = max_vals
sc = ax.scatter(x, y, c=colors, **kwargs)
if plot_max_values:
fig = ax.get_figure()
fig.colorbar(sc, ax=ax, label="[$\mu$V/m]")
if plot_names:
[ ax.annotate(a.name, (a.x, a.y), ha='center',va='center') for a in antennas ]
ax.set_title("Maximum E field at each antenna")
ax.set_ylabel("[m]")
ax.set_xlabel("[m]")
return ax, sc
if __name__ == "__main__":
import os.path as path
from earsim import REvent
import aa_generate_beacon as beacon
fname = "ZH_airshower/mysim.sry"
i = 0
if True:
ev = REvent(fname)
antennas = ev.antennas
else:
fname_dir = path.dirname(fname)
antennas_fname = path.join(fname_dir, beacon.antennas_fname)
f_beacon, tx, antennas = beacon.read_beacon_hdf5(antennas_fname)
if True:
fig, ax1 = plt.subplots()
plot_antenna_Efields(antennas[i], ax=ax1)
if True:
fig2, ax2 = plt.subplots()
plot_antenna_geometry(antennas, ax=ax2, plot_max_values=True, plot_names=False)
ax2.set_aspect('equal', 'datalim')
plt.show()