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
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
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)
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)
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
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()
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)
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)
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
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
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
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
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]))
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]))
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
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
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
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
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