Ejemplo n.º 1
0
def get_idx_from_plot_pick(deskew_row,plot_pick,flip=False,dist_e=None):
    data_file_path = os.path.join(deskew_row["data_dir"],deskew_row["comp_name"])
    data_df = utl.open_mag_file(data_file_path)

    if flip: projected_distances = utl.calc_projected_distance(deskew_row['inter_lon'],deskew_row['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),(180+deskew_row['strike'])%360)
    else: projected_distances = utl.calc_projected_distance(deskew_row['inter_lon'],deskew_row['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),deskew_row['strike'])

    min_idx = (projected_distances["dist"]-plot_pick).abs().idxmin()

    return min_idx,projected_distances["dist"][min_idx]
Ejemplo n.º 2
0
 def on_sub_btn(self, event):
     if self.parent.user_warning(
             "WARNING: This is a non-reversable function please be sure this is what you want to do. Also if you are not looking at the 0 phase profile this may have unexpected results, please view the 0 phase profile before hitting OK."
     ):
         try:
             self.deg = int(self.deg_box.GetValue())
         except (ValueError, TypeError) as e:
             self.parent.user_warning(
                 "Degree of polynomial must be a natural number")
         try:
             mag_path = os.path.join(self.parent.dsk_row["data_dir"],
                                     self.parent.dsk_row["comp_name"])
             mag_df = utl.open_mag_file(mag_path)
             self.projected_distances = utl.calc_projected_distance(
                 self.parent.dsk_row["inter_lon"],
                 self.parent.dsk_row["inter_lat"], mag_df["lon"],
                 mag_df["lat"], self.parent.dsk_row["strike"])["dist"]
             shifted_mag = mag_df['mag'].tolist()
             self.pols = np.polyfit(self.projected_distances, shifted_mag,
                                    self.deg)
             self.poly = np.polyval(self.pols, self.projected_distances)
             mag_df["mag"] = mag_df["mag"] - self.poly
             utl.write_mag_file_df(mag_df, mag_path)
         except AttributeError:
             return
     self.on_plot_btn(event)
Ejemplo n.º 3
0
 def plot_tracer_on_self_and_parent(self, dsk_row, lonlat):
     proj_locs = utl.calc_projected_distance(dsk_row['inter_lon'],
                                             dsk_row['inter_lat'],
                                             [lonlat[0]], [lonlat[1]],
                                             dsk_row['strike'])
     self.plot_tracer_point(dsk_row,
                            proj_locs.iloc[0]["dist"],
                            color="red",
                            marker="o",
                            s=10)
     self.parent.plot_tracer_point(proj_locs.iloc[0]["dist"],
                                   linestyle='--',
                                   color='red',
                                   alpha=.5)
Ejemplo n.º 4
0
    def on_select_dleft_click(self, event):
        try:
            dsk_row = self.parent.dsk_row
        except AttributeError:
            event.Skip()
            return

        pos = event.GetPosition()
        width, height = self.canvas.get_width_height()
        pos = [pos[0], height - pos[1]]
        pos = self.ax.transData.inverted().transform(pos)

        lonlat = ccrs.PlateCarree().transform_point(*pos, self.proj)
        proj_locs = utl.calc_projected_distance(dsk_row['inter_lon'],
                                                dsk_row['inter_lat'],
                                                [lonlat[0]], [lonlat[1]],
                                                dsk_row['strike'])
        self.parent.set_new_intercept(proj_locs.iloc[0]["dist"])
        self.parent.update(event)
        self.update()
Ejemplo n.º 5
0
 def fit_poly(self):
     try:
         self.deg = int(self.deg_box.GetValue())
     except (ValueError, TypeError) as e:
         self.parent.user_warning(
             "Degree of polynomial must be a natural number")
     try:
         mag_path = os.path.join(self.parent.dsk_row["data_dir"],
                                 self.parent.dsk_row["comp_name"])
         mag_df = utl.open_mag_file(mag_path)
         self.projected_distances = utl.calc_projected_distance(
             self.parent.dsk_row["inter_lon"],
             self.parent.dsk_row["inter_lat"], mag_df["lon"], mag_df["lat"],
             (180 + self.parent.dsk_row["strike"]) % 360)["dist"]
         shifted_mag = sk.phase_shift_data(
             mag_df['mag'].tolist(), self.parent.dsk_row["phase_shift"])
         self.pols = np.polyfit(self.projected_distances, shifted_mag,
                                self.deg)
         self.poly = np.polyval(self.pols, self.projected_distances)
     except AttributeError:
         return
Ejemplo n.º 6
0
def old_get_idx_from_plot_pick(deskew_row,plot_pick,dist_e=.01):
    drow,correction=deskew_row,plot_pick

    data_file_path = os.path.join(drow["data_dir"],drow["comp_name"])
    data_df = pd.read_csv(data_file_path,names=["dist","idk","mag","lat","lon"],delim_whitespace=True)

    projected_distances = utl.calc_projected_distance(drow['inter_lon'],drow['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),drow['strike'])

    found_dist=False
    for j,row in projected_distances.iterrows():
        if row['dist']>=correction-dist_e and row['dist']<=correction+dist_e:
            picked_idx = j
            picked_distance = row['dist']
            found_dist=True
            break

    if found_dist:
        print("found lat lon of %s at a distance %.3f"%(drow["comp_name"],picked_distance))
    else:
        print("couldn't find picked distance in datafile to calculate lat and lon for %s"%drow["comp_name"]); return (drow['inter_lon'],drow['inter_lat'],0)

    return picked_idx,picked_distance
Ejemplo n.º 7
0
    def draw_figures(self):

        ####################################################Get Values
        dsk_row = self.parent.dsk_row
        track = self.parent.track
        ddis = float(self.parent.samp_dis_box.GetValue())
        if ddis==0: self.parent.user_warning("Synthetic is required for comparision of phase, start by initalilzing a synthetic"); return
        synth_dis = self.parent.dis_synth
        synth_mag = self.parent.synth

        filter_type = self.filter_type_box.GetValue()
        lowcut = float(self.lowcut_box.GetValue())
        highcut = float(self.highcut_box.GetValue())
        order = int(self.order_box.GetValue())

        left_bound = float(self.low_bound_box.GetValue())
        right_bound = float(self.high_bound_box.GetValue())
        aero_diff = float(self.aero_diff_box.GetValue())

        left_idx = np.argmin(np.abs(synth_dis-left_bound))
        right_idx = np.argmin(np.abs(synth_dis-right_bound))
        left_idx,right_idx = min([left_idx,right_idx]),max([left_idx,right_idx])

        bin_range,bin_num = (-180,180),120

        ###################################################Filter Data

        data_path = os.path.join(dsk_row["data_dir"],dsk_row["comp_name"])
        data_df = utl.open_mag_file(data_path)
        projected_distances = utl.calc_projected_distance(dsk_row['inter_lon'],dsk_row['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),(180+dsk_row['strike'])%360)
        shifted_mag = sk.phase_shift_data(data_df["mag"],dsk_row["phase_shift"])
        if np.any(np.diff(projected_distances["dist"])<0): itshifted_mag = np.interp(-synth_dis,-projected_distances["dist"],shifted_mag)
        else: itshifted_mag = np.interp(synth_dis,projected_distances["dist"],shifted_mag)
        fitshifted_mag = self.filters[filter_type](itshifted_mag,lowcut,highcut,fs=1/ddis,order=order)

        ###################################################Actual Plotting

        outer = gridspec.GridSpec(4, 1)

        ###################################################Axis 0: Magnetic profiles
        self.ax0 = self.fig.add_subplot(outer[0])

        if self.parent.show_other_comp: #Handle Other Aeromag Component

            if dsk_row["track_type"]=="aero":
                if "Ed.lp" in track:
                    other_track = track.replace("Ed.lp","Vd.lp")
                    total_track = track.replace("Ed.lp","Td.lp")
                    other_phase = dsk_row["phase_shift"]-90
                elif "Hd.lp" in track:
                    other_track = track.replace("Hd.lp","Vd.lp")
                    total_track = track.replace("Hd.lp","Td.lp")
                    other_phase = dsk_row["phase_shift"]-90
                elif "Vd.lp" in track:
                    other_track = track.replace("Vd.lp","Ed.lp")
                    total_track = track.replace("Vd.lp","Td.lp")
                    if other_track not in self.parent.deskew_df["comp_name"].tolist(): other_track = track.replace("Vd.lp","Hd.lp")
                    other_phase = dsk_row["phase_shift"]+90
                else: self.parent.user_warning("Improperly named component files should have either Ed.lp, Hd.lp, or Vd.lp got: %s"%track); return
                oth_row = self.parent.deskew_df[self.parent.deskew_df["comp_name"]==other_track].iloc[0]

                oth_data_path = os.path.join(oth_row["data_dir"],oth_row["comp_name"])
                tot_data_path = os.path.join(oth_row["data_dir"],total_track) #Should be in same place

                oth_data_df = utl.open_mag_file(oth_data_path)
                oth_shifted_mag = sk.phase_shift_data(oth_data_df["mag"],other_phase)
                if np.any(np.diff(projected_distances["dist"])<0): oth_itshifted_mag = np.interp(-synth_dis,-projected_distances["dist"],oth_shifted_mag)
                else: oth_itshifted_mag = np.interp(synth_dis,projected_distances["dist"],oth_data_df)
                oth_fitshifted_mag = self.filters[filter_type](oth_itshifted_mag,lowcut,highcut,fs=1/ddis,order=order)
                if filter_type=="None": psk.plot_skewness_data(oth_row,other_phase,self.ax0,xlims=[None,None],color='darkgreen',zorder=2,picker=True,alpha=.7,return_objects=True,flip=True)
                else: self.ax0.plot(synth_dis,oth_fitshifted_mag,color="#299C29",zorder=3,alpha=.6)

                tot_data_df = utl.open_mag_file(tot_data_path)
                if np.any(np.diff(projected_distances["dist"])<0): tot_imag = np.interp(-synth_dis,-projected_distances["dist"],tot_data_df["mag"])
                else: tot_imag = np.interp(synth_dis,projected_distances["dist"],tot_data_df["mag"])
                tot_fimag = self.filters[filter_type](tot_imag,lowcut,highcut,fs=1/ddis,order=order)

        if filter_type=="None": psk.plot_skewness_data(dsk_row,dsk_row["phase_shift"],self.ax0,xlims=[None,None],zorder=3,picker=True,return_objects=True,flip=True)
        else: self.ax0.plot(synth_dis,fitshifted_mag,color="#7F7D7D",zorder=3,alpha=.6)
        self.ax0.plot(self.parent.dis_synth,self.parent.synth,'r-',alpha=.4,zorder=1)
        self.ax0.set_ylabel("Magnetic Profiles")
#        self.ax0.get_xaxis().set_ticklabels([])

        ###################################################Axis 1/2: Phase Angles and Differences

        self.ax1 = self.fig.add_subplot(outer[1], sharex=self.ax0)
        self.ax2 = self.fig.add_subplot(outer[2], sharex=self.ax0)

        ###################################################Calculate: Phase Differences
        trimmed_dis = synth_dis[left_idx:right_idx]
        trimmed_synth = synth_mag[left_idx:right_idx]
        trimmed_fitshifted_mag = fitshifted_mag[left_idx:right_idx]

        al_data = np.angle(hilbert(fitshifted_mag),deg=False)[left_idx:right_idx]
        al_synth = np.angle(hilbert(np.real(synth_mag)),deg=False)[left_idx:right_idx]

        data_synth_diff = phase_diff_func(al_synth,al_data)

        if self.parent.show_other_comp and dsk_row["track_type"]=="aero":
            trimmed_oth_fitshifted_mag = oth_fitshifted_mag[left_idx:right_idx]
            al_oth = np.angle(hilbert(oth_fitshifted_mag),deg=False)[left_idx:right_idx]

            oth_synth_diff = phase_diff_func(al_synth,al_oth)
            oth_data_diff = phase_diff_func(al_oth,al_data)

            if abs(aero_diff) > 0:
                idx = ma.array(np.abs(oth_data_diff)<aero_diff)

                self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_oth[~idx])),color="darkgreen",linestyle=":")

                self.ax2.plot((trimmed_dis[~idx]),(oth_synth_diff[~idx]),color="tab:pink",alpha=.8,linestyle=":")
                self.ax2.plot((trimmed_dis[~idx]),(oth_data_diff[~idx]),color="tab:grey",alpha=.8,linestyle=":")

                self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_data[~idx])),color="k",linestyle=":")
                self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_synth[~idx])),color="r",linestyle=":")

                self.ax2.plot((trimmed_dis[~idx]),(data_synth_diff[~idx]),color="tab:red",alpha=.8,linestyle=":")

