def filter_psd(self, segment_duration, delta_f, flow): """ Calculate the power spectral density of this time series. Use the `pycbc.psd.welch` method to estimate the psd of this time segment. The psd is then truncated in the time domain to the segment duration and interpolated to the requested sample frequency. Parameters ---------- segment_duration: float Duration in seconds to use for each sample of the spectrum. delta_f : float Frequency spacing to return psd at. flow : float The low frequency cutoff to apply when truncating the inverse spectrum. Returns ------- psd : FrequencySeries Frequency series containing the estimated PSD. """ from pycbc.psd import interpolate, inverse_spectrum_truncation p = self.psd(segment_duration) samples = int(round(p.sample_rate * segment_duration)) p = interpolate(p, delta_f) return inverse_spectrum_truncation(p, samples, low_frequency_cutoff=flow, trunc_method='hann')
def setUp(self): ###### Get data for references analysis of 170817 m = Merger("GW170817") ifos = ['H1', 'V1', 'L1'] self.psds = {} self.data = {} for ifo in ifos: print("Processing {} data".format(ifo)) # Download the gravitational wave data for GW170817 url = "https://dcc.ligo.org/public/0146/P1700349/001/" url += "{}-{}1_LOSC_CLN_4_V1-1187007040-2048.gwf" fname = download_file(url.format(ifo[0], ifo[0]), cache=True) ts = read_frame(fname, "{}:LOSC-STRAIN".format(ifo), start_time=int(m.time - 260), end_time=int(m.time + 40)) ts = highpass(ts, 15.0) ts = resample_to_delta_t(ts, 1.0 / 2048) ts = ts.time_slice(m.time - 112, m.time + 16) self.data[ifo] = ts.to_frequencyseries() psd = interpolate(ts.psd(4), ts.delta_f) psd = inverse_spectrum_truncation(psd, int(4 * psd.sample_rate), trunc_method='hann', low_frequency_cutoff=20.0) self.psds[ifo] = psd self.static = { 'mass1': 1.3757, 'mass2': 1.3757, 'f_lower': 20.0, 'approximant': "TaylorF2", 'polarization': 0, 'ra': 3.44615914, 'dec': -0.40808407, 'tc': 1187008882.42840, } self.variable = ( 'distance', 'inclination', ) self.flow = {'H1': 25, 'L1': 25, 'V1': 25} inclination_prior = SinAngle(inclination=None) distance_prior = Uniform(distance=(10, 100)) tc_prior = Uniform(tc=(m.time - 0.1, m.time + 0.1)) self.prior = JointDistribution(self.variable, inclination_prior, distance_prior) ###### Expected answers # Answer taken from marginalized gaussian model self.q1 = {'distance': 42.0, 'inclination': 2.5} self.a1 = 541.8235746138382 # answer taken from brute marginize pol + phase self.a2 = 542.581 self.pol_samples = 200
def whiten(self, segment_duration, max_filter_duration, trunc_method='hann', remove_corrupted=True, low_frequency_cutoff=None, return_psd=False, **kwds): """ Return a whitened time series Parameters ---------- segment_duration: float Duration in seconds to use for each sample of the spectrum. max_filter_duration : int Maximum length of the time-domain filter in seconds. trunc_method : {None, 'hann'} Function used for truncating the time-domain filter. None produces a hard truncation at `max_filter_len`. remove_corrupted : {True, boolean} If True, the region of the time series corrupted by the whitening is excised before returning. If false, the corrupted regions are not excised and the full time series is returned. low_frequency_cutoff : {None, float} Low frequency cutoff to pass to the inverse spectrum truncation. This should be matched to a known low frequency cutoff of the data if there is one. return_psd : {False, Boolean} Return the estimated and conditioned PSD that was used to whiten the data. kwds : keywords Additional keyword arguments are passed on to the `pycbc.psd.welch` method. Returns ------- whitened_data : TimeSeries The whitened time series """ from pycbc.psd import inverse_spectrum_truncation, interpolate # Estimate the noise spectrum psd = self.psd(segment_duration, **kwds) psd = interpolate(psd, self.delta_f) max_filter_len = int(max_filter_duration * self.sample_rate) # Interpolate and smooth to the desired corruption length psd = inverse_spectrum_truncation(psd, max_filter_len=max_filter_len, low_frequency_cutoff=low_frequency_cutoff, trunc_method=trunc_method) # Whiten the data by the asd white = (self.to_frequencyseries() / psd**0.5).to_timeseries() if remove_corrupted: white = white[int(max_filter_len/2):int(len(self)-max_filter_len/2)] if return_psd: return white, psd return white
def make(conditioned, template, fc, mc, hc, grafica): # We use 4 second samles of our time series in Welch method. psd = conditioned.psd(4) # Now that we have the psd we need to interpolate it to match our data # and then limit the filter length of 1 / PSD. After this, we can # directly use this PSD to filter the data in a controlled manner psd = interpolate(psd, conditioned.delta_f) psd = inverse_spectrum_truncation(psd, 4 * conditioned.sample_rate, low_frequency_cutoff=fc) psd_o = psd snr = matched_filter(template, conditioned, psd=psd, low_frequency_cutoff=fc) sigmasq = sigma(template, psd, low_frequency_cutoff=fc) #print 'Rho_0' #print sigmasq # Remove time corrupted by the template filter and the psd filter # We remove 4 seonds at the beginning and end for the PSD filtering # And we remove 4 additional seconds at the beginning to account for # the template length (this is somewhat generous for # so short a template). A longer signal such as from a BNS, would # require much more padding at the beginning of the vector. snr = snr.crop(4, 4) if grafica == 1: pylab.figure('Matched_filter_output') pylab.title('Matched filter output') pylab.grid() pylab.plot(snr.sample_times, abs(snr)) pylab.ylabel('Signal-to-noise ratio') pylab.xlabel('Time (s)') pylab.show() peak = abs(snr).numpy().argmax() snrp = snr[peak] time = snr.sample_times[peak] #print 'Tiempo de SNR máximo' #print time #print 'SNR recuperado' #print abs(snrp) return snr, psd, peak, time, snrp, psd_o, sigmasq
def whiten(self, segment_duration, max_filter_duration, trunc_method='hann', remove_corrupted=True, low_frequency_cutoff=None, return_psd=False, **kwds): """ Return a whitened time series Parameters ---------- segment_duration: float Duration in seconds to use for each sample of the spectrum. max_filter_duration : int Maximum length of the time-domain filter in seconds. trunc_method : {None, 'hann'} Function used for truncating the time-domain filter. None produces a hard truncation at `max_filter_len`. remove_corrupted : {True, boolean} If True, the region of the time series corrupted by the whitening is excised before returning. If false, the corrupted regions are not excised and the full time series is returned. low_frequency_cutoff : {None, float} Low frequency cutoff to pass to the inverse spectrum truncation. This should be matched to a known low frequency cutoff of the data if there is one. return_psd : {False, Boolean} Return the estimated and conditioned PSD that was used to whiten the data. kwds : keywords Additional keyword arguments are passed on to the `pycbc.psd.welch` method. Returns ------- whitened_data : TimeSeries The whitened time series """ from pycbc.psd import inverse_spectrum_truncation, interpolate # Estimate the noise spectrum psd = self.psd(segment_duration, **kwds) psd = interpolate(psd, self.delta_f) max_filter_len = int(max_filter_duration * self.sample_rate) # Interpolate and smooth to the desired corruption length psd = inverse_spectrum_truncation(psd, max_filter_len=max_filter_len, low_frequency_cutoff=low_frequency_cutoff, trunc_method=trunc_method) # Whiten the data by the asd white = (self.to_frequencyseries() / psd**0.5).to_timeseries() if remove_corrupted: white = white[int(max_filter_len/2):int(len(self)-max_filter_len/2)] if return_psd: return white, psd return white
def setUp(self, *args): self.context = _context self.scheme = _scheme self.tolerance = 1e-6 xr = numpy.random.uniform(low=-1, high=1.0, size=2**20) xi = numpy.random.uniform(low=-1, high=1.0, size=2**20) self.x = Array(xr + xi * 1.0j, dtype=complex64) self.z = zeros(2**20, dtype=float32) for i in range(0, 4): trusted_accum(self.z, self.x) m = Merger("GW170814") ifos = ['H1', 'L1', 'V1'] data = {} psd = {} for ifo in ifos: # Read in and condition the data and measure PSD ts = m.strain(ifo).highpass_fir(15, 512) data[ifo] = resample_to_delta_t(ts, 1.0 / 2048).crop(2, 2) p = data[ifo].psd(2) p = interpolate(p, data[ifo].delta_f) p = inverse_spectrum_truncation(p, int(2 * data[ifo].sample_rate), low_frequency_cutoff=15.0) psd[ifo] = p hp, _ = get_fd_waveform(approximant="IMRPhenomD", mass1=31.36, mass2=31.36, f_lower=20.0, delta_f=data[ifo].delta_f) hp.resize(len(psd[ifo])) # For each ifo use this template to calculate the SNR time series snr = {} snr_unnorm = {} norm = {} corr = {} for ifo in ifos: snr_unnorm[ifo], corr[ifo], norm[ifo] = \ matched_filter_core(hp, data[ifo], psd=psd[ifo], low_frequency_cutoff=20) snr[ifo] = snr_unnorm[ifo] * norm[ifo] self.snr = snr self.snr_unnorm = snr_unnorm self.norm = norm self.corr = corr self.hp = hp self.data = data self.psd = psd self.ifos = ifos
def setUp(self,*args): self.context = _context self.scheme = _scheme self.tolerance = 1e-6 xr = numpy.random.uniform(low=-1, high=1.0, size=2**20) xi = numpy.random.uniform(low=-1, high=1.0, size=2**20) self.x = Array(xr + xi * 1.0j, dtype=complex64) self.z = zeros(2**20, dtype=float32) for i in range(0, 4): trusted_accum(self.z, self.x) m = Merger("GW170814") ifos = ['H1', 'L1', 'V1'] data = {} psd = {} for ifo in ifos: # Read in and condition the data and measure PSD ts = m.strain(ifo).highpass_fir(15, 512) data[ifo] = resample_to_delta_t(ts, 1.0/2048).crop(2, 2) p = data[ifo].psd(2) p = interpolate(p, data[ifo].delta_f) p = inverse_spectrum_truncation(p, 2 * data[ifo].sample_rate, low_frequency_cutoff=15.0) psd[ifo] = p hp, _ = get_fd_waveform(approximant="IMRPhenomD", mass1=31.36, mass2=31.36, f_lower=20.0, delta_f=data[ifo].delta_f) hp.resize(len(psd[ifo])) # For each ifo use this template to calculate the SNR time series snr = {} snr_unnorm = {} norm = {} corr = {} for ifo in ifos: snr_unnorm[ifo], corr[ifo], norm[ifo] = \ matched_filter_core(hp, data[ifo], psd=psd[ifo], low_frequency_cutoff=20) snr[ifo] = snr_unnorm[ifo] * norm[ifo] self.snr = snr self.snr_unnorm = snr_unnorm self.norm = norm self.corr = corr self.hp = hp self.data = data self.psd = psd self.ifos = ifos
def signal_whiten(psd, signal, segment_duration, max_filter_duration, trunc_method='hann', low_frequency_cutoff=None): # Estimate the noise spectrum, no need for segment_duration, because we already get in line 193. psd = interpolate(psd, signal.delta_f) max_filter_len = int(max_filter_duration * signal.sample_rate) # Interpolate and smooth to the desired corruption length psd = inverse_spectrum_truncation(psd, max_filter_len=max_filter_len, low_frequency_cutoff=low_frequency_cutoff, trunc_method=trunc_method) # Whiten the data by the asd white = (signal.to_frequencyseries() / psd**0.5).to_timeseries() return white
def whiten_data(strain_list, psd, low_freq_cutoff=20.0): org_type = type(strain_list) if not org_type == list: strain_list = [strain_list] #set to strain[0].delta_f DF = 1.0 / strain_list[0].delta_t / ( 4 * strain_list[0].sample_rate ) #This is the definition of delta_f from the TimeSeries.whiten in the welchs method. #set to len(strain_list[0]) / 2 + 1 F_LEN = int(4 * strain_list[0].sample_rate / 2 + 1) get_hyper_waveform_defaults low_freq_diff = 20 if low_freq_cutoff > low_freq_diff: tmp_psd = aLIGOZeroDetHighPower(length=F_LEN, delta_f=DF, low_freq_cutoff=low_freq_cutoff - low_freq_diff) else: tmp_psd = aLIGOZeroDetHighPower(length=F_LEN, delta_f=DF, low_freq_cutoff=0) for i in range(len(strain_list)): max_filter_len = int( 4 * strain_list[i].sample_rate) #To replicate TimeSeries.whiten tmp_psd_2 = interpolate( tmp_psd, strain_list[i].delta_f) #To replicate TimeSeries.whiten tmp_psd_2 = inverse_spectrum_truncation( tmp_psd_2, max_filter_len=max_filter_len, low_frequency_cutoff=low_freq_cutoff, trunc_method='hann') strain_list[i] = (strain_list[i].to_frequencyseries() / tmp_psd_2**0.5).to_timeseries() strain_list[i] = strain_list[i][int(float(max_filter_len) / 2):int( len(strain_list[i]) - float(max_filter_len) / 2)] #To replicate TimeSeries.whiten if not org_type == list: return (strain_list[0]) else: return (strain_list)
def whiten_data_new(strain_list, low_freq_cutoff=20., max_filter_duration=4., psd=None): org_type = type(strain_list) if not org_type == list: strain_list = [strain_list] ret = [] for strain in strain_list: df = strain.delta_f f_len = int(len(strain) / 2) + 1 if psd is None: psd = aLIGOZeroDetHighPower(length=f_len, delta_f=df, low_freq_cutoff=low_freq_cutoff - 2.) else: if not len(psd) == f_len: msg = 'Length of PSD does not match data.' raise ValueError(msg) elif not psd.delta_f == df: psd = interpolate(psd, df) max_filter_len = int( max_filter_duration * strain.sample_rate) #Cut out the beginning and end psd = inverse_spectrum_truncation(psd, max_filter_len=max_filter_len, low_frequency_cutoff=low_freq_cutoff, trunc_method='hann') f_strain = strain.to_frequencyseries() kmin = int(low_freq_cutoff / df) f_strain.data[:kmin] = 0 f_strain.data[-1] = 0 f_strain.data[kmin:] /= psd[kmin:]**0.5 strain = f_strain.to_timeseries() ret.append(strain[max_filter_len:len(strain) - max_filter_len]) if not org_type == list: return (ret[0]) else: return (ret)
# ========================= # = Precondition the data = # ========================= # Remove low frequency noise hoft = highpass(hoft, 15.0) hoft = resample_to_delta_t(hoft, 1./2048.) # Remove 2 seconds from the start and the end to protect against edge effects conditioned = hoft.crop(2, 2) # Calculate PSD of data psd = conditioned.psd(4) psd = interpolate(psd, conditioned.delta_f) psd = inverse_spectrum_truncation(psd, 4 * conditioned.sample_rate, low_frequency_cutoff=15) psdlist[i] = psd # Generate a zeros frequency series the same length as the psd filter = FrequencySeries(numpy.zeros(len(psd)), 1, dtype=numpy.complex128) # Generate the waveform filter with given params filter = get_waveform_filter(filter, approximant=params.apx, mass1=m1, mass2=m2, sp1z=params.sp1z, sp2z=params.sp2z,
def fake_noisyGW(tab_waveform, seed, run, fs): nbr_sec = 15 nbr_sec_begin = 7 nbr_sec_end = 8 NFFT = 1 * fs fmin = 15 fmax = 200000 print("########################") print("creation of noisy signal") tab_whiten = [] tab_snr = [] random.seed(seed) for i in tqdm(range(0, len(tab_waveform))): #creation of the fake noise seed_local = random.randint(1, 1000) fake_noise = fake_gaussian_noise(nbr_sec, seed_local, fs, fmin) # injection of GW into the noise noisyGW = np.empty([len(fake_noise)]) # creation of the template (for SNR) template = np.zeros([len(fake_noise) ]) # idem pure_GW but with GW in the end(for SNR) # creation of pureGW (no use exept plot) pureGW = np.zeros([len(fake_noise)]) #pure GW in nbr_sec of 0 # injection of GW into noise k = 0 for j in range(0, len(fake_noise)): if j >= fs * nbr_sec_begin and j < fs * nbr_sec_end: noisyGW[j] = fake_noise[j] + tab_waveform[i][0][k] pureGW[j] = tab_waveform[i][0][k] k = k + 1 else: noisyGW[j] = fake_noise[j] pureGW[j] = 0 # creation of the template k = 0 for j in range(0, len(fake_noise)): if j < len(fake_noise) - len(tab_waveform[i][0]): template[j] = 0 else: template[j] = tab_waveform[i][0][k] k += 1 ########## the calcule of the SNR ############## # First we have to calculate the SNR (the following step is neccessary) # we calculate the SNR of noisy GW noisyGW_timeseries = TimeSeries(noisyGW, delta_t=tab_waveform[i][17]) # we need to calculate again the psd # we use just 4 sec of data to calulate psd (weltch method) psd = noisyGW_timeseries.psd(4) # we need to interpolate our psd on all data psd = interpolate(psd, noisyGW_timeseries.delta_f) # we need to inform that the noise was creat with a lowfrequency cutoff psd = inverse_spectrum_truncation(psd, 4 * noisyGW_timeseries.sample_rate, low_frequency_cutoff=fmin) #Second we calcule the SNR # we calculate the SNR template_timeseries = TimeSeries(template, delta_t=tab_waveform[i][17]) noisyGW_timeseries = TimeSeries(noisyGW, delta_t=tab_waveform[i][17]) if template.max(0) == 0: snrp = 0 tab_snr = tab_snr + [snrp] else: snr = matched_filter(template_timeseries, noisyGW_timeseries, psd=psd, low_frequency_cutoff=fmin) # 4+1 sec remove at the begining of SNR and 4 seconde remove at the end # +1 is because the length of the template is one sec snr = snr.crop(4 + 1, 4) peak = abs(snr).numpy().argmax() snrp = snr[peak] tab_snr = tab_snr + [abs(snrp)] # on fait le withening # I) methode pycbc # data and then flattening the frequency response. # (1) The first option sets the duration in seconds of each # sample of the data used as part of the PSD estimate. # (2) The second option sets the duration of the filter to apply #whitened = noisyGW_timeseries.whiten(15, 4) # on fait aussi un bandpass #whitened_bp = whitened.highpass_fir(30, 512).lowpass_fir(250, 512) # le whitening pycbc donne meme chose mais modifie la longueur # par facilite je reprends l autre whitening # II) method numpy Pxx, freqs = mlab.psd(noisyGW, Fs=freq, NFFT=NFFT, noverlap=NFFT / 2, window=np.blackman(NFFT)) # We will use interpolations of the ASDs computed above for whitening: psd_numpy = interp1d(freqs, Pxx) whitened = whitening(noisyGW, psd_numpy, tab_waveform[i][17]) # We need to suppress the high frequencies with some bandpassing: high_freq = 600. low_freq = fmin bb, ab = butter(4, [low_freq * 2. / fs, high_freq * 2. / fs], btype='band') whitened_bp = filtfilt(bb, ab, whitened) # Select the secon containing the GW withen_bp_onesec = np.empty([fs]) for j in range(0, len(withen_bp_onesec)): withen_bp_onesec[j] = whitened_bp[nbr_sec_begin * fs + j] tab_whiten = tab_whiten + [withen_bp_onesec] ''' ###################################################################### ### PLOT this is no necessary is just to be sure everithing is OK ### # just some plot to see if everything looks fine temps = np.empty([len(fake_noise)]) for tps in range(0, len(fake_noise)): temps[tps] = tps temps_onesec = np.empty([len(tab_waveform[i][0])]) for tps in range(0, len(tab_waveform[i][0])): temps_onesec[tps] = tps # noisy GW plt.figure("noisyGW") plt.plot(temps, noisyGW, 'b') plt.plot(temps, pureGW, 'r') plt.show() # template plt.figure("template") plt.plot(temps, template, 'r') plt.show() # the GW plt.figure("template") plt.plot(temps_onesec, tab_waveform[i][0], 'r') plt.show() # psd plt.figure("psd") plt.loglog(psd.sample_frequencies, psd) pylab.xlim(10, 2048) pylab.show() # SNR plt.figure("SNR") pylab.plot(snr.sample_times, abs(snr)) pylab.grid() pylab.show() # Whitening whitened_bp = TimeSeries(whitened_bp, delta_t=tab_waveform[i][17]) plt.figure("whitening") pylab.plot(whitened_bp.sample_times, whitened_bp) #pylab.xlim(7,8) pylab.grid() pylab.show() #whitening onesec whiten_bp_onesec_timeseries = TimeSeries(withen_bp_onesec, delta_t=tab_waveform[i][17]) plt.figure("whitening_one_sec") pylab.plot(whiten_bp_onesec_timeseries.sample_times, whiten_bp_onesec_timeseries) pylab.xlim(0.6, 1.0) pylab.grid() pylab.show() ################################################################### ''' tab_snr = np.asarray(tab_snr) tab_whiten = np.asarray(tab_whiten) # we create the big array (with all the parameters,...) tab_waveformnoisy = copy.deepcopy(tab_waveform) for i in range(0, len(tab_waveformnoisy)): tab_waveformnoisy[i][0] = tab_whiten[i] return tab_waveformnoisy, tab_snr
def whiten_data(strain_list, low_freq_cutoff=20., max_filter_duration=4., psd=None): """Returns the data whitened by the PSD. Arguments --------- strain_list : list of pycbc.TimeSeries or pycbc.TimeSeries The data that should be whitened. low_freq_cutoff : {float, 20.} The lowest frequency that is considered during calculations. It must be >= than the lowest frequency where the PSD is not zero. Unit: hertz max_filter_duration : {float, 4.} The duration to which the PSD is truncated to in the time-domain. The amount of time is removed from both the beginning and end of the input data to avoid wrap-around errors. Unit: seconds psd : {None or pycbc.FrequencySeries, None} The PSD that should be used to whiten the data. If set to None the pycbc.psd.aLIGOZeroDetHighPower PSD will be used. If a PSD is provided which does not fit the delta_f of the data, it will be interpolated to fit. Returns ------- list of pycbc.TimeSeries or TimeSeries Depending on the input type it will return a list of TimeSeries or a single TimeSeries. The data contained in this time series is the whitened input data, where the inital and final seconds as specified by max_filter_duration are removed. """ org_type = type(strain_list) if not org_type == list: strain_list = [strain_list] ret = [] for strain in strain_list: df = strain.delta_f f_len = int(len(strain) / 2) + 1 if psd is None: psd = aLIGOZeroDetHighPower(length=f_len, delta_f=df, low_freq_cutoff=low_freq_cutoff - 2.) else: if not len(psd) == f_len: msg = 'Length of PSD does not match data.' raise ValueError(msg) elif not psd.delta_f == df: psd = interpolate(psd, df) max_filter_len = int( max_filter_duration * strain.sample_rate) #Cut out the beginning and end psd = inverse_spectrum_truncation(psd, max_filter_len=max_filter_len, low_frequency_cutoff=low_freq_cutoff, trunc_method='hann') f_strain = strain.to_frequencyseries() kmin = int(low_freq_cutoff / df) f_strain.data[:kmin] = 0 f_strain.data[-1] = 0 f_strain.data[kmin:] /= psd[kmin:]**0.5 strain = f_strain.to_timeseries() ret.append(strain[max_filter_len:len(strain) - max_filter_len]) if not org_type == list: return (ret[0]) else: return (ret)
# Remove time corrupted by the high pass filter strain[ifo] = strain[ifo].crop(4,4) # Also create a frequency domain version of the data stilde[ifo] = strain[ifo].to_frequencyseries() #print (strain.delta_t) pylab.plot(strain['H1'].sample_times, strain['H1']) pylab.xlabel('Time (s)') pylab.show() from pycbc.psd import interpolate, inverse_spectrum_truncation psds = {} for ifo in ['L1','H1']: # Calculate a psd from the data. We'll use 2s segments in a median -welch style estimate# We then interpolate the PSD to the desired frequencystep. psds[ifo] = interpolate(strain[ifo].psd(2), stilde[ifo].delta_f) # We explicitly control how much data will be corruptedbyoverwhitening the data later on# In this case we choose 2 seconds. psds[ifo] = inverse_spectrum_truncation(psds[ifo],int(2*strain[ifo].sample_rate),low_frequency_cutoff=15.0,trunc_method='hann') pylab.loglog(psds[ifo].sample_frequencies, psds[ifo],label=ifo) pylab.xlim(20,1024) pylab.ylim(1e-47,1e-42) pylab.legend() from pycbc.waveform import get_fd_waveform from pycbc.filter import matched_filter from pycbc.conversions import mass1_from_mchirp_q import numpy # We will try different component masses and see whichgives us thelargest masses = numpy.arange(1.3,1.5,.01) # Variables to store when we've found the max hmax, smax, tmax, mmax, nsnr = None, {}, {},0,0 snrs = [] for m in masses: