Example #1
0
def heightErr(height, *cyscan_inputs):
    t, traj, stat_pos, sounding = cyscan_inputs

    trial_source = traj.findGeo(height)

    stat_pos.pos_loc(trial_source)
    trial_source.pos_loc(trial_source)

    trial_source.z = trial_source.elev
    stat_pos.z = stat_pos.elev

    ### Ray Trace
    r, _ = cyscan(np.array([trial_source.x, trial_source.y, trial_source.z]), np.array([stat_pos.x, stat_pos.y, stat_pos.z]), \
                    sounding, trace=False, plot=False, particle_output=True, debug=False, \
                    wind=True, h_tol=300, v_tol=3000, print_times=True, processes=1)

    T = r[0] + traj.findTime(trial_source.elev)

    try:
        t_err = (T - t)**2
    except:
        t_err = 999
    print("Height {:.2f} km | Err {:.2f}".format(height[0] / 1000,
                                                 np.sqrt(t_err)))
    return t_err
Example #2
0
def refractiveFactor(S, D, zProfile, D_ANGLE=2):
    """ Compare the difference between launch angles of rays in an ideal and a realistic atmosphere going
    to the same points on the ground (D_ANGLE away from each other in the realistic atmosphere)
    """

    # put into local coordinates
    D.pos_loc(S)
    S.pos_loc(S)

    # Find launch angles in the ideal atmosphere (straight lines)
    dx, dy, dz = D.xyz - S.xyz
    tf_ideal_n = np.degrees(np.arctan2(dz, np.sqrt((dy)**2 + (dx)**2))) + 90
    az_ideal_n = np.degrees(np.arctan2(dx, dy)) % 360

    # Find actual angles
    _, az_n, tf_n, _ = cyscan(S.xyz, D.xyz, zProfile, wind=True,\
            h_tol=330, v_tol=330)

    d_angle_ideal = [np.nan]

    # Number of angles to try
    RANGE = 4

    # Go through <RANGE> number of angles <D_ANGLE> away from the actual angles
    for i in range(RANGE):
        d_az = np.degrees(
            np.arctan(
                np.cos(i * 2 * np.pi / RANGE) * np.tan(np.radians(D_ANGLE))))
        d_tf = np.degrees(
            np.arctan(
                np.sin(i * 2 * np.pi / RANGE) * np.tan(np.radians(D_ANGLE))))

        D_n = anglescan(S.xyz, az_n + d_az, tf_n + d_tf, zProfile, wind=True)

        dx, dy, dz = D_n[0:3] - S.xyz

        # Find ideal atmosphere angles to get to new launch points
        tf = np.degrees(np.arctan2(dz, np.sqrt((dy)**2 + (dx)**2))) + 90
        az = (np.degrees(np.arctan2(dx, dy))) % 360

        tf = (tf - tf_ideal_n)
        az = (az - az_ideal_n)

        d_angle_ideal.append(addAngleComp(tf, az, deg=True))

    if np.isnan(d_angle_ideal).all():
        return np.nan

    d_angle_ideal = np.nanmean(d_angle_ideal)

    rf = np.sqrt(D_ANGLE / d_angle_ideal)

    return rf
Example #3
0
def eigenError(S, D, z_profile, n_angle=90):
    ''' Used to check the error, and verify that the eigenray solution works properly'''

    z_profile = zInteg(S[2], D[2], z_profile)

    f_time, frag_azimuth, frag_takeoff, frag_err = cyscan(S, D, z_profile, wind=True, \
                    n_theta=n_angle, n_phi=n_angle, h_tol=1e-15, v_tol=330)

    D_new = anglescan(S, frag_azimuth, frag_takeoff, z_profile, wind=True)

    d_loc_err = np.sqrt((D[0] - D_new[0])**2 + (D[1] - D_new[1])**2)
    d_time_err = D_new[3] - f_time
    d_err_err = d_loc_err - frag_err

    print("Spatial Error: ", d_loc_err)
    print("Temporal Error: ", d_time_err)
    print("Error Error: ", d_err_err)
Example #4
0
def eigenAngleError(S, az, tf, z_profile, n_angle=90):

    D = anglescan(S, az, tf, z_profile, wind=True)

    z_profile = zInteg(S[2], D[2], z_profile)

    f_time, frag_azimuth, frag_takeoff, frag_err = cyscan(S, D[:3], z_profile, wind=True, \
                n_theta=n_angle, n_phi=n_angle, h_tol=1e-15, v_tol=330)

    tf_error = tf - frag_takeoff
    az_error = az - frag_azimuth
    time_err = D[3] - f_time

    print("Takeoff Error [deg]: ", tf_error)
    print("Azimuth Error [deg]: ", az_error)
    print("Temporal Error: ", time_err)
    print("Spatial Error: ", frag_err)
Example #5
0
def eigenConsistancy(S,
                     D,
                     az,
                     tf,
                     z_profile,
                     n_angle=90,
                     h_tol=None,
                     v_tol=None):
    '''
    Checks if a given source-detector, atmosphere, initial launch angle, solution is consistant with itself
    by running through both anglescan and cyscan 
    '''
    n_angle = 360
    D_new = anglescan(S, az, tf, z_profile, wind=True, debug=True)

    if np.isnan(D_new[0]):
        print('Inconsistant - bad anglescan')
        return 0

    T_new, az_new, tf_new, err = cyscan(S, D_new, z_profile, wind=True, \
                n_theta=n_angle, n_phi=n_angle, h_tol=330, v_tol=330, debug=True)

    d_loc_err = np.sqrt((D[0] - D_new[0])**2 + (D[1] - D_new[1])**2)
    d_time_err = D_new[3] - T_new
    tf_error = tf - tf_new
    az_error = az - az_new
    d_err_err = d_loc_err - err

    if np.isnan(T_new):
        print('Inconsistant - nan solution')
        return 0

    max_error = np.sqrt(h_tol**2 + v_tol**2)

    if not (d_loc_err < max_error and d_time_err < max_error / 310
            and tf_error < 1 and az_error < 1):
        print("Inconsistant Solution at height {:.2f} km".format(S[2] / 1000))
        print('Spatial Error: ', d_loc_err)
        print('Temporal Error: ', d_time_err)
        print("Takeoff Error [deg]: ", tf_error)
        print("Azimuth Error [deg]: ", az_error)
        print("Error Error: ", d_err_err)
        return 0

    return 1
Example #6
0
    def makeStaff(self, stn, ts, time_err, doc):

        # Generate pointes along trajecotry
        traj = self.bam.setup.trajectory
        points = traj.trajInterp2(div=100)

        D = stn.metadata.position
        ref_pos = Position(self.bam.setup.lat_centre,
                           self.bam.setup.lon_centre, 0)
        D.pos_loc(ref_pos)

        times = []
        heights = []

        for pt in points:
            S = Position(pt[0], pt[1], pt[2])
            S.pos_loc(ref_pos)
            sounding, perts = self.bam.atmos.getSounding(
                lat=[S.lat, D.lat],
                lon=[S.lon, D.lon],
                heights=[S.elev, D.elev])

            f_time, _, _, _ = cyscan(S.xyz, D.xyz, sounding, wind=self.prefs.wind_en, n_theta=self.prefs.pso_theta, n_phi=self.prefs.pso_phi, \
                                    h_tol=self.prefs.pso_min_ang, v_tol=self.prefs.pso_min_dist)
            times.append(f_time + pt[3])
            heights.append(pt[2] / 1000)

        plt.scatter(heights, times)

        X = np.linspace(17, 50)

        for tt in range(len(ts)):
            plt.fill_between(X,
                             ts[tt] - time_err[tt][0],
                             y2=ts[tt] + time_err[tt][1])
            plt.annotate('F{:}'.format(tt + 1), (18, ts[tt]))
            plt.axhline(y=ts[tt], color="black", linestyle="--")

        pic_file = os.path.join(self.prefs.workdir,
                                self.bam.setup.fireball_name, 'staff.png')
        plt.savefig(pic_file)
        doc.add_picture(pic_file)
        os.remove(pic_file)
        plt.clf()
Example #7
0
    def __init__(self, setup, stn_list, position, pert_idx):

        QWidget.__init__(self)
        self.setWindowTitle('Height Solver')
        self.setup = setup
        self.stn_list = stn_list
        p = self.palette()
        p.setColor(self.backgroundRole(), Qt.black)
        self.setPalette(p)

        menu_bar = QMenuBar(self)
        #layout.addWidget(menu_bar, 0, 1)
        file_menu = menu_bar.addMenu('&File')
        select_menu = menu_bar.addMenu('&Select')

        self.selected = False

        file_export = QAction("Export Selected Menu", self)
        file_export.setShortcut('Ctrl+E')
        file_export.setStatusTip(
            'Brings up the Export Menu with the selected stations')
        file_export.triggered.connect(self.export)
        file_menu.addAction(file_export)

        select_all = QAction("Select All", self)
        select_all.setShortcut('Ctrl+A')
        select_all.setStatusTip('Selects all')
        select_all.triggered.connect(self.select)
        select_menu.addAction(select_all)

        theme(self)

        self.position = position
        point = position
        widget = QWidget()
        self.layout = QVBoxLayout(widget)
        self.layout.setAlignment(Qt.AlignTop)
        for point in position:
            print('Best Position: {:}'.format(point))

        ref_pos = Position(self.setup.lat_centre, self.setup.lon_centre, 0)
        count = 0
        max_steps = len(stn_list) * len(position) * self.setup.perturb_times
        dataset = parseWeather(self.setup)
        self.save_button = []
        self.stn_view = []
        self.stn_canvas = []
        self.invert = []
        self.waveform_data = [None] * len(stn_list)

        A = self.setup.trajectory.pos_i
        B = self.setup.trajectory.pos_f

        A.pos_loc(B)
        B.pos_loc(B)

        # Get prediction of time of the meteor, so the timing of each fragmentation can be known
        length_of_meteor = np.sqrt((A.x - B.x)**2 + (A.y - B.y)**2 +
                                   (A.z - B.z)**2)
        time_of_meteor = length_of_meteor / self.setup.trajectory.v
        for index in range(len(stn_list)):

            stn = stn_list[index]
            station_layout = QGridLayout()
            self.layout.addLayout(station_layout)
            label_layout = QVBoxLayout()
            control_layout = QGridLayout()
            waveform_layout = QVBoxLayout()
            station_layout.addLayout(label_layout, 0, 100, 1, 100)
            station_layout.addLayout(control_layout, 0, 0, 1, 100)
            station_layout.addLayout(waveform_layout, 0, 200, 1, 100)

            label_layout.addWidget(
                QLabel('Station: {:} - {:}'.format(stn_list[index].network,
                                                   stn_list[index].code)))
            label_layout.addWidget(
                QLabel('Channel: {:}'.format(stn_list[index].channel)))
            label_layout.addWidget(
                QLabel('Name: {:}'.format(stn_list[index].name)))
            label_layout.addWidget(
                QLabel('Lat: {:}'.format(stn_list[index].position.lat)))
            label_layout.addWidget(
                QLabel('Lon: {:}'.format(stn_list[index].position.lon)))
            label_layout.addWidget(
                QLabel('Elev: {:}'.format(stn_list[index].position.elev)))

            self.save_button.append(QPushButton('Select'))
            self.save_button[index].clicked.connect(
                partial(self.saveButton, index))
            control_layout.addWidget(self.save_button[index], 0, 0)
            control_layout.addWidget(QPushButton('-'), 0, 1)
            control_layout.addWidget(QPushButton('-'), 1, 0)
            control_layout.addWidget(QPushButton('-'), 1, 1)

            self.stn_view.append(pg.GraphicsLayoutWidget())
            self.stn_canvas.append(self.stn_view[index].addPlot())
            waveform_layout.addWidget(self.stn_view[index])
            self.invert.append(False)

            min_point, max_point = self.discountDrawWaveform(
                setup, index, self.stn_canvas[index])
            # for ptb_n in range(self.setup.perturb_times):
            for p, point in enumerate(position):
                for ptb_n in range(self.setup.perturb_times):
                    dataset = self.perturbGenerate(ptb_n, dataset,
                                                   self.perturbSetup())
                    zProfile, _ = getWeather(np.array([point.lat, point.lon, point.elev]), np.array([stn.position.lat, stn.position.lon, stn.position.elev]), self.setup.weather_type, \
                                    ref_pos, dataset, convert=True)
                    point.pos_loc(ref_pos)
                    stn.position.pos_loc(ref_pos)
                    f_time, _, _, _ = cyscan(np.array([point.x, point.y, point.z]), np.array([stn.position.x, stn.position.y, stn.position.z]), zProfile, wind=True, \
                            n_theta=self.setup.n_theta, n_phi=self.setup.n_phi, h_tol=self.setup.h_tol, v_tol=self.setup.v_tol)

                    correction = time_of_meteor - A.z / self.setup.trajectory.pos_i.elev * (
                        time_of_meteor)

                    nom_time = f_time + correction
                    with warnings.catch_warnings():
                        warnings.simplefilter("ignore")
                        try:
                            self.stn_canvas[index].plot(
                                x=[f_time, f_time],
                                y=[min_point, max_point],
                                pen=PEN[p % 7],
                                brush=PEN[p % 7])
                        except:
                            pass
                        count += 1
                        loadingBar("Generating Plots", count, max_steps)
                    try:
                        self.stn_canvas[index].setXRange(nom_time - 25,
                                                         nom_time + 25,
                                                         padding=1)
                    except:
                        avg_time = stn.position.pos_distance(point) / 330
                        self.stn_canvas[index].setXRange(avg_time - 25,
                                                         avg_time + 25,
                                                         padding=1)

        self.setWidget(widget)
        self.setWidgetResizable(True)
Example #8
0
    def __init__(self, invert, setup, stn_list, position):
        self.link = False
        self.grid = False
        QWidget.__init__(self)
        self.selected_stn_view = pg.GraphicsLayoutWidget()
        self.stn_canvas = []
        self.stn_list = stn_list
        self.setup = setup
        self.waveform_data = [None]*len(stn_list)
        ref_pos = Position(self.setup.lat_centre, self.setup.lon_centre, 0)
        count = 0
        max_steps = len(position)*len(stn_list)*setup.perturb_times
        dataset = parseWeather(self.setup)

        theme(self)
        for ii, index in enumerate(invert):

            if index:
                stn = stn_list[ii]
                self.stn_canvas.append(self.selected_stn_view.addPlot())
                self.selected_stn_view.nextRow()
                min_point, max_point = self.discountDrawWaveform(setup, ii, self.stn_canvas[-1])
                self.stn_canvas[-1].getAxis('bottom').setPen((0, 0, 0)) 
                self.stn_canvas[-1].getAxis('left').setPen((0, 0, 0)) 
                self.waveform_data[ii].setPen((0, 0, 0))
                #self.stn_canvas[-1].addItem(pg.LabelItem(text="{:}-{:}".format(stn.network, stn.code), color=(0, 0, 0)))
                self.stn_canvas[-1].setTitle("{:}-{:}".format(stn.network, stn.code), color=(0, 0, 0))

                for p, point in enumerate(position):
                    for ptb_n in range(self.setup.perturb_times):             
                        dataset = self.perturbGenerate(ptb_n, dataset, self.perturbSetup())
                        zProfile, _ = getWeather(np.array([point.lat, point.lon, point.elev]), np.array([stn.position.lat, stn.position.lon, stn.position.elev]), self.setup.weather_type, \
                                            [ref_pos.lat, ref_pos.lon, ref_pos.elev], dataset, convert=False)
                        point.pos_loc(ref_pos)
                        stn.position.pos_loc(ref_pos)
                        f_time, _, _, _ = cyscan(np.array([point.x, point.y, point.z]), np.array([stn.position.x, stn.position.y, stn.position.z]), zProfile, wind=True, \
                            n_theta=self.setup.n_theta, n_phi=self.setup.n_phi, h_tol=self.setup.h_tol, v_tol=self.setup.v_tol)

                        A = self.setup.trajectory.pos_i
                        B = self.setup.trajectory.pos_f

                        A.pos_loc(B)
                        B.pos_loc(B)

                        # Get prediction of time of the meteor, so the timing of each fragmentation can be known
                        length_of_meteor = np.sqrt((A.x - B.x)**2 + (A.y - B.y)**2 + (A.z - B.z)**2)
                        time_of_meteor = length_of_meteor/self.setup.trajectory.v

                        correction = time_of_meteor - A.z/self.setup.trajectory.pos_i.elev*(time_of_meteor)

                        nom_time = f_time + correction
                        with warnings.catch_warnings():
                            warnings.simplefilter("ignore")
                            try:
                                self.stn_canvas[-1].plot(x=[f_time, f_time], y=[min_point, max_point], pen=PEN[p%7], brush=PEN[p%7])
                            except:
                                pass
                        count += 1
                        loadingBar("Generating Plots", count, max_steps)
                        try:
                            self.stn_canvas[-1].setXRange(nom_time-25, nom_time+25, padding=1)
                        except:
                            pass

        self.selected_stn_view.setBackground((255, 255, 255))


        layout = QVBoxLayout()
        layout.addWidget(self.selected_stn_view)
        self.setLayout(layout)

        toggle_group = QGroupBox("Toggles")
        layout.addWidget(toggle_group)

        toggle_group_layout = QVBoxLayout()
        toggle_group.setLayout(toggle_group_layout)

        sync_button = QCheckBox('Sync')
        toggle_group_layout.addWidget(sync_button)
        sync_button.stateChanged.connect(self.sync)

        grid_button = QCheckBox('Grid')
        toggle_group_layout.addWidget(grid_button)
        grid_button.stateChanged.connect(self.grid_toggle)

        export_button = QPushButton("Export")
        export_button.clicked.connect(self.export)
        layout.addWidget(export_button)
Example #9
0
def timeConstraints(x, *args):
    
    # Retrieve passed arguments
    stns, w, kotc, setup, ref_pos, dataset, v = args

    # number of stations total
    n_stations = len(stns)

    # Residuals to each station
    sotc = np.empty(n_stations)

    # Initialize variables
    # Weight of each station
    wn = w

    # Mean occurrence time
    motc = 0

    ### multiprocessing

    # number of stations total
    n_stations = len(stns)

    # Station Times
    tobs = stns[0:n_stations, 4]

    # Station Location
    xstn = stns[0:n_stations, 0:3]

    # Initialize arrays
    # Simulated travel times to each station
    time3D = np.empty(n_stations)

    # Residuals to each station
    sotc = np.empty(n_stations)
    
    for j in range(n_stations):
        # if station has weight
        if w[j] > 0:

            # Create interpolated atmospheric profile for use with cyscan
            sounding, points = getWeather([x[0], x[1], x[2]], xstn[j, :], setup.weather_type, ref_pos, copy.copy(dataset))


            # Use distance and atmospheric data to find path time
            time3D[j], _, _, _ = cyscan(np.array([x[0], x[1], x[2]]), np.array(xstn[j, :]), sounding, \
                                                wind=setup.enable_winds, n_theta=setup.n_theta, n_phi=setup.n_phi, h_tol=setup.h_tol, v_tol=setup.v_tol)
            # Residual time for each station
            sotc[j] = tobs[j] - time3D[j]

        # If station has no weight
        else:
            sotc[j] = tobs[j]
    
    t_avg = (setup.t_min + setup.t_max)/2


    diff = abs(motc - t_avg)
    TOL = setup.t_max - t_avg
    motc = np.dot(wn, sotc)/sum(wn)

    
    if diff < TOL:
        return diff
    else:
        return -diff
Example #10
0
def outputWeather(n_stations, x_opt, stns, setup, ref_pos, atmos, output_name,
                  s_name, kotc, w, prefs, theo):
    """ Function to calculate the results of the optimal Supracenter and export interpolated weather data
        from each station.

    Arguments:
        n_stations: [int] number of stations
        x_opt: [list] lat, lon, and height of the optimal supracenter
        xstn: [list] lat, lon, and height of all the stations
        setup: [Object] object storing user-defined setup
        consts: [Object] object storing physical constants
        ref_pos: [list] mean position of the stations to act as the origin for the local coordinate system
        dataset: [ndarray] atmospheric profile of search area
        output_name: [string] folder name to save output files in
        s_name: [list] name of all the stations
        kotc: [list] user-defined occurence time [HH, MM, SS]
        tobs: [list] station arrival times to each station
        w: [list] weights of each station


    Returns:
        time3D: [list] calculated travel time to each station
        az: [list] initial azimuth angle to each station
        tf: [list] initial takeoff angle to each station
        r: [list] residuals to each station            
    
    """
    consts = Constants()
    # Station Times
    tobs = stns[0:n_stations, 4]

    # Station Location
    xstn = stns[0:n_stations, 0:3]

    # Travel time to each station
    time3D = np.zeros(n_stations)

    # Initial azimuths angles of each station
    az = np.zeros(n_stations)

    # Initial takeoff angles of each station
    tf = np.zeros(n_stations)

    # difference in theoretical and simulated travel times
    sotc = np.zeros_like(tobs)

    #difference in theoretical and simulated travel times
    r = np.zeros_like(tobs)

    trace = []

    # Find parameters of optimal location
    print('Exporting weather profiles...')
    for j in range(n_stations):
        #print(np.array(x_opt), np.array(xstn[j, :]))
        # get interpolated weather profile
        D = Position(0, 0, 0)
        D.x, D.y, D.z = xstn[j, 0], xstn[j, 1], xstn[j, 2]
        D.pos_geo(ref_pos)
        if not theo:
            sounding, _ = atmos.getSounding(lat=[x_opt.lat, D.lat],
                                            lon=[x_opt.lon, D.lon],
                                            heights=[x_opt.elev, D.elev],
                                            ref_time=setup.fireball_datetime)

            # Rotate winds to match with coordinate system
            #sounding[:, 3] = np.radians(angle2NDE(np.degrees(sounding[:, 3])))

            # Export the interpolated weather profiles from the optimal Supracenter for each station
            # with open(os.path.join(output_name, s_name[j] + '_sounding.txt'), 'w') as f:

            #     # With winds
            #     if prefs.wind_en == True:
            #         if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio':
            #             f.write('| Height (m) | Temp (K) | soundSpd (m/s) | wdSpd (m/s) | wdDir (deg fN) |\n')
            #         else:
            #             f.write('| Latitude (deg N) | Longitude (deg E) | Height (m) | Temp (K) | soundSpd (m/s) | wdSpd (m/s) | wdDir (deg fN) |\n')

            #     # No winds
            #     else:
            #         if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type== 'radio':
            #             f.write('| Height (m) | Temp (K) | soundSpd (m/s) |\n')
            #         else:
            #             f.write('| Latitude (deg N) | Longitude (deg E) | Height (m) | Temp (K) | soundSpd (m/s) |\n')

            # for ii in range(len(sounding)):

            #     if prefs.wind_en == True:
            #         if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio':
            #             f.write('|  {:8.2f}  |  {:7.3f} |    {:6.4f}    |   {:7.3f}   |     {:6.2f}     |\n'\
            #                 .format(sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, \
            #                     sounding[ii, 1], sounding[ii, 2], sounding[ii, 3]))
            #         else:
            #             f.write('|       {:7.4f}    |      {:7.4f}    |  {:8.2f}  |  {:7.3f} |    {:6.4f}    |   {:7.3f}   |     {:6.2f}     |\n'\
            #                 .format(points[ii][0], points[ii][1], sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, \
            #                     sounding[ii, 1], sounding[ii, 2], sounding[ii, 3]))
            #     else:
            #         if prefs.atm_type == 'custom' or prefs.atm_type == 'none' or prefs.atm_type == 'radio':
            #             f.write('|  {:8.2f}  |  {:7.3f} |    {:6.4f}    |\n'\
            #                 .format(sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, sounding[ii, 1]))
            #         else:
            #             f.write('|       {:7.4f}    |      {:7.4f}    |  {:8.2f}  |  {:7.3f} |    {:6.4f}    |\n'\
            #                 .format(points[ii][0], points[ii][1], sounding[ii, 0], sounding[ii, 1]**2*consts.M_0/consts.GAMMA/consts.R, sounding[ii, 1]))

            # ray tracing function
            # _, _, _, _, temp_trace = slowscan(x_opt.xyz, np.array(xstn[j, :]), sounding, \
            #                  wind=prefs.wind_en, n_theta=prefs.pso_theta, n_phi=prefs.pso_phi,\
            #                     h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)

            time3D[j], _, _, _ = cyscan(x_opt.xyz, np.array(xstn[j, :]), sounding, \
                         wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist, processes=1)
        else:
            time3D[j] = x_opt.pos_distance(D) / prefs.avg_sp_sound
        # trace.append(temp_trace)

        # find residuals
        sotc[j] = tobs[j] - time3D[j]

    # for ii, element in enumerate(time3D):
    #     if np.isnan(element):
    #         w[ii] = 0

    # User defined occurrence time
    if kotc != None:
        motc = kotc
        index = []

    # elif setup.manual_fragmentation_search != '' and len(setup.manual_fragmentation_search) > 0:
    #     motc = setup.manual_fragmentation_search[3]

    # Unknown occurrence time

    else:
        w = np.array([1] * len(sotc))

        index = np.isnan(sotc)
        sotc[index] = 0
        motc = np.dot(w, sotc) / sum(w)
    # Station residuals (from average)

    r = sotc - motc
    r[index] = np.nan

    return time3D, az, tf, r, motc, sotc, trace
Example #11
0
def scatterPlot(setup, results_arr, n_stations, xstn, s_name, dataset):
    """ Outputs a scatter plot of the search data and the optimal supracenter

    Arguments:
        single_point: [list] lat, lon, and height of a manual search point
        n_stations: [int] number of stations
        xstn: [list] lat, lon, and height of all the stations
        s_name: [list] name of all the stations
        r: [list] residuals to each station
        x_opt: [list] lat, lon, and height of the optimal supracenter
        reported_points: [list] name, lat, lon, and height of extra reference points to be plotted
        search: [list] min/max lat, lon, and height of the search boundary
        output_name: [string] folder name to save output files in
        ref_pos: [list] mean position of the stations to act as the origin for the local coordinate system
        sup: [ndarray] array of points searched with the search algorithm used for plotting
        errors: [ndarray] array of the errors in each of the points in sup for plotting

    Returns:
        min_search: [list] min lat, lon and height of the search area
        max_search: [list] max lat, lon and height of the search area
    """

    single_point = setup.manual_fragmentation_search
    r = results_arr[0].r
    x_opt = results_arr[0].x_opt
    reported_points = setup.reported_points
    search = setup.search
    output_name = setup.output_name
    ref_pos = [setup.ref_pos.lat, setup.ref_pos.lon, setup.ref_pos.elev]
    sup = results_arr[0].sup
    errors = results_arr[0].errors
    run_times = len(results_arr)

    ### Convert back to lat/lon
    # Search Region boundaries
    min_search = loc2Geo(ref_pos[0], ref_pos[1], ref_pos[2],
                         [search[0], search[2], search[4]])
    max_search = loc2Geo(ref_pos[0], ref_pos[1], ref_pos[2],
                         [search[1], search[3], search[5]])
    # Set up axes
    fig = plt.figure(figsize=plt.figaspect(0.5))
    fig.set_size_inches(20.9, 11.7)

    # automatic search
    if len(single_point) == 0:
        ax1 = fig.add_subplot(1, 2, 1, projection='3d')
        ax2 = fig.add_subplot(1, 2, 2, projection='3d')

    # manual search
    else:
        ax1 = fig.add_subplot(1, 1, 1, projection='3d')

    ### Labels
    ax1.set_title("Supracenter Locations")
    ax1.set_xlabel("Latitude (deg N)", linespacing=3.1)
    ax1.set_ylabel("Longitude (deg E)", linespacing=3.1)
    ax1.set_zlabel('Elevation (m)', linespacing=3.1)

    # plot station names and residuals
    for h in range(n_stations):

        # Convert station locations to geographic
        xstn[h, 0], xstn[h, 1], xstn[h, 2] = loc2Geo(ref_pos[0], ref_pos[1],
                                                     ref_pos[2], xstn[h, :])

        # Add station names
        ax1.text(xstn[h, 0],
                 xstn[h, 1],
                 xstn[h, 2],
                 '%s' % (s_name[h]),
                 size=10,
                 zorder=1,
                 color='k')
        if len(single_point) == 0:
            ax2.text(xstn[h, 0],
                     xstn[h, 1],
                     xstn[h, 2],
                     '%s' % (s_name[h]),
                     size=10,
                     zorder=1,
                     color='k')

    # Add stations with color based off of residual
    res = ax1.scatter(xstn[:, 0],
                      xstn[:, 1],
                      xstn[:, 2],
                      c=abs(r),
                      marker='^',
                      cmap='viridis_r',
                      depthshade=False)

    # Add point and label
    for j in range(run_times):
        if j == 0:
            ax1.scatter(x_opt[0], x_opt[1], x_opt[2], c='r', marker='*')
            ax1.text(x_opt[0],
                     x_opt[1],
                     x_opt[2],
                     '%s' % ('Supracenter'),
                     zorder=1,
                     color='k')
        else:
            ax1.scatter(results_arr[j].x_opt[0],
                        results_arr[j].x_opt[1],
                        results_arr[j].x_opt[2],
                        alpha=0.2,
                        c='r',
                        marker='*')

    # Get the limits of the plot

    x_min, y_min = search[0], search[2]
    x_max, y_max = search[1], search[3]

    img_dim = 30

    x_data = np.linspace(x_min, x_max, img_dim)
    y_data = np.linspace(y_min, y_max, img_dim)
    xx, yy = np.meshgrid(x_data, y_data)

    # Make an array of all plane coordinates
    plane_coordinates = np.c_[xx.ravel(),
                              yy.ravel(),
                              np.zeros_like(xx.ravel())]

    times_of_arrival = np.zeros_like(xx.ravel())

    x_opt = geo2Loc(ref_pos[0], ref_pos[1], ref_pos[2], x_opt[0], x_opt[1],
                    x_opt[2])

    print('Creating contour plot...')
    # Calculate times of arrival for each point on the reference plane
    for i, plane_coords in enumerate(plane_coordinates):

        #plane_coords = geo2Loc(ref_pos[0], ref_pos[1], ref_pos[2], plane_coords[0], plane_coords[1], plane_coords[2])

        # Create interpolated atmospheric profile for use with cyscan
        sounding, points = getWeather(x_opt, plane_coords, setup.weather_type,
                                      ref_pos, copy.copy(dataset))

        # Use distance and atmospheric data to find path time
        ti, _, _ = cyscan(np.array(x_opt), np.array(plane_coords), sounding, \
                                wind=setup.enable_winds, n_theta=setup.n_theta, n_phi=setup.n_phi, \
                                precision=setup.angle_precision)
        if np.isnan(ti):
            #Make blank contour
            ti = -1
        times_of_arrival[i] = ti

    times_of_arrival = times_of_arrival.reshape(img_dim, img_dim)

    # Determine range and number of contour levels, so they are always centred around 0
    toa_abs_max = np.max(
        [np.abs(np.min(times_of_arrival)),
         np.max(times_of_arrival)])
    toa_abs_min = np.min(
        [np.abs(np.min(times_of_arrival)),
         np.max(times_of_arrival)])
    levels = np.linspace(toa_abs_min, toa_abs_max, 50)

    # Plot colorcoded times of arrival on the surface
    toa_conture = ax1.contourf(xx,
                               yy,
                               times_of_arrival,
                               levels,
                               cmap='inferno',
                               alpha=1.0)
    # Add a color bar which maps values to colors
    fig.colorbar(toa_conture, label='Time of arrival (s)', ax=ax1)

    # plot given reference points
    for q in range(len(reported_points[:])):
        ax1.scatter(reported_points[q][1],
                    reported_points[q][2],
                    reported_points[q][3],
                    c='k',
                    marker='X')
        ax1.text(reported_points[q][1],
                 reported_points[q][2],
                 reported_points[q][3],
                 '%s' % (reported_points[q][0]),
                 size=7,
                 zorder=1,
                 color='k')

    if len(single_point) == 0:
        # potential Supracenter points with color based on error
        for i in range(len(sup)):
            sup[i, 0], sup[i, 1], sup[i, 2] = loc2Geo(ref_pos[0], ref_pos[1],
                                                      ref_pos[2], sup[i, :])
        sc = ax2.scatter(sup[:, 0],
                         sup[:, 1],
                         sup[:, 2],
                         c=errors,
                         cmap='inferno_r',
                         depthshade=False)
        a = plt.colorbar(sc, ax=ax2)
        a.set_label("Error in Supracenter (s)")
        ax2.scatter(xstn[:, 0],
                    xstn[:, 1],
                    xstn[:, 2],
                    c=abs(r),
                    marker='^',
                    cmap='viridis_r',
                    depthshade=False)
        ax2.set_title("Fits of Potential Supracenters")
        ax2.set_xlabel("Latitude (deg N)")
        ax2.set_ylabel("Longitude (deg E)")
        ax2.set_zlabel('Elevation (m)')

    if len(single_point) == 0 and setup.restrict_to_trajectory:
        # A = [0, 0, 0]
        # B = [0, 0, 0]
        # A[0], A[1], A[2] = loc2Geo(ref_pos[0], ref_pos[1], ref_pos[2], [setup.lat_i, setup.lon_i, setup.elev_i])
        # B[0], B[1], B[2] = loc2Geo(ref_pos[0], ref_pos[1], ref_pos[2], [setup.lat_f, setup.lon_f, setup.elev_f])
        ax1.plot([setup.lat_i, setup.lat_f], [setup.lon_i, setup.lon_f],
                 [setup.elev_i * 1000, setup.elev_f * 1000],
                 c='g')
        ax2.plot([setup.lat_i, setup.lat_f], [setup.lon_i, setup.lon_f],
                 [setup.elev_i * 1000, setup.elev_f * 1000],
                 c='g')

    elif setup.restrict_to_trajectory:
        ax1.plot([setup.lat_i, setup.lat_f], [setup.lon_i, setup.lon_f],
                 [setup.elev_i * 1000, setup.elev_f * 1000],
                 c='g')

    # colorbars
    b = plt.colorbar(res, ax=ax1)
    b.set_label("Station Residuals (s)")

    # automatic search
    if len(single_point) == 0:
        ax1.set_xlim3d(min_search[0], max_search[0])
        ax2.set_xlim3d(min_search[0], max_search[0])

        ax1.set_ylim3d(min_search[1], max_search[1])
        ax2.set_ylim3d(min_search[1], max_search[1])

        ax1.set_zlim3d(0, max_search[2])
        ax2.set_zlim3d(0, max_search[2])

    # manual search, don't pull up pso graph
    else:
        ax1.set_xlim3d(min_search[0], max_search[0])
        ax1.set_ylim3d(min_search[1], max_search[1])
        ax1.set_zlim3d(0, max_search[2])

    plt.tight_layout()
    plt.savefig(os.path.join(output_name, '1output_graph.png'))
    plt.show()

    plt.clf()
    plt.close()

    return min_search, max_search
