Why is clk-phase not zero is unresolved

Merge branch 'why-is-clk-phase-not-zero-centered' into main
This commit is contained in:
Eric Teunis de Boone 2023-03-27 17:08:16 +02:00
commit 5ea4a0df17
39 changed files with 1027 additions and 308 deletions

4
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "simulations/airshower_beacon_simulation/earsim"] [submodule "simulations/airshower_beacon_simulation/earsim"]
path = simulations/airshower_beacon_simulation/lib/earsim path = airshower_beacon_simulation/lib/earsim
url = https://gitlab.com/harmscho/earsim.git url = https://gitlab.com/harmscho/earsim.git
[submodule "simulations/airshower_beacon_simulation/lib/atmocal"] [submodule "simulations/airshower_beacon_simulation/lib/atmocal"]
path = simulations/airshower_beacon_simulation/lib/atmocal path = airshower_beacon_simulation/lib/atmocal
url = https://gitlab.com/harmscho/AtmosphereCal url = https://gitlab.com/harmscho/AtmosphereCal

View file

@ -27,35 +27,56 @@ SEED ?= 12345
all: beacon clocks phases findks vary-fixes reconstruct all: beacon clocks phases findks vary-fixes reconstruct
beacon: 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 ./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 --data-dir ${DATA_DIR} | tee ${FIG_DIR}/ab.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} ./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} ./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}
clocks: #
new-clocks:
./ab_modify_clocks.py ${CLK_DEV} --seed ${SEED} --gaussian --data-dir ${DATA_DIR} ./ab_modify_clocks.py ${CLK_DEV} --seed ${SEED} --gaussian --data-dir ${DATA_DIR}
phases: 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} ./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} ./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} ./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} ./bd_antenna_phase_deltas.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
#
findks: 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} ./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} ./cb_report_measured_antenna_time_offsets.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
#
vary-fixes: vary-fixes:
./dc_grid_power_time_fixes.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR} ./dc_grid_power_time_fixes.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR}
#
reconstruct: reconstruct:
./da_reconstruction.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR} --input-fname ${INPUT_DIR} ./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} ./db_longitudinal_figure.py --no-show-plots --fig-dir=${FIG_DIR} --data-dir ${DATA_DIR}
dist-clean: dist-clean:
rm -vf ${DATA_DIR}/antennas.hdf5 rm -vf ${DATA_DIR}/
rm -vf ${DATA_DIR}/ca_breaked_run rm -vf ${FIG_DIR}/
rm -vf ${DATA_DIR}/res.pkl
rm -vf ${DATA_DIR}/clocks.csv
rm -vf ${DATA_DIR}/tx.json

View file

