Source code for backscatter.fitacf.filtering
from backscatter import config
import numpy as np
import math
MIN_LAGS = int(config.get("fitacf","minimum_lags"))
ACF_SNR_CUTOFF = float(config.get("fitacf","acf_snr_cutoff"))
ALPHA_CUTOFF = float(config.get("fitacf","alpha_cutoff"))
FLUCTUATION_CUTOFF_COEFF = int(config.get("fitacf","fluctuation_cutoff_coeff"))
[docs]class Filtering(object):
"""This class contains static methods relating to the filtering
of bad data from the consideration of fitting.
"""
[docs] @staticmethod
def mark_bad_samples(raw_data):
"""Mark the samples that are blacked out by TX overlap
:param raw_data: a dictionary of raw data parameters
"""
mppul = raw_data['mppul']
pulses = raw_data['ptab']
mpinc = raw_data['mpinc']
txpl = raw_data['txpl']
smsep = raw_data['smsep']
lagfr = raw_data['lagfr']
offset = raw_data['offset']
channel = raw_data['channel']
pulses_in_us = [mpinc * pulse for pulse in pulses]
pulses_stereo = []
if offset != 0 and (channel == 1 or channel == 2):
for pulse in pulses:
if channel == 1:
pulse_us = pulse * mpinc - offset
else:
pulse_us = pulse * mpinc + offset
pulses_stereo.append(pulse_us)
pulses_in_us = pulses_in_us + pulses_stereo
pulses_in_us = sorted(pulses_in_us)
i = -1
ts, t1, t2, sample = lagfr, 0, 0, 0
bad_samples = []
for pulse_us in pulses_in_us:
t1 = pulse_us - txpl/2
t2 = t1 + 3 * txpl/2 + 100
# we now have a pulse that occurs after the current sample. Start
# incrementing the sample number until we find a sample that lies
# within the pulse
while (ts < t1):
sample = sample + 1
ts = ts + smsep
# ok, we now have a sample which occurs after the pulse starts.
# check to see if it occurs before the pulse ends, and if so, mark
# it as a bad sample
while ((ts >= t1) and (ts <= t2)):
bad_samples.append(sample)
sample = sample + 1
ts = ts + smsep
return bad_samples
[docs] @staticmethod
def filter_tx_overlapped_lags(raw_data,lags,range_list):
"""Remove data points affected by TX overlapped lags
:param raw_data: a dictionary of raw data parameters
:param lags: list of lag dictionaries
:param range_list: A list of Range objects with data points
"""
bad_samples = Filtering.mark_bad_samples(raw_data)
for range_obj in range_list:
bad_indices = []
for idx,lag in enumerate(lags):
sample1 = lag['sample_base1'] + range_obj.range_number
sample2 = lag['sample_base2'] + range_obj.range_number
if (sample1 in bad_samples) or (sample2 in bad_samples):
bad_indices.append(idx)
range_obj.pwrs.remove_bad_points(bad_indices)
range_obj.phases.remove_bad_points(bad_indices)
range_obj.elevs.remove_bad_points(bad_indices)
range_obj.remove_bad_alphas(bad_indices)
[docs] @staticmethod
def filter_inf_lags(range_list):
for range_obj in range_list:
log_pwrs = range_obj.pwrs.log_pwrs
inf_indices = [idx for idx,log_pwr in enumerate(log_pwrs) if not np.isfinite(log_pwr)]
range_obj.pwrs.remove_bad_points(inf_indices)
range_obj.remove_bad_alphas(inf_indices)
[docs] @staticmethod
def filter_low_pwr_lags(raw_data,range_list):
"""Removes low power lags from fitting
Prunes off low power lags determined by cutoff criteria. Once a
cutoff lag is determined, all subsequent lags in the list are
removed
:param raw_data: a dictionary of raw data parameters
:param range_list: A list of Range objects with data points
"""
pwr0 = raw_data['pwr0']
mplgs = raw_data['mplgs']
nave = raw_data['nave']
noise_mean = raw_data['noise.mean']
#Division by zero error
if nave <= 0:
return
for range_obj in range_list:
range_number = range_obj.range_number
if len(range_obj.pwrs.log_pwrs) == 0:
continue
log_sigma_fluc = np.log(FLUCTUATION_CUTOFF_COEFF * pwr0[range_number]/math.sqrt(2 * nave))
bad_indices = []
cutoff_lag = mplgs + 1
for idx,(log_pwr,alpha_2) in enumerate(zip(range_obj.pwrs.log_pwrs,range_obj.pwrs.alpha_2)):
if idx > cutoff_lag:
bad_indices.append(idx)
else:
if((1/np.sqrt(alpha_2) <= ALPHA_CUTOFF) and
((log_pwr < log_sigma_fluc) or np.isclose(log_pwr,log_sigma_fluc))):
cutoff_lag = idx
bad_indices.append(idx)
range_obj.pwrs.remove_bad_points(bad_indices)
[docs] @staticmethod
def filter_bad_acfs(raw_data,ranges,noise_pwr):
"""Removes bad ACFs entirely from analysis
Removes ACFs which are deemed to be pure noise, or ACFs with too
few power lags left
:param raw_data: a dictionary of raw data parameters
:param range_list: A list of Range objects with data points
:param noise_pwr: minimum power for which an ACF is pure noise
"""
nave = raw_data['nave']
pwr0 = raw_data['pwr0']
#Division by zero error
if nave <= 0:
return
cutoff_pwr = noise_pwr * 2
bad_indices = []
for idx,rang in enumerate(ranges):
range_number = rang.range_number
pw = float(pwr0[range_number])
ln = len(rang.pwrs.log_pwrs)
if (pw <= cutoff_pwr) or (ln < MIN_LAGS):
bad_indices.append(idx)
continue
#check to see if all powers are equal
pwr_val = rang.pwrs.log_pwrs[0]
for pwr in rang.pwrs.log_pwrs:
if pwr != pwr_val:
break
else:
bad_indices.append(idx)
for i in sorted(bad_indices,reverse=True):
del ranges[i]
[docs] @staticmethod
def filter_bad_fits(ranges):
bad_indices = []
for idx,range_obj in enumerate(ranges):
if (range_obj.phase_fit.b == 0.0 or
range_obj.linear_pwr_fit.b == 0.0 or
range_obj.quadratic_pwr_fit.b == 0.0):
bad_indices.append(idx)
for i in sorted(bad_indices,reverse=True):
del ranges[i]