Example #12
0
def calcAllTimes(obj, bam, prefs):
    ''' Calculates all arrivals to all stations
    '''
    time_step = -999
    #######################################
    # Check if times need to be calculated
    #######################################

    if checkSkip(bam, prefs):
        if prefs.debug:
            printStatus(bam, prefs)
        return bam.stn_list

    qm = QMessageBox()
    ret = qm.question(obj, '', "No arrival times detected, calculate?",
                      qm.Yes | qm.No)

    if ret == qm.No:
        return bam.stn_list

    ####################
    # Times Calculation
    ####################

    ref_pos = Position(bam.setup.lat_centre, bam.setup.lon_centre, 0)

    if not hasattr(bam.setup, "fragmentation_point"):
        no_of_frags = 0
    else:
        no_of_frags = len(bam.setup.fragmentation_point)

    total_steps = 1 + (
        prefs.frag_en * no_of_frags *
        (1 + prefs.pert_en * prefs.pert_num) + prefs.ballistic_en *
        (1 + prefs.pert_en * prefs.pert_num)) * len(bam.stn_list)

    step = 0

    for ii, stn in enumerate(bam.stn_list):

        if not hasattr(stn, 'times'):
            stn.times = Times()

        stn.times.ballistic = []
        stn.times.fragmentation = []

        ################
        # Fragmentation
        ################

        if prefs.frag_en:

            for i, frag in enumerate(bam.setup.fragmentation_point):

                if i == 0 and ii == 0:
                    t1 = time.time()
                elif i == 1 and ii == 0:
                    time_step = time.time() - t1

                step += 1
                time_hour = time_step * (total_steps - step) // 3600
                time_minute = time_step * (total_steps - step) % 3600 // 60
                time_second = time_minute * (total_steps - step) % 60

                time_string = "{:02d}:{:02d}:{:02d}".format(
                    int(time_hour), int(time_minute), int(time_second))

                loadingBar(
                    'Calculating Station Times - ETA {:}: '.format(
                        time_string), step, total_steps)

                offset = frag.time

                a = []

                supra = frag.position

                # convert to local coordinates based off of the ref_pos
                stn.metadata.position.pos_loc(supra)
                supra.pos_loc(supra)

                lats = [supra.lat, stn.metadata.position.lat]
                lons = [supra.lon, stn.metadata.position.lon]
                heights = [supra.elev, stn.metadata.position.elev]

                sounding, perturbations = bam.atmos.getSounding(
                    lats,
                    lons,
                    heights,
                    ref_time=bam.setup.fireball_datetime,
                    spline=1000)

                # Travel time of the fragmentation wave
                f_time, frag_azimuth, frag_takeoff, frag_err = cyscan(np.array([supra.x, supra.y, supra.z]), np.array([stn.metadata.position.x, stn.metadata.position.y, stn.metadata.position.z]), sounding, \
                    wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)

                results = []

                if perturbations is not None:
                    for pert in perturbations:
                        step += 1
                        loadingBar('Calculating Station Times: ', step,
                                   total_steps)
                        temp = cyscan(np.array([supra.x, supra.y, supra.z]), \
                            np.array([stn.metadata.position.x, stn.metadata.position.y, stn.metadata.position.z]), \
                            sounding, wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)
                        temp[0] += offset
                        results.append(temp)

                a.append(
                    [f_time + offset, frag_azimuth, frag_takeoff, frag_err])
                a.append(results)
                stn.times.fragmentation.append(a)

        ############
        # Ballistic
        ############

        if prefs.ballistic_en and bam.setup.trajectory is not None:

            step += 1
            loadingBar('Calculating Station Times: ', step, total_steps)

            a = []
            # define line bottom boundary

            max_height = bam.setup.trajectory.pos_i.elev
            min_height = bam.setup.trajectory.pos_f.elev

            points = bam.setup.trajectory.trajInterp2(div=1,
                                                      min_p=min_height,
                                                      max_p=max_height)

            u = np.array([
                bam.setup.trajectory.vector.x, bam.setup.trajectory.vector.y,
                bam.setup.trajectory.vector.z
            ])

            angle_off = []
            X = []
            for pt in points:

                S = Position(pt[0], pt[1], pt[2])

                lats = [S.lat, stn.metadata.position.lat]
                lons = [S.lon, stn.metadata.position.lon]
                heights = [S.elev, stn.metadata.position.elev]

                S.pos_loc(S)
                stn.metadata.position.pos_loc(S)

                sounding, perturbations = bam.atmos.getSounding(
                    lats, lons, heights, ref_time=bam.setup.fireball_datetime)

                # Travel time of the fragmentation wave
                _, az, tf, _ = cyscan(np.array([S.x, S.y, S.z]), \
                    np.array([stn.metadata.position.x, stn.metadata.position.y, stn.metadata.position.z]), \
                    sounding, wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)

                az = np.radians(az)
                tf = np.radians(180 - tf)
                v = np.array([
                    np.sin(az) * np.sin(tf),
                    np.cos(az) * np.sin(tf), -np.cos(tf)
                ])

                angle_off.append(
                    np.degrees(
                        np.arccos(
                            np.dot(u / np.sqrt(u.dot(u)),
                                   v / np.sqrt(v.dot(v))))))
                X.append(S.elev)
            angle_off = np.array(angle_off)
            try:
                best_indx = np.nanargmin(abs(angle_off - 90))

            except ValueError:
                best_indx = None
                a.append([np.nan, np.nan, np.nan, np.nan])
                results = []
                if perturbations is not None:
                    for pert in perturbations:
                        results.append(
                            np.array([np.nan, np.nan, np.nan, np.nan]))
                a.append(results)
                stn.times.ballistic.append(a)
                continue

            supra = points[best_indx]
            ref_time = supra[3]
            supra = Position(supra[0], supra[1], supra[2])
            supra.pos_loc(supra)

            lats = [supra.lat, stn.metadata.position.lat]
            lons = [supra.lon, stn.metadata.position.lon]
            heights = [supra.elev, stn.metadata.position.elev]

            sounding, perturbations = bam.atmos.getSounding(
                lats, lons, heights, ref_time=bam.setup.fireball_datetime)
            # Travel time of the fragmentation wave
            f_time, frag_azimuth, frag_takeoff, frag_err = cyscan(np.array([supra.x, supra.y, supra.z]), \
                np.array([stn.metadata.position.x, stn.metadata.position.y, stn.metadata.position.z]),\
                sounding, wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)

            speed = bam.setup.trajectory.v
            distance = supra.pos_distance(bam.setup.trajectory.pos_f)
            timing = distance / speed

            results = []
            if perturbations is not None:
                for pert in perturbations:
                    step += 1
                    loadingBar('Calculating Station Times: ', step,
                               total_steps)
                    e, b, c, d = cyscan(np.array([supra.x, supra.y, supra.z]), \
                        np.array([stn.metadata.position.x, stn.metadata.position.y, stn.metadata.position.z]),\
                        sounding, wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)
                    e += timing
                    results.append(np.array([e, b, c, d]))

            a.append([ref_time + f_time, frag_azimuth, frag_takeoff, frag_err])
            a.append(results)

            stn.times.ballistic.append(a)

    step += 1
    loadingBar('Calculating Station Times: ', step, total_steps)

    if prefs.debug:
        printStatus(bam, prefs)

    return bam.stn_list