#                import pdb; pdb.set_trace()
#                not_trimmed_dis = (trimmed_dis[~idx])
#                not_trimmed_dis[np.diff(~idx,prepend=[0])] = ma.masked
#                not_al_data = (al_data[~idx])
#                not_al_data[np.diff(~idx)] = ma.masked
#                not_al_synth = (al_synth[~idx])
#                not_al_synth[np.diff(~idx)] = ma.masked
#                not_al_oth = (al_oth[~idx])
#                not_al_oth[np.diff(~idx)] = ma.masked
#                not_data_synth_diff = (data_synth_diff[~idx])
#                not_data_synth_diff[np.diff(~idx)] = ma.masked
#                not_oth_synth_diff = (oth_synth_diff[~idx])
#                not_oth_synth_diff[np.diff(~idx)] = ma.masked
#                not_oth_data_diff = (oth_data_diff[~idx])
#                not_oth_data_diff[np.diff(~idx)] = ma.masked
                trimmed_dis = (trimmed_dis[idx])
                al_data = (al_data[idx])
                al_synth = (al_synth[idx])
                al_oth = (al_oth[idx])
                data_synth_diff = (data_synth_diff[idx])
                oth_synth_diff = (oth_synth_diff[idx])
                oth_data_diff = (oth_data_diff[idx])

            self.ax1.plot(trimmed_dis,np.rad2deg(al_oth),color="darkgreen")

            self.ax2.plot(trimmed_dis,oth_synth_diff,color="tab:pink",alpha=.8)
            self.ax2.plot(trimmed_dis,oth_data_diff,color="tab:grey",alpha=.8)

        self.ax1.plot(trimmed_dis,np.rad2deg(al_data),color="k")
        self.ax1.plot(trimmed_dis,np.rad2deg(al_synth),color="r")

        self.ax2.plot(trimmed_dis,data_synth_diff,color="tab:red",alpha=.8)