@ -18,6 +18,7 @@ import lib
# {{{ vim marker # {{{ vim marker
tx_fname = 'tx.json' tx_fname = 'tx.json'
antennas_fname = 'antennas.hdf5' antennas_fname = 'antennas.hdf5'
snr_fname = 'snr.json'
c_light = lib.c_light c_light = lib.c_light
def read_antenna_clock_repair_offsets(antennas, mode='all', freq_name=None): def read_antenna_clock_repair_offsets(antennas, mode='all', freq_name=None):
@ -49,6 +50,17 @@ def read_antenna_clock_repair_offsets(antennas, mode='all', freq_name=None):
return time_offsets 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): def write_tx_file(fname, tx, f_beacon, **kwargs):
with open(fname, 'w') as fp: with open(fname, 'w') as fp:
return json.dump( return json.dump(
@ -309,6 +321,7 @@ if __name__ == "__main__":
# Noise properties # Noise properties
noise_sigma = args.noise_sigma # mu V/m set to None to ignore 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 properties
beacon_amplitudes = np.array(args.beacon_amplitudes) # mu V/m beacon_amplitudes = np.array(args.beacon_amplitudes) # mu V/m
@ -369,6 +382,7 @@ if __name__ == "__main__":
init_antenna_hdf5(antennas_fname, tx, f_beacon) init_antenna_hdf5(antennas_fname, tx, f_beacon)
# make beacon per antenna # make beacon per antenna
noise_realisation = np.array([0])
for i, antenna in enumerate(ev.antennas): for i, antenna in enumerate(ev.antennas):
#TODO: allow to change the samplerate (2, 4, 8 ns) #TODO: allow to change the samplerate (2, 4, 8 ns)
@ -397,9 +411,9 @@ if __name__ == "__main__":
beacon = 1e-6 * lib.beacon_from(tx, antenna, f_beacon, antenna.t, c_light=c_light, radiate_rsq=beacon_radiate_rsq) # mu V/m 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 # noise realisation
noise_realisation = 0 if unique_noise_realisations or (noise_realisation == 0).all(): # either create one for every antenna, or generate a single one
if noise_sigma is not None: print("Noise realisation!")
noise_realisation = 1e-6 * rng.normal(0, noise_sigma, size=len(beacon)) # mu V/m 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) # 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]) traces = np.array([antenna.Ex, antenna.Ey, antenna.Ez, beacon, noise_realisation])

View file

@ -26,6 +26,8 @@ if __name__ == "__main__":
parser.add_argument('-s', '--seed', type=int, nargs='?', default=None, help='Fix seed if supplied.') 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('--uniform', action='store_const', const='uniform', dest='dist_type')
parser.add_argument('--gaussian', action='store_const', const='gauss', 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.set_defaults(dist_type='gauss')
parser.add_argument('--data-dir', type=str, default="./data", help='Path to Data Directory. (Default: %(default)s)') parser.add_argument('--data-dir', type=str, default="./data", help='Path to Data Directory. (Default: %(default)s)')
@ -62,7 +64,11 @@ if __name__ == "__main__":
N_antennas = len(group.keys()) N_antennas = len(group.keys())
if True: 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.") print(f"Modifying clocks upto {max_clock_offset}ns.")
clock_offsets = np.zeros( N_antennas ) clock_offsets = np.zeros( N_antennas )
if args.dist_type == 'uniform': # uniform if args.dist_type == 'uniform': # uniform
@ -110,6 +116,7 @@ if __name__ == "__main__":
h5ant['E_AxB'][0, :] += clk_offset h5ant['E_AxB'][0, :] += clk_offset
# save to simple csv # save to simple csv
if args.save_clocks:
np.savetxt(clocks_fname, clock_offsets) np.savetxt(clocks_fname, clock_offsets)
print("Antenna clocks modified in " + str(antennas_fname)) print("Antenna clocks modified in " + str(antennas_fname))

View file

@ -42,6 +42,7 @@ if __name__ == "__main__":
fname_dir = args.data_dir fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname) antennas_fname = path.join(fname_dir, beacon.antennas_fname)
tx_fname = path.join(fname_dir, beacon.tx_fname) tx_fname = path.join(fname_dir, beacon.tx_fname)
snr_fname = path.join(fname_dir, beacon.snr_fname)
# create fig_dir # create fig_dir
if fig_dir: if fig_dir:
@ -92,6 +93,9 @@ if __name__ == "__main__":
N_samples = len(antennas[0].beacon) 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) ] 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) fig, ax = plt.subplots(figsize=figsize)
ax.set_title(f"Maximum Beacon/Noise SNR (N_samples:{N_samples:.1e})") ax.set_title(f"Maximum Beacon/Noise SNR (N_samples:{N_samples:.1e})")
ax.set_xlabel("Antenna no.") ax.set_xlabel("Antenna no.")

View file