Example #13
0
def plotAllWaveforms(dir_path, stn_list, setup, sounding, ax=None, waveform_window=None,\
    difference_filter_all=False):
    """ Bandpass filter and plot all waveforms from the given data list. 

    Keyword arguments:
        waveform_window: [int] If given, the waveforms will be cut around the modelled time of arrival line
            with +/- waveform_window/2 seconds. None by default, which means the whole waveform will be 
            plotted.
        difference_filter_all: [bool] If True, the Kalenda et al. (2014) difference filter will be applied
                on the data plotted in the overview plot of all waveforms.
    """

    # Initialize variables
    v_sound = setup.v_sound
    t0 = setup.t0
    lat_centre = setup.lat_centre
    lon_centre = setup.lon_centre

    if ax is None:
        ax = plt.gca()

    max_wave_value = 0
    min_wave_value = np.inf
    min_time = np.inf
    max_time = 0

    # # Add extra stations from the config file
    # if setup.stations is not None:
    #     for line in setup.stations:

    #         # Prevent adding duplicates
    #         if line in data_list:
    #             continue

    #         data_list.append(line)

    lats = []
    lons = []
    for i in range(len(stn_list)):
        lats.append(stn_list[i].position.lat)
        lons.append(stn_list[i].position.lon)

    # Azimuth from source point to station (degrees +N of due E)
    az = np.arctan2(lat_centre - np.array(lats), lon_centre - np.array(lons))
    # az - normalized values for color-coding, 
    # az_n - original values for text

    az_n = copy.copy(az)
    #az_n = (90 - np.degrees(az_n)%360)%360

    # normalize azimuths
    for i in range(len(az)):
        az[i] += abs(min(az))
        az[i] /= (max(az) + abs(min(az)))

    # Go though all stations and waveforms
    bad_stats = []

    for idx, stn in enumerate(stn_list):


        sys.stdout.write('\rPlotting: {:} {:}              '.format(stn.network, stn.code))
        sys.stdout.flush()
        time.sleep(0.001)

        mseed_file_path = os.path.join(dir_path, stn.file_name)

        try:
            
            # Read the miniSEED file
            if os.path.isfile(mseed_file_path):

                mseed = obspy.read(mseed_file_path)

            else:
                bad_stats.append(idx)
                print('File {:s} does not exist!'.format(mseed_file_path))
                continue


        except TypeError as e:
            bad_stats.append(idx)
            print('Opening file {:} failed with error: {:}'.format(mseed_file_path, e))
            continue

        # Find channel with BHZ, HHZ, or BDF

        for i in range(len(mseed)):
            if mseed[i].stats.channel == 'BDF':
                stn.channel = 'BDF'
                stream = i

        for i in range(len(mseed)):
            if mseed[i].stats.channel == 'BHZ':
                stn.channel = 'BHZ'
                stream = i

        for i in range(len(mseed)):
            if mseed[i].stats.channel == 'HHZ':
                stn.channel = 'HHZ'
                stream = i

        for i in range(len(mseed)):
            if mseed[i].stats.channel == 'EHZ':
                stn.channel = 'EHZ'
                stream = i

        for i in range(len(mseed)):
            if mseed[i].stats.channel == 'SHZ':
                stn.channel = 'SHZ'
                stream = i



        # Unpack miniSEED data
        delta = mseed[stream].stats.delta
        waveform_data = mseed[stream].data

        # Extract time
        start_datetime = mseed[stream].stats.starttime.datetime
        end_datetime = mseed[stream].stats.endtime.datetime

        stn.offset = (start_datetime - setup.fireball_datetime - datetime.timedelta(minutes=5)).total_seconds()

        # Skip stations with no data
        if len(waveform_data) == 0:
            continue

        # Apply the Kalenda et al. (2014) difference filter instead of Butterworth
        if difference_filter_all:

            waveform_data = convolutionDifferenceFilter(waveform_data)

        else:

            ### BANDPASS FILTERING ###

            # Init the butterworth bandpass filter
            butter_b, butter_a = butterworthBandpassFilter(0.8, 5.0, 1.0/delta, order=6)

            # Filter the data
            waveform_data = scipy.signal.filtfilt(butter_b, butter_a, waveform_data)

            # Average and subsample the array for quicker plotting (reduces 40Hz to 10Hz)
            waveform_data = subsampleAverage(waveform_data, 4)
            delta *= 4

            ##########################


        # Calculate the distance from the source point to this station (kilometers)
        station_dist = greatCircleDistance(np.radians(lat_centre), np.radians(lon_centre), stn.position.lat_r, stn.position.lon_r)

        # Construct time array, 0 is at start_datetime
        time_data = np.arange(0, (end_datetime - start_datetime).total_seconds(), delta)

        # Cut the waveform data length to match the time data
        waveform_data = waveform_data[:len(time_data)]
        time_data = time_data[:len(waveform_data)] + stn.offset
        
        # Detrend the waveform and normalize to fixed width
        waveform_data = waveform_data - np.mean(waveform_data)

        #waveform_data = waveform_data/np.percentile(waveform_data, 99)*2
        waveform_data = waveform_data/np.max(waveform_data)*10

        # Add the distance to the waveform
        waveform_data += station_dist


        # Cut the waveforms around the time of arrival, if the window for cutting was given.
        if waveform_window is not None:

            # Time of arrival
            toa = station_dist/(v_sound/1000) + t0

            # Cut the waveform around the time of arrival
            crop_indices = (time_data >= toa - waveform_window/2) & (time_data <= toa + waveform_window/2)
            time_data = time_data[crop_indices]
            waveform_data = waveform_data[crop_indices]
            

            # Skip plotting if array empty
            if len(time_data) == 0:
                continue

        # Replace all NaNs with 0s
        waveform_data = np.nan_to_num(waveform_data, 0)
        
        max_time = np.max([max_time, np.max(time_data)])
        min_time = np.min([min_time, np.min(time_data)])

        # Keep track of minimum and maximum waveform values (used for plotting)
        max_wave_value = np.max([max_wave_value, np.max(waveform_data)])
        min_wave_value = np.min([min_wave_value, np.min(waveform_data)])

        if setup.colortoggle:
            c = plt.cm.plasma(az[idx])
        else:
            c = None
            
        #if data_list[idx][1].strip() not in setup.rm_stat: 
            
        # Plot the waveform on the the time vs. distance graph
        ax.plot(waveform_data, time_data, c=c, alpha=0.7, linewidth=0.2, zorder=2)

        if stn.code in setup.rm_stat:
            print('Excluding station: {:}'.format(stn.network + '-' + stn.code))
        else:
            # Print the name of the station
            # Fragmentation
            if stn.code in setup.high_f:
                ax.text(np.mean(waveform_data), np.max(time_data), "{:} - {:} \n Az: {:5.1f}".format(stn.network, stn.code, az_n[idx]), \
                    rotation=270, va='bottom', ha='center', size=7, zorder=2, color="g")
            # Ballistic
            elif stn.code in setup.high_b:
                ax.text(np.mean(waveform_data), np.max(time_data), "{:} - {:} \n Az: {:5.1f}".format(stn.network, stn.code, az_n[idx]), \
                    rotation=270, va='bottom', ha='center', size=7, zorder=2, color="b")
        
            else:
                ax.text(np.mean(waveform_data), np.max(time_data), "{:} - {:} \n Az: {:5.1f}".format(stn.network, stn.code, az_n[idx]), \
                    rotation=270, va='bottom', ha='center', size=7, zorder=2, color="w")

    toa_line_time = np.linspace(0, max_time, 10)

    # Plot the constant sound speed line (assumption is that the release happened at t = 0)
    ax.plot((toa_line_time)*v_sound/1000, (toa_line_time + t0), color='r', alpha=0.25, linewidth=1, \
        zorder=2, label="$V_s = " + "{:d}".format(int(v_sound)) + r" \rm{ms^{-1}}$")

    # Reference location for the local coordinate system
    ref_pos = position(lat_centre, lon_centre, 0)
    # Ballistic Prediction
    b_time = [0]*len(stn_list)
    b_dist = [0]*len(stn_list)
    rb_dist = [0]*len(stn_list)

    good_stats = (x for x in range(len(stn_list)) if x not in bad_stats)
    
    print('')

    if setup.perturb_times <= 0 and setup.perturb:
        print("ERROR: perturb_times must be greater than 0")

    # for ptb_n in range(setup.perturb_times):
    #     # Manual search for ballistic wave
    #     if ptb_n > 0:
    #         print("STATUS: Perturbation: {:}".format(ptb_n))
    #         sounding_p = perturb(sounding, setup.perturb_method)
    #     else:
            # sounding_p = sounding
    sounding_p = sounding

    if setup.show_ballistic_waveform:
        # Input coordinate type. True - coordinates are given as lat/lon. False - coordinates are given in local 
        # coordinates in reference to the source center

        # Convert to local coordinates
        setup.traj_f.pos_loc(ref_pos)


        for stn in good_stats:

            if stn_list[stn].code.strip() not in setup.rm_stat: 
                # Station location in local coordinates

                stn_list[stn].position.pos_loc(ref_pos)

                # Time to travel from trajectory to station
                b_time[i] = timeOfArrival([stn_list[stn].position.x, stn_list[stn].position.y, stn_list[stn].position.z], setup.traj_f.x/1000, setup.traj_f.y/1000, setup.t0, 1000*setup.v, \
                                            np.radians(setup.azim), np.radians(setup.zangle), setup, sounding=sounding_p, fast=True)# - setup.t + setup.t0

                # Point on trajectory where wave is released
                bx, by, bz = waveReleasePoint([stn_list[stn].position.x, stn_list[stn].position.y, stn_list[stn].position.z], setup.traj_f.x, setup.traj_f.y, setup.t0, 1000*setup.v, \
                                            np.radians(setup.azim), np.radians(setup.zangle), setup.v_sound)

                # Distance from source center to station
                b_dist[i] = ((stn_list[stn].position.x)**2 + (stn_list[stn].position.y)**2)**0.5

                # Distance from ballistic wave to station
                rb_dist[i] = ((stn_list[stn].position.x - bx)**2 + (stn_list[stn].position.y - by)**2 + (stn_list[stn].position.z - bz)**2)**0.5

                # Convert to km
                b_dist[i] /= 1000
                rb_dist[i] /= 1000

            else:
                b_dist[i], b_time[i], rb_dist[i] = np.nan, np.nan, np.nan
            
    # Plot Ballistic Prediction
    # if ptb_n == 0:
    ax.scatter(b_dist, b_time, c='b', marker='_', s=100, label='Ballistic', zorder=3)
    # else:
    #    ax.scatter(b_dist, b_time, c='b', marker='_', s=100, alpha=0.3, zorder=3)

    # Fragmentation Prediction
    f_time = [0]*len(stn_list)
    f_dist = [0]*len(stn_list)
    rf_dist = [0]*len(stn_list)

    # Manual search for fragmentation waves
    if setup.show_fragmentation_waveform:

        if len(setup.fragmentation_point) == 0:
            print("ERROR: Cannot plot fragmentation if there is no fragmentation point. Set show_fragmentation_waveform = False if not using.")
            exit()

        for j, line in enumerate(setup.fragmentation_point):
            # Supracenter location in local coordinates
            supra = position(float(line[0]), float(line[1]), float(line[2]))

            supra.pos_loc(ref_pos)

            for i, stn in enumerate(stn_list):

                if stn.code.strip() not in setup.rm_stat:
                    if stn in bad_stats:
                        f_dist[i], f_time[i], rf_dist[i] = np.nan, np.nan, np.nan
                    # Station location in local coordinates

                    stn.position.pos_loc(ref_pos)

                    ###### DIFFERENT WEATHERS HERE ######
                    if setup.weather_type == 'none':
                        zProfile = np.array([[0, setup.v_sound, 0, 0], [10000, setup.v_sound, 0, 0]])

                    else:   
                    # Cut down atmospheric profile to the correct heights, and interp
                        zProfile, _ = getWeather(np.array([supra.x, supra.y, supra.z]), np.array([stn.position.x, stn.position.y, stn.position.z]), setup.weather_type, \
                            [ref_pos.lat, ref_pos.lon, ref_pos.elev], sounding_p, convert=True)

                    # Time to travel from Supracenter to station 
                    f_time[i], _, _ = cyscan(np.array([supra.x, supra.y, supra.z]), np.array([stn.position.x, stn.position.y, stn.position.z]), zProfile, wind=True)
                    
                    # Add reference time
                    f_time[i] += float(line[3])
                    
                    # Distance from source center to station
                    f_dist[i] = ((stn.position.x)**2 + (stn.position.y)**2)**0.5

                    # Distance from Supracenter to station
                    rf_dist[i] = ((stn.position.x - supra.x)**2 + (stn.position.y - supra.y)**2 + (stn.position.z - supra.z)**2)**0.5

                    # Convert to km
                    f_dist[i] /= 1000
                    rf_dist[i] /= 1000

                else:
                    f_dist[i], f_time[i], rf_dist[i] = np.nan, np.nan, np.nan
                    # Plot Fragmentation Prediction
            # if ptb_n == 0:
            #ax.scatter(f_dist, f_time, c=C[(j+1)%4], marker='_', s=100, label='Fragmentation {:}'.format(j+1), zorder=3)
            else:
                ax.scatter(f_dist, f_time, c=C[(j+1)%4], marker='_', s=100, alpha=0.3, zorder=3)
            ax.scatter(f_dist[i], f_time[i], c=C[(j+1)%4], marker='_', s=100, label='Fragmentation', zorder=3)


        
    ax.set_xlabel('Distance (km)')
    ax.set_ylabel('Time (s)')

    #ax.set_ylim(min_time - 200, max_time + 500)
    ax.set_xlim(0, max_wave_value)

    ax.grid(color='#ADD8E6', linestyle='dashed', linewidth=0.5, alpha=0.7)

    # Export station distance file
    with open(os.path.join(dir_path, 'output.txt'), 'w') as f:

        f.write('Station Lat(deg N) Lon(deg E) Elev(m) Az(+E dN) Ball_d(km) Ball_t(s) Frag_d(km) Frag_t(s)\n')
        for i, stn in enumerate(stn_list):
            f.write('{:8}, {:8.4f}, {:8.4f}, {:7.2f}, {:8.3f},  {:7.2f},  {:7.2f},  {:7.2f},  {:7.2f}\n'\
             .format(str(stn.network) + '-' + str(stn.code), stn.position.lat, stn.position.lon, \
                    stn.position.elev, az_n[i], rb_dist[i], b_time[i], rf_dist[i], f_time[i]))
