ZH: (WIP) fitting random phasor sum to antenna phases

This commit is contained in:
Eric Teunis de Boone 2023-04-13 12:34:14 +02:00
parent 1130f2c679
commit 103bde61f8
2 changed files with 78 additions and 12 deletions

View file

@ -206,7 +206,7 @@ if __name__ == "__main__":
figsize=figsize, figsize=figsize,
hist_kwargs=hist_kwargs, hist_kwargs=hist_kwargs,
fit_gaussian=plot_residuals, fit_gaussian=plot_residuals,
fit_ricianphase=plot_residuals, fit_randomphasesum=plot_residuals,
return_fit_info = True, return_fit_info = True,
) )

View file

@ -1,9 +1,35 @@
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import scipy.stats as stats from scipy import stats
from scipy import special
from scipy import optimize
from itertools import zip_longest from itertools import zip_longest
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 random_phase_sum_distribution(theta, sigma, s=1):
theta = np.asarray(theta)
ct = np.cos(theta)
st = np.sin(theta)
k = s/sigma
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 gaussian_phase_distribution(theta, sigma, s=1):
theta = np.asarray(theta)
k=s/sigma
return (2*np.pi)**-0.5*k*np.exp(-(k*theta)**2/2)
def phase_comparison_figure( def phase_comparison_figure(
measured_phases, measured_phases,
true_phases, true_phases,
@ -16,6 +42,7 @@ def phase_comparison_figure(
legend_on_scatter=True, legend_on_scatter=True,
secondary_axis='time', secondary_axis='time',
fit_gaussian=False, fit_gaussian=False,
fit_randomphasesum=False,
mean_snr=None, mean_snr=None,
return_fit_info=False, return_fit_info=False,
**fig_kwargs **fig_kwargs
@ -29,14 +56,19 @@ def phase_comparison_figure(
default_text_kwargs = dict(fontsize=14, verticalalignment='top') default_text_kwargs = dict(fontsize=14, verticalalignment='top')
default_sc_kwargs = dict(alpha=0.6, ls='none') default_sc_kwargs = dict(alpha=0.6, ls='none')
do_hist_plot = hist_kwargs is not False
if hist_kwargs is False:
hist_kwargs = {}
do_scatter_plot = sc_kwargs is not False
if sc_kwargs is False:
sc_kwargs = {}
fig_kwargs = {**default_fig_kwargs, **fig_kwargs} fig_kwargs = {**default_fig_kwargs, **fig_kwargs}
hist_kwargs = {**default_hist_kwargs, **hist_kwargs} hist_kwargs = {**default_hist_kwargs, **hist_kwargs}
text_kwargs = {**default_text_kwargs, **text_kwargs} text_kwargs = {**default_text_kwargs, **text_kwargs}
sc_kwargs = {**default_sc_kwargs, **sc_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) fig, axs = plt.subplots(0+do_hist_plot+do_scatter_plot, 1, **fig_kwargs)
if not hasattr(axs, '__len__'): if not hasattr(axs, '__len__'):
@ -67,10 +99,14 @@ def phase_comparison_figure(
text_kwargs=text_kwargs, text_kwargs=text_kwargs,
hist_kwargs={**hist_kwargs, **dict(label='Measured', color=colors[0], ls='solid')}, hist_kwargs={**hist_kwargs, **dict(label='Measured', color=colors[0], ls='solid')},
mean_snr=mean_snr, mean_snr=mean_snr,
fit_distr=[],
) )
if fit_gaussian: if fit_gaussian:
this_kwargs['fit_distr'] = 'gaussian' this_kwargs['fit_distr'].append('gaussian')
if fit_randomphasesum:
this_kwargs['fit_distr'].append('randomphasesum')
_, fit_info = fitted_histogram_figure( _, fit_info = fitted_histogram_figure(
measured_phases, measured_phases,
@ -139,6 +175,8 @@ def fitted_histogram_figure(
min_x = min(amplitudes) min_x = min(amplitudes)
max_x = max(amplitudes) max_x = max(amplitudes)
bin_centers = bins[:-1] + np.diff(bins) / 2
dx = bins[1] - bins[0] dx = bins[1] - bins[0]
scale = len(amplitudes) * dx scale = len(amplitudes) * dx
@ -146,6 +184,9 @@ def fitted_histogram_figure(
for distr in fit_distr: for distr in fit_distr:
fit_params2text_params = lambda x: x fit_params2text_params = lambda x: x
fit_ys = None
fit_params = None
cdf = None
if 'rice' == distr: if 'rice' == distr:
name = "Rice" name = "Rice"
@ -166,19 +207,44 @@ def fitted_histogram_figure(
fit_params2text_params = lambda x: (x[0]+x[1]/2,) fit_params2text_params = lambda x: (x[0]+x[1]/2,)
elif 'randomphasesum' == distr:
name = "RandPhaseS"
param_names = [ "$\\sigma$", 's']
pdf = random_phase_sum_distribution
bounds = ((0,0.9999), (np.inf,1))
fit_params, pcov = optimize.curve_fit(pdf, bin_centers, counts, bounds=bounds)
fit_ys = pdf( xs, *fit_params)
fit_params2text_params = lambda x: (x[1], x[0])
elif 'gaussphase' == distr:
name = 'GaussPhase'
param_names = [ "$\\sigma$", 's']
pdf = gaussian_phase_distribution
bounds = ((0,0.9999), (np.inf,1))
fit_params, pcov = optimize.curve_fit(pdf, bin_centers, counts, bounds=bounds)
fit_ys = pdf( xs, *fit_params)
fit_params2text_params = lambda x: (x[1], x[0])
else: else:
raise ValueError('Unknown distribution function '+distr) raise ValueError('Unknown distribution function '+distr)
label = name +"(" + ','.join(param_names) + ')' label = name +"(" + ','.join(param_names) + ')'
if fit_ys is None:
fit_params = distr_func.fit(amplitudes) fit_params = distr_func.fit(amplitudes)
fit_ys = distr_func.pdf(xs, *fit_params) fit_ys = scale * distr_func.pdf(xs, *fit_params)
cdf = distr_func.cdf
ax.plot(xs, fit_ys*scale, label=label) ax.plot(xs, fit_ys, label=label)
chisq_strs = [] chisq_strs = []
if calc_chisq: if calc_chisq and cdf:
ct = np.diff(distr_func.cdf(bins, *fit_params))*np.sum(counts) ct = np.diff(cdf(bins, *fit_params))*np.sum(counts)
c2t = stats.chisquare(counts, ct, ddof=len(fit_params)) c2t = stats.chisquare(counts, ct, ddof=len(fit_params))
chisq_strs = [ chisq_strs = [
f"$\\chi^2$/dof = {c2t[0]: .2g}/{len(fit_params)}" f"$\\chi^2$/dof = {c2t[0]: .2g}/{len(fit_params)}"