@ -12,6 +12,8 @@ import numpy as np
import aa_generate_beacon as beacon import aa_generate_beacon as beacon
import lib import lib
from lib import figlib
if __name__ == "__main__": if __name__ == "__main__":
from os import path from os import path
@ -49,6 +51,7 @@ if __name__ == "__main__":
#### ####
fname_dir = args.data_dir fname_dir = args.data_dir
antennas_fname = path.join(fname_dir, beacon.antennas_fname) 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 fig_dir = args.fig_dir # set None to disable saving
@ -83,7 +86,8 @@ if __name__ == "__main__":
N_antennas = len(group.keys()) N_antennas = len(group.keys())
# just for funzies # just for funzies
found_data = np.zeros((N_antennas, 3)) found_data = np.zeros((N_antennas, 3)) # freq, phase, amp
noise_data = np.zeros((N_antennas, 2)) # phase, amp
# Determine frequency and phase # Determine frequency and phase
for i, name in enumerate(group.keys()): for i, name in enumerate(group.keys()):
@ -133,6 +137,7 @@ if __name__ == "__main__":
# TODO: refine masking # TODO: refine masking
# use beacon but remove where E_AxB-Beacon != 0 # use beacon but remove where E_AxB-Beacon != 0
# Uses the first traces as reference # Uses the first traces as reference
t_mask = 0
if N_mask and orients[0] != 'B': if N_mask and orients[0] != 'B':
N_pre, N_post = N_mask//2, N_mask//2 N_pre, N_post = N_mask//2, N_mask//2
@ -166,6 +171,18 @@ if __name__ == "__main__":
beacon_phases = [ 2*np.pi*t0*f_beacon ] beacon_phases = [ 2*np.pi*t0*f_beacon ]
amps = [ 3e-7 ] 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 # choose highest amp
idx = np.argmax(amps) idx = np.argmax(amps)
if False and len(beacon_phases) > 1: if False and len(beacon_phases) > 1:
@ -246,10 +263,16 @@ if __name__ == "__main__":
h5attrs['amplitude'] = amplitude h5attrs['amplitude'] = amplitude
h5attrs['orientation'] = orientation 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) print("Beacon Phases, Amplitudes and Frequencies written to", antennas_fname)
# show histogram of found frequencies # show histogram of found frequencies
if show_plots or fig_dir: if show_plots or fig_dir:
snrs = beacon.read_snr_file(snr_fname)
if True or allow_frequency_fitting: if True or allow_frequency_fitting:
fig, ax = plt.subplots(figsize=figsize) fig, ax = plt.subplots(figsize=figsize)
ax.set_xlabel("Frequency") ax.set_xlabel("Frequency")
@ -260,12 +283,45 @@ if __name__ == "__main__":
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".hist_freq.pdf")) fig.savefig(path.join(fig_dir, path.basename(__file__) + f".hist_freq.pdf"))
if True: if True:
fig, ax = plt.subplots(figsize=figsize) fig, _ = figlib.fitted_histogram_figure(found_data[:,2], fit_distr=[], mean_snr=snrs['mean'])
ax.set_xlabel("Amplitudes") ax = fig.axes[0]
ax.set_xlabel("Amplitude")
ax.set_ylabel("Counts") ax.set_ylabel("Counts")
ax.hist(found_data[:,2], bins='sqrt', density=False) ax.hist(found_data[:,2], bins='sqrt', density=False)
ax.legend()
if fig_dir: if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".hist_amp.pdf")) 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: if show_plots:
plt.show() plt.show()

View file

@ -0,0 +1,197 @@
#!/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