Example #14
0
    def rayTraceFromSource(self, source, clean_mode=False, debug=False):

        traj = self.bam.setup.trajectory

        ### Set up parameters of source

        stat_idx = self.station_combo.currentIndex()
        stat = self.bam.stn_list[stat_idx]
        stat_pos = stat.metadata.position

        lat = [source.lat, stat_pos.lat]
        lon = [source.lon, stat_pos.lon]
        elev = [source.elev, stat_pos.elev]

        if not clean_mode:
            print("Source Location")
            print(source)
            print("Station Location")
            print(stat_pos)

        sounding, perturbations = self.bam.atmos.getSounding(
            lat=lat,
            lon=lon,
            heights=elev,
            spline=1000,
            ref_time=self.bam.setup.fireball_datetime)

        ref_pos = Position(self.bam.setup.lat_centre,
                           self.bam.setup.lon_centre, 0)

        stat_pos.pos_loc(source)
        source.pos_loc(source)

        source.z = source.elev
        stat_pos.z = stat_pos.elev

        h_tol = float(self.horizontal_tol.text())
        v_tol = float(self.vertical_tol.text())

        ### Ray Trace

        r, tr, f_particle = cyscan(np.array([source.x, source.y, source.z]), np.array([stat_pos.x, stat_pos.y, stat_pos.z]), \
                            sounding, trace=True, plot=False, particle_output=True, debug=False, \
                            wind=True, h_tol=h_tol, v_tol=v_tol, print_times=True, processes=1)

        if not clean_mode:
            az = np.radians(r[1])
            tf = np.radians(180 - r[2])
            u = np.array([traj.vector.x, traj.vector.y, traj.vector.z])
            v = np.array([
                np.sin(az) * np.sin(tf),
                np.cos(az) * np.sin(tf), -np.cos(tf)
            ])
            angle_off = abs(
                np.degrees(
                    np.arccos(
                        np.dot(u / np.sqrt(u.dot(u)), v /
                               np.sqrt(v.dot(v))))) - 90)
            if not self.pertstog.isChecked():
                dx = np.abs(stat_pos.x - source.x)
                dy = np.abs(stat_pos.y - source.y)
                dz = np.abs(stat_pos.z - source.z)
                time_along_trajectory = traj.findTime(source.elev)

                print("###### RESULTS ######")
                print("Time Along Trajectory (wrt Reference): {:.4f} s".format(
                    time_along_trajectory))
                print("Acoustic Path Time: {:.4f} s".format(r[0]))
                print("Total Time from Reference: {:.4f} s".format(
                    r[0] + time_along_trajectory))
                print("Launch Angle {:.2f} deg".format(angle_off))
                print("###### EXTRAS #######")
                print("Time: {:.4f} s".format(r[0]))
                print("Azimuth: {:.2f} deg from North".format(r[1]))
                print("Takeoff: {:.2f} deg from up".format(r[2]))
                print("Error in Solution {:.2f} m".format(r[3]))
                print("Distance in x: {:.2f} m".format(dx))
                print("Distance in y: {:.2f} m".format(dy))
                print("Distance in z: {:.2f} m".format(dz))
                print("Horizontal Distance: {:.2f} m".format(
                    np.sqrt(dx**2 + dy**2)))
                print("Total Distance: {:.2f} m".format(
                    np.sqrt(dx**2 + dy**2 + dz**2)))
                print("No Winds Time: {:.2f} s".format(
                    np.sqrt(dx**2 + dy**2 + dz**2) / 330))
                print("Time Difference: {:.2f} s".format(
                    r[0] - np.sqrt(dx**2 + dy**2 + dz**2) / 330))
                print("Time Along Trajectory: {:.4f} s".format(
                    time_along_trajectory))
                print("Total Time from Reference: {:.4f} s".format(
                    r[0] + time_along_trajectory))
            else:
                t_array = []
                az_array = []
                tk_array = []
                err_array = []
                angle_off_array = []
                t_array.append(r[0])
                az_array.append(r[1])
                tk_array.append(r[2])
                err_array.append(r[3])
                angle_off_array.append(angle_off)

        try:

            N_LAYERS = 1

            for i in range(N_LAYERS):
                try:
                    ba, tf = determineBackAz(tr[-(i + 2), :], tr[-1, :],
                                             sounding[0, 2],
                                             np.degrees(sounding[0, 3]))

                    if hasattr(self, "plot_ba_data"):
                        self.plot_ba_data.append([source.elev / 1000, ba, tf])
                except IndexError:
                    pass

        except TypeError:
            pass

        ### Plot begin and end points of ray-trace

        self.rtv_graph.ax.scatter(source.lon,
                                  source.lat,
                                  source.elev,
                                  c='r',
                                  marker='*',
                                  s=200)
        self.rtv_graph.ax.scatter(stat_pos.lon,
                                  stat_pos.lat,
                                  stat_pos.elev,
                                  c='b',
                                  marker='^',
                                  s=200)

        positions = []

        ### Plot trace of eigen-ray

        try:
            path_len = 0
            for i in range(len(tr[:, 0])):
                A = Position(0, 0, 0)
                A.x, A.y, A.z = tr[i, 0], tr[i, 1], tr[i, 2]
                if i > 0:
                    path_len += np.sqrt((tr[i, 0] - tr[i - 1, 0])**2 +
                                        (tr[i, 1] - tr[i - 1, 1])**2 +
                                        (tr[i, 2] - tr[i - 1, 2])**2)

                else:
                    path_len += np.sqrt((tr[i, 0] - 0)**2 + (tr[i, 1] - 0)**2 +
                                        (tr[i, 2] - 0)**2)

                A.pos_geo(source)
                positions.append([A.lon, A.lat, A.elev])

            if not clean_mode:
                print("Total Path Length: {:.2f} m".format(path_len))
                print("Approximate Ray Time: {:.2f} s".format(path_len / 330))
            positions = np.array(positions)
            if not clean_mode:

                self.rtv_graph.ax.scatter(positions[:, 0],
                                          positions[:, 1],
                                          positions[:, 2],
                                          c='b',
                                          alpha=0.5)
            self.rtv_graph.ax.plot(positions[:, 0],
                                   positions[:, 1],
                                   positions[:, 2],
                                   c='k')

            self.current_eigen = [positions, tr[:, -1]]

            err = np.sqrt((stat_pos.x - tr[-1, 0])**2 +
                          (stat_pos.y - tr[-1, 1])**2 +
                          (stat_pos.z - tr[-1, 2])**2)

            h_err = np.sqrt((stat_pos.x - tr[-1, 0])**2 +
                            (stat_pos.y - tr[-1, 1])**2)
            v_err = np.sqrt((stat_pos.z - tr[-1, 2])**2)

            if debug:
                print(
                    "Source Height: {:.2f} km - Final Error: {:.2f} m (v) {:.2f} m (h)"
                    .format(source.elev / 1000, v_err, h_err))

            self.rtv_graph.ax.scatter(positions[-1, 0],
                                      positions[-1, 1],
                                      positions[-1, 2],
                                      c='g')

            self.current_loaded_rays.append([positions, tr[:, -1]])
            self.plothvt()
        except:
            pass

        if not clean_mode:
            for sol in f_particle:
                r = anglescan(np.array([source.x, source.y, source.z]),
                              sol[0],
                              sol[1],
                              sounding,
                              trace=True,
                              debug=False,
                              wind=True)
                tr = np.array(r[1])
                positions = []
                for i in range(len(tr[:, 0])):
                    A = Position(0, 0, 0)
                    A.x, A.y, A.z = tr[i, 0], tr[i, 1], tr[i, 2]
                    A.pos_geo(source)
                    positions.append([A.lon, A.lat, A.elev])
                positions = np.array(positions)
                # self.rtv_graph.ax.plot(positions[:, 0], positions[:, 1], positions[:, 2], alpha=0.3)
                err = np.sqrt((stat_pos.x - tr[-1, 0])**2 +
                              (stat_pos.y - tr[-1, 1])**2 +
                              (stat_pos.z - tr[-1, 2])**2)
                if err <= 1000:
                    self.rtv_graph.ax.scatter(positions[-1, 0],
                                              positions[-1, 1],
                                              positions[-1, 2],
                                              c='g')
                else:
                    self.rtv_graph.ax.scatter(positions[-1, 0],
                                              positions[-1, 1],
                                              positions[-1, 2],
                                              c='r')

        if self.pertstog.isChecked() and len(perturbations) > 0:
            if clean_mode:
                t_array = []
                az_array = []
                tk_array = []
                err_array = []
                angle_off_array = []
            for pert_idx, pert in enumerate(perturbations):
                sys.stdout.write("\r Working on Perturbation {:}/{:}".format(
                    pert_idx + 1, len(perturbations)))
                sys.stdout.flush()
                r, tr, f_particle = cyscan(np.array([source.x, source.y, source.z]), np.array([stat_pos.x, stat_pos.y, stat_pos.z]), \
                                pert, trace=True, plot=False, particle_output=True, debug=False, \
                                wind=True, h_tol=h_tol, v_tol=v_tol, print_times=True)

                t_array.append(r[0])
                az_array.append(r[1])
                tk_array.append(r[2])
                err_array.append(r[3])

                az = np.radians(r[1])
                tf = np.radians(180 - r[2])
                u = np.array([traj.vector.x, traj.vector.y, traj.vector.z])
                v = np.array([
                    np.sin(az) * np.sin(tf),
                    np.cos(az) * np.sin(tf), -np.cos(tf)
                ])
                angle_off = abs(
                    np.degrees(
                        np.arccos(
                            np.dot(u / np.sqrt(u.dot(u)), v /
                                   np.sqrt(v.dot(v))))) - 90)
                angle_off_array.append(angle_off)

                try:

                    ba = determineBackAz(tr[-2, :], tr[-1, :], pert[0, 2],
                                         np.degrees(pert[0, 3]))

                    # print("Height: {:.2f} km".format(source.elev/1000))
                    # print("Back Azimuth: {:.2f} deg".format(ba))
                    # print("Travel Time: {:.2f} s".format(r[0]))
                    # print("Approx: {:.2f} deg".format(last_azimuth))
                    # print("Pure Winds: {:.2f} deg".format(np.degrees(sounding[0, 3])%360))
                    self.plot_ba_data.append([source.elev / 1000, ba, r[0]])

                except TypeError:
                    pass

            print()
            print("###### NOMINAL RESULTS ######")
            print("Time: {:.4f} s".format(t_array[0]))
            print("Azimuth: {:.2f} deg from North".format(az_array[1]))
            print("Takeoff: {:.2f} deg from up".format(tk_array[2]))
            print("Error in Solution {:.2f} m".format(err_array[3]))
            print("### UNCERTAINTIES ###")
            print("Time: {:.4f} - {:.4f} s ({:.4f} s)".format(
                np.nanmin(t_array), np.nanmax(t_array),
                np.nanmax(t_array) - np.nanmin(t_array)))
            print(
                "Azimuth: {:.2f} - {:.2f} deg from North ({:.2f} deg)".format(
                    np.nanmin(az_array), np.nanmax(az_array),
                    np.nanmax(az_array) - np.nanmin(az_array)))
            print("Takeoff: {:.2f} - {:.2f} deg from up ({:.2f} deg)".format(
                np.nanmin(tk_array), np.nanmax(tk_array),
                np.nanmax(tk_array) - np.nanmin(tk_array)))
            print("Error in Solution {:.2f} - {:.2f} m ({:.2f} m)".format(
                np.nanmin(err_array), np.nanmax(err_array),
                np.nanmax(err_array) - np.nanmin(err_array)))
            print("Angle Off {:.2f} - {:.2f} deg ({:.2f} deg)".format(
                np.nanmin(angle_off_array), np.nanmax(angle_off_array),
                np.nanmax(angle_off_array) - np.nanmin(angle_off_array)))

            print("Saving CSV of Perturbations")
            file_name = saveFile("csv", note="Perturbations")

            with open(file_name, "w+") as f:
                f.write(
                    "Height [km], Time [s], Azimuth [deg from North], Takeoff [deg from Up], Error [m], Angle Off [deg]\n"
                )
                for ll, line in enumerate(t_array):
                    if ll == len(t_array):
                        f.write("{:}, {:}, {:}, {:}, {:}, {:}".format(
                            source.elev / 1000, t_array[ll], az_array[ll],
                            tk_array[ll], err_array[ll], angle_off_array[ll]))
                    else:
                        f.write("{:}, {:}, {:}, {:}, {:}, {:}\n".format(
                            source.elev / 1000, t_array[ll], az_array[ll],
                            tk_array[ll], err_array[ll], angle_off_array[ll]))