#        self.ax2.get_xaxis.set_ticklabels
        self.ax0.set_xlim(*self.parent.ax.get_xlim())
        self.ax0.set_ylim(*self.parent.ax.get_ylim())

        self.ax1.set_ylabel("Phase Angles")
        self.ax2.set_ylabel("Phase Differences")


        ###################################################Axis 2.1: Power Spectrum
#        inner = gridspec.GridSpecFromSubplotSpec(1, 2, subplot_spec=outer[2])#, hspace=0.)
#        self.ax2 = self.fig.add_subplot(inner[0])


        ###################################################Axis 2.2: Phase Statistics
        self.ax3 = self.fig.add_subplot(outer[3])

        if self.parent.show_other_comp and dsk_row["track_type"]=="aero":
            self.ax3.hist(oth_synth_diff,range=bin_range,bins=bin_num,color="tab:pink",alpha=.5,zorder=2)
            self.ax3.hist(oth_data_diff,range=bin_range,bins=bin_num,color="tab:grey",alpha=.5,zorder=1)

        self.ax3.hist(data_synth_diff,range=bin_range,bins=bin_num,color="tab:red",alpha=.5,zorder=3)
        self.ax3.axvline(np.median(data_synth_diff),color="k",alpha=.5,zorder=5,linestyle=":")
        self.ax3.axvline(np.mean(data_synth_diff),color="k",alpha=.5,zorder=5)
        self.ax3.axvspan(np.mean(data_synth_diff)-np.std(data_synth_diff),np.mean(data_synth_diff)+np.std(data_synth_diff),color="tab:grey",alpha=.3,zorder=0)

        self.ax3.annotate(r"$\theta_{mean}$ = $%.1f^\circ \pm %.1f^\circ$"%(np.mean(data_synth_diff),np.std(data_synth_diff)) + "\n" + r"$\theta_{median}$ = %.1f$^\circ$"%np.median(data_synth_diff),xy=(0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,va='top',ha='left')

        self.ax3.set_ylabel(r"$\Delta \theta$ Count")

        self.fig.suptitle("%s\n%s\n"%(dsk_row["sz_name"],track))

        ###################################################Power Figure

        N = (right_idx-left_idx) #Length of signal in distance domain
        NW = 3 #following Parker and O'brien '97 and HJ-Gordon '03 we use a time-bandwith product of 6 (Nw is half)
        Ns = 5 #Number of points to use in running average smoothing

        #Handle Distance Domain
#        import pdb; pdb.set_trace()
        Sk_complex, weights, eigenvalues=pmtm(itshifted_mag[left_idx:right_idx], NW=NW, NFFT=N, show=False)
        Sk = np.abs(Sk_complex)**2
        smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1]