@ -8,6 +8,7 @@ import numpy as np
import aa_generate_beacon as beacon import aa_generate_beacon as beacon
import lib import lib
from lib import figlib
if __name__ == "__main__": if __name__ == "__main__":
@ -108,45 +109,42 @@ if __name__ == "__main__":
for i in range(2): for i in range(2):
plot_residuals = i == 1 plot_residuals = i == 1
colors = ['blue', 'orange']
fig, axs = plt.subplots(2, 1, sharex=True, figsize=figsize) true_phases = my_phase_diffs
measured_phases = phase_diffs[:,1]
if True: hist_kwargs = {}
phase2time = lambda x: x/(2*np.pi*f_beacon) if plot_residuals:
time2phase = lambda x: 2*np.pi*x*f_beacon measured_phases = lib.phase_mod(measured_phases - true_phases)
secax = axs[0].secondary_xaxis('top', functions=(phase2time, time2phase)) hist_kwargs['histtype'] = 'stepfilled'
secax.set_xlabel('Time $\\Delta\\varphi/(2\\pi f_{beac})$ [ns]')
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: if plot_residuals:
phase_residuals = lib.phase_mod(phase_diffs[:,1] - my_phase_diffs)
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])+')')) 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]") axs[-1].set_xlabel("Baseline Phase Residual $\\Delta\\varphi_{ij_{meas}} - \\Delta\\varphi_{ij_{true}}$ [rad]")
else: 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])+')')) 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]") axs[-1].set_xlabel("Baseline Phase $\\Delta\\varphi_{ij}$ [rad]")
#
i=0 i=0
axs[i].set_ylabel("#") secax = axs[i].child_axes[0]
if plot_residuals: secax.set_xlabel('Time $\\Delta\\varphi/(2\\pi f_{beac})$ [ns]')
axs[i].hist(phase_residuals, bins='sqrt', density=False, alpha=0.8, color=colors[0])
else:
axs[i].hist(phase_diffs[:,1], bins='sqrt', density=False, alpha=0.8, color=colors[0], ls='solid' , histtype='step', label='Measured')
axs[i].hist(my_phase_diffs, bins='sqrt', density=False, alpha=0.8, color=colors[1], ls='dashed', histtype='step', label='Actual')
#
i=1 i=1
axs[i].set_ylabel("Baseline no.") axs[i].set_ylabel("Baseline no.")
if plot_residuals:
axs[i].plot(phase_residuals, np.arange(N_base), alpha=0.6, ls='none', marker='x', color=colors[0])
else:
axs[i].plot(phase_diffs[:,1], np.arange(N_base), alpha=0.8, color=colors[0], ls='none', marker='x', label='calculated')
axs[i].plot(my_phase_diffs, np.arange(N_base), alpha=0.8, color=colors[1], ls='none', marker='+', label='actual time shifts')
axs[i].legend()
fig.tight_layout()
if fig_dir: if fig_dir:
extra_name = "measured" extra_name = "measured"

View file

@ -7,9 +7,11 @@ import matplotlib.pyplot as plt
from matplotlib.colors import Normalize from matplotlib.colors import Normalize
import matplotlib as mpl import matplotlib as mpl
import numpy as np import numpy as np
import json
import aa_generate_beacon as beacon import aa_generate_beacon as beacon
import lib import lib
from lib import figlib
if __name__ == "__main__": if __name__ == "__main__":
@ -118,6 +120,11 @@ if __name__ == "__main__":
mean_clock_phase = np.nanmean(clock_phase_matrix[my_mask], axis=0) mean_clock_phase = np.nanmean(clock_phase_matrix[my_mask], axis=0)
std_clock_phase = np.nanstd( 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 # Show resulting matrix as figure
if True: if True:
@ -174,45 +181,40 @@ if __name__ == "__main__":
global_phase_shift = actual_antenna_phase_shifts[0] - mean_clock_phase[0] 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 ) actual_antenna_phase_shifts = lib.phase_mod(actual_antenna_phase_shifts - global_phase_shift )
fit_info = {}
for i in range(2): for i in range(2):
plot_residuals = i == 1 plot_residuals = i == 1
colors = ['blue', 'orange'] true_phases = actual_antenna_phase_shifts
measured_phases = mean_clock_phase
fig, axs = plt.subplots(2, 1, sharex=True, figsize=figsize) hist_kwargs = {}
if plot_residuals:
measured_phases = lib.phase_mod(measured_phases - actual_antenna_phase_shifts)
if True: fig, _fit_info = figlib.phase_comparison_figure(
phase2time = lambda x: x/(2*np.pi*f_beacon) measured_phases,
time2phase = lambda x: 2*np.pi*x*f_beacon true_phases,
secax = axs[0].secondary_xaxis('top', functions=(phase2time, time2phase)) plot_residuals=plot_residuals,
secax.set_xlabel('Time $\\Delta\\varphi/(2\\pi f_{beac})$ [ns]') 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: if plot_residuals:
phase_residuals = lib.phase_mod(mean_clock_phase - actual_antenna_phase_shifts)
fig.suptitle("Difference between Measured and Actual phases (minus global phase)\n for Antenna $i$") fig.suptitle("Difference between Measured and Actual phases (minus global phase)\n for Antenna $i$")
axs[-1].set_xlabel("Antenna Phase Residual $\\Delta_\\varphi$") axs[-1].set_xlabel("Antenna Mean Phase Residual $\\Delta_\\varphi$")
else: else:
fig.suptitle("Comparison Measured and Actual phases (minus global phase)\n for Antenna $i$") fig.suptitle("Comparison Measured and Actual phases (minus global phase)\n for Antenna $i$")
axs[-1].set_xlabel("Antenna Phase $\\varphi$") axs[-1].set_xlabel("Antenna Mean Phase $\\varphi$")
i=0
axs[i].set_ylabel("#")
if plot_residuals:
axs[i].hist(phase_residuals, bins='sqrt', alpha=0.8, color=colors[0])
else:
axs[i].hist(mean_clock_phase, bins='sqrt', density=False, alpha=0.8, color=colors[0], ls='solid' , histtype='step', label='Measured')
axs[i].hist(actual_antenna_phase_shifts, bins='sqrt', density=False, alpha=0.8, color=colors[1], ls='dashed', histtype='step', label='Actual')
i=1 i=1
axs[i].set_ylabel("Antenna no.") axs[i].set_ylabel("Antenna no.")
if plot_residuals: #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')
axs[i].plot(phase_residuals, np.arange(N_ant), alpha=0.6, ls='none', marker='x', color=colors[0])
else:
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')
axs[i].plot(actual_antenna_phase_shifts, antenna_names, ls='none', marker='3', alpha=0.8, color=colors[1], label='Actual')
axs[i].legend()
fig.tight_layout() fig.tight_layout()
if fig_dir: if fig_dir:
@ -221,6 +223,19 @@ if __name__ == "__main__":
extra_name = "residuals" extra_name = "residuals"
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".phase.{extra_name}.pdf")) 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)
########################## ##########################
########################## ##########################
########################## ##########################