Example #15
0
def waveReleasePointWindsContour(bam,
                                 traj,
                                 ref_loc,
                                 points,
                                 div=37,
                                 mode='ballistic'):
    setup = bam.setup
    atmos = bam.atmos
    steps = 90
    alpha = np.linspace(0, 360 * ((steps - 1) / steps), steps)
    alpha = np.radians(alpha)
    beta = np.linspace(0, 90 * ((steps - 1) / steps), steps)
    beta = np.radians(beta)
    theta = setup.trajectory.azimuth.rad
    phi = setup.trajectory.zenith.rad
    tol = 25  #deg
    # tol_fact = np.radians(np.linspace(-tol, tol, 10))
    WIND = True
    u = traj.getTrajVect()

    v_list = []
    for i in range(steps):
        for j in range(steps):

            v = np.array([np.sin(alpha[i])*np.sin(beta[j]),\
                        np.cos(alpha[i])*np.sin(beta[j]),\
                        -np.cos(beta[j])])

            if np.abs(90 - np.degrees(np.arccos(np.dot(u, v)))) <= tol:

                v_list.append([alpha[i], beta[j]])

    v_list = np.array(v_list)

    results = []

    # temp hotfix
    if mode == 'ballistic_old':

        grid_space = 25

        p1 = Position(43.8, 13.1, 0)
        p2 = Position(47.8, 17.1, 0)

        p1.pos_loc(ref_loc)
        p2.pos_loc(ref_loc)

        xs = np.linspace(p1.x, p2.x, grid_space)
        ys = np.linspace(p1.y, p2.y, grid_space)

        n_steps = grid_space**2 * len(points)
        for xnum, xx in enumerate(xs):
            for ynum, yy in enumerate(ys):

                angle_list = []
                time_list = []
                D = [xx, yy, 0]

                for pnum, pp in enumerate(points):

                    step = pnum + ynum * len(points) + xnum * grid_space * len(
                        points)
                    loadingBar("Contour Calculation", step, n_steps)

                    P = Position(pp[0], pp[1], pp[2])

                    P.pos_loc(ref_loc)

                    S = P.xyz

                    R = Position(0, 0, 0)
                    R.x = D[0]
                    R.y = D[1]
                    R.z = D[2]
                    R.pos_geo(ref_loc)

                    lats = [P.lat, R.lat]
                    lons = [P.lon, R.lon]
                    elev = [P.elev, R.elev]

                    z_profile, _ = atmos.getSounding(
                        lats,
                        lons,
                        elev,
                        ref_time=setup.fireball_datetime,
                        spline=100)

                    res = cyscan(np.array(S),
                                 np.array(D),
                                 z_profile,
                                 wind=True,
                                 h_tol=330,
                                 v_tol=3000,
                                 debug=False)

                    alpha = np.radians(res[1])
                    beta = np.radians(180 - res[2])

                    res_vector = np.array([np.sin(alpha)*np.sin(beta),\
                                np.cos(alpha)*np.sin(beta),\
                                -np.cos(beta)])

                    angle_list.append(
                        np.abs(90 - np.degrees(
                            np.arccos(
                                np.dot(
                                    u / np.sqrt(u.dot(u)), res_vector /
                                    np.sqrt(res_vector.dot(res_vector)))))))
                    time_list.append(res[0])

                if np.nanmin(angle_list) <= tol:

                    best_angle_index = np.nanargmin(angle_list)

                    best_time = time_list[best_angle_index]

                    if not np.isnan(best_time):
                        res = [xx, yy, 0, best_time]

                        results.append(res)

            # u = np.array([bam.setup.trajectory.vector.x,
            #               bam.setup.trajectory.vector.y,
            #               bam.setup.trajectory.vector.z])

            # angle_off = []
            # X = []
            # for i in range(len(bam.setup.fragmentation_point)):
            #     az = stn.times.fragmentation[i][0][1]
            #     tf = stn.times.fragmentation[i][0][2]

            #     az = np.radians(az)
            #     tf = np.radians(180 - tf)
            #     v = np.array([np.sin(az)*np.sin(tf), np.cos(az)*np.sin(tf), -np.cos(tf)])

            #     angle_off.append(np.degrees(np.arccos(np.dot(u/np.sqrt(u.dot(u)), v/np.sqrt(v.dot(v))))))
            #     X.append(bam.setup.fragmentation_point[i].position.elev)
            # angle_off = np.array(angle_off)
            # try:
            #     best_indx = np.nanargmin(abs(angle_off - 90))

            # except ValueError:
            #     best_indx = None
            #     a.append(np.array([np.nan, np.nan, np.nan, np.nan]))
            #     for pert in perturbations:
            #         a.append(np.array([np.nan, np.nan, np.nan, np.nan]))
            #     stn.times.ballistic.append(a)
            #     continue

            # np.array([t_arrival, azimuth, takeoff, E[k, l]])

    elif mode == 'ballistic':
        n_steps = len(v_list) * len(points)
        WIND = True
        for pp, p in enumerate(points):

            for vv, v in enumerate(v_list):

                step = vv + pp * len(v_list)
                loadingBar("Contour Calculation", step, n_steps)

                # v[i] = np.array([np.sin(alpha[i])*np.sin(beta[i]),\
                #                  np.cos(alpha[i])*np.sin(beta[i]),\
                #                                 -np.cos(beta[i])])

                P = Position(p[0], p[1], p[2])
                S = Position(p[0], p[1], 0)

                P.pos_loc(ref_loc)

                pt = P.xyz
                # s = p + p[2]/np.cos(beta[i])*v[i]
                # S = Position(0, 0, 0)
                # P = Position(0, 0, 0)
                # S.x, S.y, S.z = s[0], s[1], s[2]
                # P.x, P.y, P.z = p[0], p[1], p[2]
                # S.pos_geo(ref_loc)
                # P.pos_geo(ref_loc)

                lats = [P.lat, S.lat]
                lons = [P.lon, S.lon]
                elev = [P.elev, S.elev]
                # z_profile, _ = supra.Supracenter.cyweatherInterp.getWeather(p, s, setup.weather_type, \
                #      ref_loc, copy.copy(sounding), convert=True)
                if WIND:
                    z_profile, _ = atmos.getSounding(
                        lats,
                        lons,
                        elev,
                        ref_time=setup.fireball_datetime,
                        spline=200)

                    res = anglescan(pt,
                                    np.degrees(v[0]),
                                    np.degrees(v[1]) + 90,
                                    z_profile,
                                    wind=True,
                                    debug=False)

                else:
                    # if no wind, just draw a straight line

                    vect = np.array([np.sin(v[0])*np.sin(v[1]),\
                            np.cos(v[0])*np.sin(v[1]),\
                            -np.cos(v[1])])

                    s = -pt[2] / vect[2]

                    ground_point = pt + s * vect

                    ground_time = s / 330

                    res = [
                        ground_point[0], ground_point[1], ground_point[2],
                        ground_time
                    ]

                # This is the limit in distance from the trajectory (hardcoded)
                if res[-1] <= 1000:
                    # if np.sqrt(res[0]**2 + res[1]**2) <= 150000:
                    results.append(res)

    else:
        # n_steps = len(tol_fact)*len(points)*steps

        beta = np.linspace(90 + 0.01, 180, steps)
        beta = np.radians(beta)
        p = points

        for ii, i in enumerate(range(steps)):
            for jj, j in enumerate(range(steps)):
                # print(np.degrees(beta[i]))
                step = jj + ii * steps
                loadingBar("Contour Calculation", step, n_steps)


                v[i*steps + j] = np.array([np.sin(alpha[i])*np.sin(beta[j]),\
                                 np.cos(alpha[i])*np.sin(beta[j]),\
                                                -np.cos(beta[j])])
                s = p + p[2] / np.cos(beta[j]) * v[i * steps + j]

                S = Position(0, 0, 0)
                P = Position(0, 0, 0)
                S.x, S.y, S.z = s[0], s[1], s[2]
                P.x, P.y, P.z = p[0], p[1], p[2]
                S.pos_geo(ref_loc)
                P.pos_geo(ref_loc)

                lats = [P.lat, S.lat]
                lons = [P.lon, S.lon]
                elev = [P.elev, S.elev]

                z_profile, _ = atmos.getSounding(
                    lats,
                    lons,
                    elev,
                    ref_time=setup.fireball_datetime,
                    spline=100)
                res = anglescan(p,
                                np.degrees(alpha[i]),
                                np.degrees(beta[j]),
                                z_profile,
                                wind=True,
                                debug=False)

                # if np.sqrt(res[0]**2 + res[1]**2) <= 200000:
                results.append(res)

    return results