#        smoothed_tshifted_freq = np.convolve(smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing
        tdata_freqs = np.linspace(0.0, 1.0/(2.0*ddis), N-N//2) #0 to Nyquest

        self.power_ax = self.power_fig.add_subplot(111)

        if self.parent.show_other_comp and dsk_row["track_type"]=="aero":
            Sk_complex, weights, eigenvalues=pmtm(oth_itshifted_mag[left_idx:right_idx], NW=NW, NFFT=N, show=False)
            Sk = np.abs(Sk_complex)**2
            oth_smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1]
#            oth_smoothed_tshifted_freq = np.convolve(oth_smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing
            self.power_ax.semilogy(tdata_freqs, oth_smoothed_tshifted_freq, color="darkgreen")
#            self.power_ax.semilogy(tdata_freqs, oth_smoothed_tshifted_freq+smoothed_tshifted_freq, color="grey")


            Sk_complex, weights, eigenvalues=pmtm(tot_imag[left_idx:right_idx], NW=NW, NFFT=N, show=False)
            Sk = np.abs(Sk_complex)**2
            tot_smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1]
#            tot_smoothed_tshifted_freq = np.convolve(tot_smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing
            self.power_ax.semilogy(tdata_freqs, tot_smoothed_tshifted_freq, color="tab:orange")

        #Old Numpy Method