View file

@ -10,6 +10,7 @@ import numpy as np
from os import path from os import path
import aa_generate_beacon as beacon import aa_generate_beacon as beacon
from lib import figlib
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
@ -76,15 +77,28 @@ if __name__ == "__main__":
for i in range(2): for i in range(2):
plot_residuals = i == 1 plot_residuals = i == 1
colors = ['blue', 'orange']
fig, axs = plt.subplots(2, 1, sharex=True, figsize=figsize)
if True: true_phases = actual_time_shifts
phase2time = lambda x: x/(2*np.pi*f_beacon) measured_phases = measured_time_shifts
time2phase = lambda x: 2*np.pi*x*f_beacon
secax = axs[0].secondary_xaxis('top', functions=(time2phase, phase2time)) hist_kwargs = {}
secax.set_xlabel('Phase $2\\pi t f_{beac}$ [rad]') 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: if plot_residuals:
time_shift_residuals = measured_time_shifts - actual_time_shifts time_shift_residuals = measured_time_shifts - actual_time_shifts
@ -94,26 +108,6 @@ if __name__ == "__main__":
fig.suptitle("Comparison Measured and Actual clock offset") 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]") axs[-1].set_xlabel("Antenna Time Offset $t_c = \\left(\\frac{\\Delta\\varphi}{2\\pi} + k\\right) / f_{beac}$ [ns]")
i=0
axs[i].set_ylabel("#")
if plot_residuals:
axs[i].hist(time_shift_residuals, bins='sqrt', alpha=0.8, color=colors[0])
else:
axs[i].hist(measured_time_shifts, bins='sqrt', density=False, alpha=0.8, color=colors[0], ls='solid' , histtype='step', label='Measured')
axs[i].hist(actual_time_shifts, bins='sqrt', density=False, alpha=0.8, color=colors[1], ls='dashed', histtype='step', label='Actual')
i=1
axs[i].set_ylabel("Antenna no.")
if plot_residuals:
axs[i].plot(time_shift_residuals, np.arange(N_ant), alpha=0.6, ls='none', marker='x', color=colors[0])
else:
axs[i].errorbar(measured_time_shifts, np.arange(N_ant), yerr=None, marker='4', alpha=0.7, ls='none', color=colors[0], label='Measured')
axs[i].plot(actual_time_shifts, antenna_names, ls='none', marker='3', alpha=0.8, color=colors[1], label='Actual')
axs[i].legend()
fig.tight_layout()
if fig_dir: if fig_dir:
extra_name = "comparison" extra_name = "comparison"
if plot_residuals: if plot_residuals:

View file