Example #16
0
def timeFunction(x, *args):
    ''' Helper function for PSO
    Takes in supracenter ranges, and calculates the travel time, which is used to find the error.
    Returns the residual error with each guess of a supracenter from pso()

    Arguments:
        x: [ndarray] position to search with
        args: list of passed arguments
            stns: [list] list of station positions and arrival times
            w: [list] list of station weights
            kotc: [float] user-defined occurence time
            tweaks: [Object] user-defined option
            ref_pos: [list] mean station location used for converting to/from local coordinates
            dataset: [ndarray] atmospheric profile for the entire search area
            pool: [multiprocessing pool] pool of workers for multiprocessing

    Returns:
        err: [float] error in the current position, x, searched
    '''

    # Retrieve passed arguments
    stns, w, kotc, setup, ref_pos, atmos, prefs, v, pert_num, theo = args

    # number of stations total
    n_stations = len(stns)

    # Residuals to each station
    sotc = np.empty(n_stations)

    # Initialize variables
    # Weight of each station
    wn = w

    # total weight
    nwn = sum(w)

    # Mean occurrence time
    motc = 0

    S = Position(0, 0, 0)
    S.x, S.y, S.z = x[0], x[1], x[2]
    S.pos_geo(ref_pos)

    # number of stations total
    n_stations = len(stns)

    # Station Times
    tobs = stns[0:n_stations, 4]

    # Station Location
    xstn = stns[0:n_stations, 0:3]

    # Initialize arrays
    # Simulated travel times to each station
    time3D = np.empty(n_stations)

    # Residuals to each station
    sotc = np.empty(n_stations)

    for j in range(n_stations):
        # if station has weight
        if w[j] > 0:
            
            D = Position(0, 0, 0)
            D.x, D.y, D.z = xstn[j, 0], xstn[j, 1], xstn[j, 2]
            D.pos_geo(ref_pos)

            if not theo:
                if pert_num == 0:

                    # No perturbations used here
                    sounding, _ = atmos.getSounding(lat=[S.lat, D.lat], lon=[S.lon, D.lon], heights=[S.elev, D.elev], ref_time=setup.fireball_datetime)
               
                else:

                    # Sounding is a specfic perturbation number
                    # TODO: Go back and fix how this is done, not all perts need to be generated, just one here 
                    nom, sounding = atmos.getSounding(lat=[S.lat, D.lat], lon=[S.lon, D.lon], heights=[S.elev, D.elev], ref_time=setup.fireball_datetime)
                    
                    # sounding is none when there is an error in getting sounding
                    if sounding is not None:
                    
                        sounding = sounding[pert_num - 1]
                    
                    else:
                    
                        sounding = nom

                # Use distance and atmospheric data to find path time
                time3D[j], _, _, _ = cyscan(S.xyz, D.xyz, sounding, \
                            wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist, processes=1)
                # Residual time for each station
            else:
                distance = np.sqrt((S.x - D.x)**2 + (S.y - D.y)**2 + (S.z - D.z)**2)
                time3D[j] = distance/prefs.avg_sp_sound
            sotc[j] = tobs[j] - time3D[j]

        # If station has no weight
        else:
            sotc[j] = tobs[j]

    motc = np.nanmean(sotc)
    ##########
    N_s = np.count_nonzero(~np.isnan(sotc))
    # User defined occurrence time
    if kotc != None:

        # make kOTC a list
        err = np.dot(wn, np.absolute(sotc - np.array([kotc]*n_stations)))/nwn

    # Unknown occurrence time
    else:
        #err = np.dot(wn, np.absolute(sotc - motc))/nwn
        N_s = 0
        err = 0

        for s in sotc:
            if np.isnan(s):
                continue
            else:
                err += (1 + (s - motc)**2)**0.5 - 1
                N_s += 1
        
        if N_s == 0:
            err = np.inf
        else:
            err = err/N_s


    # if setup.debug:
    #     # print out current search location
    #     print("Supracenter: {:10.2f} m x {:10.2f} m y {:10.2f} m z  Time: {:8.2f} Error: {:25.2f}".format(x[0], x[1], x[2], motc, err))

    # variable to be minimized by the particle swarm optimization
    
    perc_fail = 100 - (n_stations-N_s)/n_stations*100

    # temporary adjustment to try and get the most stations
    if N_s >= 3:
        total_error = err# + 2*max(error_list)*(failed_stats)
    else:
        total_error = np.inf

    if np.isnan(total_error):
        total_error = np.inf

    if prefs.debug:
        print("Error {:10.4f} | Solution {:10.4f}N {:10.4f}E {:8.2f} km {:8.2f} s | Failed Stats {:3} {:}".format(total_error, S.lat, S.lon, S.elev/1000, motc, n_stations-N_s, printPercent(perc_fail, N_s)))
        # Quick adjustment to try and better include stations


    return total_error
Example #17
0
def waveReleasePointWinds(stat_coord, bam, prefs, ref_loc, points, u):
    #azim = (np.pi - azim)%(2*np.pi)
    # Break up the trajectory into points

    # Trajectory vector
    #u = np.array([-np.cos(azim)*np.sin(zangle), np.sin(azim)*np.sin(zangle), -np.cos(zangle)])

    # Cut down atmospheric profile to the correct heights, and interp

    u = np.array([u.x, u.y, u.z])

    a = (len(points))

    D = np.array(stat_coord)

    cyscan_res = []
    import time

    # Compute time of flight residuals for all stations
    for i in range(a):

        S = np.array(points[i][0:3])
        traj_point = angle2Geo(points[i][0:3], ref_loc)
        stat_point = angle2Geo(stat_coord, ref_loc)

        lats = [traj_point.lat, stat_point.lat]
        lons = [traj_point.lon, stat_point.lon]
        heights = [traj_point.elev, stat_point.elev]

        if traj_point.elev < 17000 or traj_point.elev > 50000:
            continue

        sounding, perturbations = bam.atmos.getSounding(
            lats,
            lons,
            heights,
            ref_time=bam.setup.fireball_datetime,
            perturbations=0,
            spline=50)

        if prefs.wind_en:

            A = cyscan(S, D, sounding, \
                 wind=prefs.wind_en, h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist, processes=1)

        else:

            dx, dy, dz = D[0] - S[0], D[1] - S[1], D[2] - S[2]

            r = (dx**2 + dy**2 + dz**2)**0.5
            h = (dx**2 + dy**2)**0.5
            T = r / 330
            az = np.arctan2(dx, dy)
            tf = np.pi / 2 + np.arctan2(dz, h)

            A = np.array([T, az, tf, np.nan])

        az = np.radians(A[1])
        tf = np.radians(A[2])

        v = np.array(
            [np.sin(az) * np.sin(tf),
             np.cos(az) * np.sin(tf), -np.cos(tf)])

        mag_u = u / np.sqrt(u.dot(u))
        mag_v = v / np.sqrt(v.dot(v))

        angle = np.abs(90 - np.degrees(np.arccos(np.dot(mag_u, mag_v))))

        print("\tHeight: {:.2f} km | Angle: {:.2f} deg".format(
            traj_point.elev / 1000, angle))
        cyscan_res.append(A)

    T_nom = getTimes(np.array(cyscan_res), u, a)

    T_pert = []

    # if perturbations is not None:

    #     for p in range(len(perturbations)):

    #         cyscan_res = []
    #         for i in range(a):

    #             S = np.array(points[i])
    #             traj_point = angle2Geo(points[i][0:3], ref_loc)
    #             stat_point = angle2Geo(stat_coord, ref_loc)

    #             lats = [traj_point.lat, stat_point.lat]
    #             lons = [traj_point.lon, stat_point.lon]
    #             heights = [traj_point.elev, stat_point.elev]

    #             sounding, perturbations = bam.atmos.getSounding(lats, lons, heights)

    #             A_p = cyscan(S, D, perturbations[p], \
    #              wind=prefs.wind_en, n_theta=prefs.pso_theta, n_phi=prefs.pso_phi,
    #                 h_tol=prefs.pso_min_ang, v_tol=prefs.pso_min_dist)

    #             cyscan_res.append(A_p)

    #         T_pert.append(getTimes(np.array(cyscan_res), u, a))

    return T_nom, T_pert
Example #18
0
D = np.array([6.41420602e-13, 1.04751934e+04, 0.00000000e+00])
theta = 135
phi = 0

a2 = anglescan(S,
               phi,
               theta,
               sounding,
               wind=True,
               debug=True,
               trace=False,
               plot=False)

print("A2: ", a2)

c5 = cyscan(S, D, sounding, wind=True, h_tol=330, v_tol=330)

print("C5: ", c5)

c2 = cy2(S,
         D,
         sounding,
         wind=True,
         h_tol=330,
         v_tol=330,
         n_theta=1080,
         n_phi=1080)

print("C2: ", c2)

tt = np.sqrt(2) * 7000 / 300 + np.sqrt(2) * 3000 / 330
Example #19
0
    def integrate(self, height, D_ANGLE=1.5, tf=1, az=1):
        ref_pos = Position(self.setup.lat_centre, self.setup.lon_centre, 0)
        try:
            point = self.setup.trajectory.findGeo(height)
        except AttributeError:
            print("STATUS: No trajectory given, assuming lat/lon center")
            point = Position(self.setup.lat_centre, self.setup.lon_centre,
                             height)
        point.pos_loc(ref_pos)

        stn = self.stn_list[self.current_station]
        stn.metadata.position.pos_loc(ref_pos)

        lats = [point.lat, stn.metadata.position.lat]
        lons = [point.lon, stn.metadata.position.lon]
        elevs = [point.elev, stn.metadata.position.elev]

        # make the spline lower to save time here

        sounding, perturbations = self.bam.atmos.getSounding(
            lats,
            lons,
            elevs,
            spline=N_LAYERS,
            ref_time=self.setup.fireball_datetime)

        trans = []
        ints = []
        ts = []
        ps = []
        rfs = []

        if perturbations is None:
            ptb_len = 1
        else:
            ptb_len = len(perturbations) + 1

        for ptb_n in range(ptb_len):

            # Temporary adjustment to remove randomness from perts

            if ptb_n == 0:
                zProfile = sounding
            else:
                zProfile = perturbations[ptb_n - 1]

            S = np.array([point.x, point.y, point.z])
            D = np.array([
                stn.metadata.position.x, stn.metadata.position.y,
                stn.metadata.position.z
            ])

            _, az_n, tf_n, _ = cyscan(S, D, zProfile, wind=True,\
                h_tol=30, v_tol=30)

            self.T_d = tryFloat(self.freq_edits.text())

            self.v = 1 / self.T_d

            f, g, T, P, path_length, pdr, reed_attenuation = intscan(S,
                                                                     az_n,
                                                                     tf_n,
                                                                     zProfile,
                                                                     self.v,
                                                                     wind=True)

            self.reed_attenuation = reed_attenuation

            rf = refractiveFactor(point,
                                  stn.metadata.position,
                                  zProfile,
                                  D_ANGLE=D_ANGLE)
            trans.append(f)
            ints.append(g)
            ts.append(T)
            ps = P

            rfs.append(rf)

        return trans, ints, ts, ps, rfs, path_length, pdr