#        synth_freqs = np.fft.fftfreq(len(synth_dis[left_idx:right_idx]),ddis)
#        tdata_freqs = np.fft.fftfreq(len(shifted_mag[left_idx:right_idx]),ddis)
#        tshifted_freq = np.fft.fft(shifted_mag[left_idx:right_idx])
#        fitshifted_freq = np.fft.fft(fitshifted_mag[left_idx:right_idx])
#        tsynth_freq = np.fft.fft(synth_mag[left_idx:right_idx])

        self.power_ax.semilogy(tdata_freqs, smoothed_tshifted_freq, color="k",zorder=100)

#        self.power_ax.semilogy(tdata_freqs, np.abs(tshifted_freq), color="k")
#        self.power_ax.plot(synth_freqs, np.abs(fitshifted_freq), color="#7F7D7D")
#        self.power_ax.plot(synth_freqs, np.abs(tsynth_freq), color="r")

        self.power_ax.set_xlim(0.0,0.4)
        self.power_ax.set_ylim(1e-1,1e6)
Ejemplo n.º 8
0
def auto_dsk(dsk_row,synth,bounds,conv_limit=0,conv_bounds=[None,None],phase_args=(0.,360.,1.),highcut=0.,order=3):
    """
    Returns the maximum likelihood phase shift to deskew the data to match a provided synthetic given a bounds
    on a window to match.

    Parameters
    ----------

    dsk_row : Pandas.Series
        Single row of a deskew file with valid path to data file
    synth : list
        This should be the output of the make synthetic function it needs to contain three elements
            0) an array of the synthetic magnetic anomalies
            1) an array of the distance coordinates of the points in 0, should be of equal length to 0, MUST be in
               the same coordinate system as the profile provided in dsk_row!!! Which it may not by default.
            2) the distance resolution of the synthetic in 0 and 1
    bounds : list of floats
        Has two elements which corespond to the left and right bounds of the window
    conv_limit : float, optional
        Weather or not to realign the anomaly each phase shift using a time lagged convolution method which
        increases runtime significantly but can also increase accuracy. This argument should be a positve
        float which corresponds to the amount of +- shift the anomaly is allowed to move used otherwise it should
        be 0 to not use the shift method (Default: 0, which implies not to use method).
    conv_bounds : list of 2 floats, optional
        The left and right boundary in the distance domain to use to time lag convolve the synthetic and the filtered
        data signal. Thus 300 km of signal can be convolved but only the 10 km of motion allowed to pin down the 
        crossing location. (Default: [None,None], which implies conv_bounds=bounds)
    phase_args : tuple or other unpackable sequence, optional
        Arguments to np.arange which define the phases searched in the minimization. (Default: (0.,360.,1.) which
        implies a search of the entire parameter space of phases at 1 degree resolution)
    highcut : float, optional
        The upper cutoff frequency to filter the data by in order to remove any topographic anomalies in the data.
        This value should be between 0 and Nyquest of the synthetic which MUST be regularly sampled like those
        returned by make_synthetic. The data is up or down sampled to the synthetic before filtering. (Default:
        0 which implies not to filter the data)
    order : int, optional
        The order of the lowpass butterworth filter to apply to the data.

    Returns
    ----------

    best_phase : float
        The maximum liklehood phase shift to match the data to the synthetic
    best_shift : float
        the maximum likelihood shift for the best_phase which aligned the two anomalies
    phase_func : Numpy.NdArray
        The summed phase asynchrony between the data and the synthetic as a function of phase shift (best_phase is
        the global minimum of this function)
    best_shifts : Numpy.NdArray
        the maximum likelihood shift as a function of the phase shift
    """

    #Unpack Arguments
    dage = dsk_row["age_max"]-dsk_row["age_min"]
    phases = np.arange(*phase_args)
    left_bound,right_bound = bounds
    synth_mag = np.array(synth[0])
    synth_dis = np.array(synth[1])
    ddis = synth[2]

    data_path = os.path.join(dsk_row["data_dir"],dsk_row["comp_name"])
    data_df = utl.open_mag_file(data_path)
    projected_distances = utl.calc_projected_distance(dsk_row['inter_lon'],dsk_row['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),(180+dsk_row['strike'])%360)
    if conv_limit: #create the fully interpolated profile for convolution

        #create the shortened synthetic for the time lagged convolution
        if isinstance(conv_bounds[0],type(None)): conv_bounds[0] = bounds[0]
        if isinstance(conv_bounds[1],type(None)): conv_bounds[1] = bounds[1]
        left_idx = np.argmin(np.abs(synth_dis - conv_bounds[0]))
        right_idx = np.argmin(np.abs(synth_dis - conv_bounds[1]))
        right_idx,left_idx = max([right_idx,left_idx]),min([right_idx,left_idx])
        conv_synth,conv_synth_dis = synth_mag[left_idx:right_idx],synth_dis[left_idx:right_idx]

        if np.any(np.diff(projected_distances["dist"])<0): #redefine to the right because interp dumbs
            mag = data_df["mag"].to_numpy()[::-1]
            mag_dis = projected_distances["dist"].to_numpy()[::-1]
        full_imag = np.interp(conv_synth_dis,mag_dis,mag)
        if highcut: full_fimag = butter_lowpass_filter(full_imag,highcut=highcut,fs=1/ddis,order=order)
        else: full_fimag = full_imag

    #trim to only window of relivence
    left_idx = np.argmin(np.abs(synth_dis - left_bound))
    right_idx = np.argmin(np.abs(synth_dis - right_bound))
    right_idx,left_idx = max([right_idx,left_idx]),min([right_idx,left_idx])
    tsynth_mag = synth_mag[left_idx:right_idx]
    tsynth_dis = synth_dis[left_idx:right_idx]
    N = len(tsynth_mag) #because this is easier and regularly sampled plus the user can set it simply
    al2 = np.angle(hilbert(np.real(tsynth_mag),N),deg=False)

    best_shifts = [] #record best shifts as function of phase shift
    phase_async_func = [] #record summed phase asynchrony as a function of phase shift
    for i,phase in enumerate(phases):
        shifted_mag = phase_shift_data(data_df["mag"],phase)

        if conv_limit: #DON'T YOU KNOW WE'RE GONNAAAA DOOOOOOO THE COOONVOLUTIOOOON!!!
            shifted_full_fimag = phase_shift_data(full_fimag,phase)
            correlation_func = np.abs(np.convolve(shifted_full_fimag,conv_synth,"full"))
            correlation_func = correlation_func[int(len(conv_synth)-conv_limit/ddis+.5):int(len(conv_synth)+conv_limit/ddis+.5)]

            best_shift = ddis*(len(correlation_func)/2-np.argmax(correlation_func))/2

        else: best_shift = 0.

        #trim the data to the right segments
        left_idx = np.argmin(np.abs(projected_distances["dist"] - left_bound + best_shift))
        right_idx = np.argmin(np.abs(projected_distances["dist"]- right_bound + best_shift))
        right_idx,left_idx = max([right_idx,left_idx]),min([right_idx,left_idx])
        tproj_dist = projected_distances["dist"][left_idx:right_idx] + best_shift
        tshifted_mag = shifted_mag[left_idx:right_idx]

        #numpy.interp only works for monotonic increasing independent variable data
        if np.any(np.diff(tproj_dist)<0): itshifted_mag = np.interp(-tsynth_dis,-tproj_dist,tshifted_mag)
        else: itshifted_mag = np.interp(tsynth_dis,tproj_dist,tshifted_mag)
        if highcut: fitshifted_mag = butter_lowpass_filter(itshifted_mag,highcut=highcut,fs=1/ddis,order=order)
        else: fitshifted_mag = itshifted_mag

        al1 = np.angle(hilbert(fitshifted_mag,N),deg=False)
        phase_asynchrony = np.sin((al1-al2)/2) #shouldn't go negative but...just in case
        best_shifts.append(best_shift)
        phase_async_func.append(phase_asynchrony.sum())

    best_idx = np.argmin(phase_async_func)

    return phases[best_idx],best_shifts[best_idx],phase_async_func,best_shifts