@ -46,6 +46,7 @@ if __name__ == "__main__":
fig_subdir = path.join(fig_dir, 'reconstruction') fig_subdir = path.join(fig_dir, 'reconstruction')
show_plots = args.show_plots show_plots = args.show_plots
apply_signal_window_from_max = True
remove_beacon_from_traces = True remove_beacon_from_traces = True
#### ####

View file

@ -19,6 +19,65 @@ import lib
from lib import rit 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__": if __name__ == "__main__":
valid_cases = ['no_offset', 'repair_none', 'repair_phases', 'repair_all'] valid_cases = ['no_offset', 'repair_none', 'repair_phases', 'repair_all']
@ -95,6 +154,9 @@ if __name__ == "__main__":
'scale02d': scale02d, 'scale02d': scale02d,
} }
N_plot = 30
trace_zoom_wx = 100
plot_titling = { plot_titling = {
'no_offset': "no clock offset", 'no_offset': "no clock offset",
'repair_none': "unrepaired clock offset", 'repair_none': "unrepaired clock offset",
@ -175,6 +237,9 @@ if __name__ == "__main__":
backup_antenna_t = [ ant.t for ant in ev.antennas ] backup_antenna_t = [ ant.t for ant in ev.antennas ]
backup_antenna_t_AxB = [ ant.t_AxB 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"): with joblib.parallel_backend("loky"):
for case in wanted_cases: for case in wanted_cases:
print(f"Starting {case} figure") print(f"Starting {case} figure")
@ -195,73 +260,29 @@ if __name__ == "__main__":
if i == 0: if i == 0:
# Specifically compare the times # Specifically compare the times
print(bak_ants[i].t[0], ev.antennas[i].t[0], ev.antennas[i].t[0], ev.antennas[i].attrs['clock_offset'], measured_offsets[i]) 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 # Plot overlapping traces at 0,0,0
# #
if True: 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)
P, t_, a_, a_sum, t_sum = rit.pow_and_time([0,0,0], ev, dt=1) plt.close(fig)
fig, axs = plt.subplots(figsize=figsize)
axs.set_title("Antenna traces" + "\n" + plot_titling[case])
axs.set_xlabel("Time [ns]")
axs.set_ylabel("Amplitude [$\\mu V/m$]")
a_max = [ np.amax(ant.E_AxB) for ant in ev.antennas ]
power_sort_idx = np.argsort(a_max)
N_plot = 30
for i, idx in enumerate(reversed(power_sort_idx)):
if i > N_plot:
break
alpha = max(0.4, 1/len(a_))
axs.plot(t_[idx], a_[idx], color='r', alpha=alpha, lw=2)
if fig_dir:
fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'.trace_overlap.{case}.pdf'))
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'.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:]]
axs.plot(a, [0]*N_plot, 'gx', ms=10)
t_low = np.nanmin(a)
b = [np.max(t_[idx]) for idx in power_sort_idx[-N_plot:]]
axs.plot(b, [0]*N_plot, 'b+', ms=10)
t_high = np.nanmax(b)
center_x = (t_high - t_low)/2 + t_low
wx = 200
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'.trace_overlap.zoomed.{case}.pdf'))
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'.trace_overlap.zoomed.{case}.png'), transparent=True)
if True:
continue
# Measure power on grid # Measure power on grid
# and plot overlapping traces at position with highest power
for scalename, scale in scales.items(): for scalename, scale in scales.items():
wx, wy = scale, scale wx, wy = scale, scale
print(f"Starting grid measurement for figure {case} with {scalename}") print(f"Starting grid measurement for figure {case} with {scalename}")
xx, yy, p, maxp = rit.shower_plane_slice(ev, X=X, Nx=Nx, Ny=Nx, wx=wx, wy=wy) 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') 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() suptitle = fig._suptitle.get_text()
fig.suptitle("") fig.suptitle("")
@ -271,7 +292,24 @@ if __name__ == "__main__":
if fig_dir: if fig_dir:
fig.tight_layout() fig.tight_layout()
fig.savefig(path.join(fig_dir, path.basename(__file__) + f'.X{X}.{case}.{scalename}.pdf')) 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: if args.show_plots:
plt.show() plt.show()

@ -0,0 +1 @@
Subproject commit 6ef809020477cf78415b9fa733d4fbb4a74102a1

View file

@ -0,0 +1,219 @@
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

@ -58,6 +58,8 @@ def pow_and_time(test_loc,ev,dt=1.0):
a_sum = np.add(a_sum,a_int) a_sum = np.add(a_sum,a_int)
if len(a_sum) != 0: if len(a_sum) != 0:
P = np.sum(np.square(np.absolute(np.fft.fft(a_sum)))) 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: else:
print("ERROR, a_sum lenght = 0", print("ERROR, a_sum lenght = 0",
"tmin ",t_min, "tmin ",t_min,
@ -112,11 +114,21 @@ def shower_plane_slice(e,X=750.,Nx=10,Ny=10,wx=1e3,wy=1e3,xoff=0,yoff=0,zgr=0,n_
return xx,yy,p,locs[np.argmax(p)] return xx,yy,p,locs[np.argmax(p)]
def slice_figure(e,X,xx,yy,p,mode='horizontal'): 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, 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)) 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,cmap='Spectral_r',alpha=0.6) sc = axs.scatter(xx/1e3,yy/1e3,c=p,**scatter_kwargs)
fig.colorbar(sc,ax=axs) fig.colorbar(sc,ax=axs, **colorbar_kwargs)
zgr = 0 + e.core[2] zgr = 0 + e.core[2]
dX = atm.distance_to_slant_depth(np.deg2rad(e.zenith),X,zgr) 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 xc = np.sin(np.deg2rad(e.zenith))*np.cos(np.deg2rad(e.azimuth))* dX

View file

@ -0,0 +1,67 @@
#!/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

@ -17,10 +17,17 @@ for options in product(baselines, clock_devs, noise_sigmas, trace_lengths):
# Make directory # Make directory
if path.exists(dirname): if path.exists(dirname):
print(f"{dirname} already exists! continuing..") print(f"{dirname} already exists! continuing anyway..")
os.makedirs(dirname, exist_ok=True) 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 # Setup config.mk
with open(path.join(dirname, 'env.sh'), 'w') as fp: with open(path.join(dirname, 'env.sh'), 'w') as fp:
template = f""" template = f"""

View file

@ -0,0 +1,217 @@
#!/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,148 +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
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)
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:
fig, ax = plt.subplots(figsize=figsize)
spatial_unit='m'
fig.suptitle('Clock phases\nf_beacon= {:2.0f}MHz'.format(f_beacon*1e3))
antenna_locs = list(zip(*[(ant.x, ant.y) for ant in antennas]))
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))
scatter_kwargs = {}
scatter_kwargs['cmap'] = 'inferno'
#scatter_kwargs['vmin'] = -np.pi
#scatter_kwargs['vmax'] = +np.pi
color_label='$\\varphi(\\sigma_t)$ [rad]'
sc = ax.scatter(*antenna_locs, c=clock_phases, **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.savefig(path.join(fig_dir, path.basename(__file__) + f".F{freq_name}.pdf"))
# Plot True Phases - Actual True Phases at their location
if show_plots or fig_dir:
fig, ax = plt.subplots(figsize=figsize)
fig.suptitle('Clock phase Residuals\nf_beacon={:2.0f}MHz'.format(f_beacon*1e3))
actual_clock_phases = np.array([ -2*np.pi*a.attrs['clock_offset']*f_beacon for a in antennas ])
# 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)
antenna_locs = list(zip(*[(ant.x, ant.y) for ant in antennas]))
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))
scatter_kwargs = {}
scatter_kwargs['cmap'] = 'inferno'
color_label='$\\Delta\\varphi(\\sigma_t) = \\varphi_{meas} - \\varphi_{true}$ [rad]'
sc = ax.scatter(*antenna_locs, c=clock_phase_residuals, **scatter_kwargs)
fig.colorbar(sc, ax=ax, label=color_label)
if fig_dir:
fig.savefig(path.join(fig_dir, path.basename(__file__) + f".residual.F{freq_name}.pdf"))
print(f"True phases written to", antennas_fname)
if show_plots:
plt.show()

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