def test_find_linspace_num(self): a = 7000e3 e = 0.0 T = n.pi * 2.0 * n.sqrt(a**3 / MU_earth) circ = n.pi * 2.0 * a num = simulate_tracking.find_linspace_num(t0=0.0, t1=T, a=a, e=e, max_dpos=circ / 10.0) self.assertEqual(num, 10)
def setUp(self): self.p = PropagatorKepler(in_frame='EME', out_frame='ITRF') self.radar = mock_radar() self.big_radar = mock_radar_mult() self.orbit = { 'a': 7500, 'e': 0, 'i': 90.0, 'raan': 0, 'aop': 0, 'mu0': 0.0, 'mjd0': 57125.7729, 'm': 0, } self.T = n.pi * 2.0 * n.sqrt(7500e3**3 / MU_earth) self.o = SpaceObject(C_D=2.3, A=1.0, C_R=1.0, oid=42, d=1.0, propagator=PropagatorKepler, propagator_options={ 'in_frame': 'EME', 'out_frame': 'ITRF', }, **self.orbit) #from pen and paper geometry we know that for a circular orbit and the radius of the earth at pole #that the place where object enters FOV is #true = mean = arccos(R_E / semi major) self.rise_ang = 90.0 - n.degrees(n.arccos(wgs84_a / 7500e3)) self.fall_ang = 180.0 - self.rise_ang #then we can find the time as a fraction of the orbit traversal from the angle self.rise_T = self.rise_ang / 360.0 * self.T self.fall_T = self.fall_ang / 360.0 * self.T self.num = simulate_tracking.find_linspace_num(t0=0.0, t1=self.T, a=7500e3, e=0.0, max_dpos=1e3) self.full_t = n.linspace(0, self.T, num=self.num)
def get_passes_simple(o,radar,t0,t1,max_dpos=100e3,debug=False, sanity_check=False): '''Follow object and find peak SNR. Assume that this occurs for minimum zenith angle of each TX. :param SpaceObject o: The object in space to be followed. :param RadarSystem radar: The radar system used for tracking. :param float t0: Start time for tracking :param float t1: End time for tracking :param float max_dpos: Maximum separation in m between orbital evaluation points, used to calculate time-step size by approximating orbits as circles :param bool debug: Verbose output :param bool sanity_check: Even more verbose output :return: Tuple of (Peak SNR for tracking, Number of receivers that can observe, time of best detection) ''' # figure out the number of time points we need to evaluate to meet the max_dpos criteria num_t = simulate_tracking.find_linspace_num(t0, t1, o.a*1e3, o.e, max_dpos=max_dpos) # time vector t=n.linspace(t0,t1,num=num_t) dt = (t1-t0)/num_t # propagate object for all time points requested ecef=o.get_orbit(t) # zenith angles zenith = [] # tx and rx site locations in ecef tx_ecef = [] rx_ecef = [] # zenith directions for each tx and rx site zenith_tx = [] zenith_tx = [] for tx in radar._tx: tx_ecef.append( tx.ecef ) zenith_tx.append( coord.azel_ecef(tx.lat, tx.lon, 0.0, 0.0, 90.0) ) zenith_rx = [] for rx in radar._rx: rx_ecef.append( rx.ecef ) zenith_rx.append( coord.azel_ecef(rx.lat, rx.lon, 0.0, 0.0, 90.0) ) # position vectors between tx and rx pos_rx = [] for rxp0 in rx_ecef: pos_vec=(ecef.T-rxp0).T pos_rx.append(pos_vec) n_tx=len(radar._tx) n_rx=len(radar._rx) # peak snr for tx->rx combo peak_snr=n.zeros([n_tx,n_rx]) # number of receivers that can observe TX n_rx_detections=n.zeros(n_tx) # for each transmitter for txi,txp0 in enumerate(tx_ecef): # tx tx = radar._tx[txi] # zenith direction vector for this TX zenith = zenith_tx[txi] # position vector pos_tx=(ecef.T-txp0).T # unit vector for tx->target position vector pos_tx0=pos_tx/n.sqrt(pos_tx[0,:]**2.0+pos_tx[1,:]**2.0+pos_tx[2,:]**2.0) # zenith -> target angle z_angles_tx=180.0*n.arccos(pos_tx0[0,:]*zenith[0]+pos_tx0[1,:]*zenith[1]+pos_tx0[2,:]*zenith[2])/n.pi # peak elevation angle min_z=n.min(z_angles_tx) det_idx=n.argmin(z_angles_tx) if min_z < (90.0-radar._tx[txi].el_thresh): # object possibly detectable pos_vec=pos_rx[txi][:,det_idx] tx_dist=n.linalg.norm(pos_vec) # point tx antenna towards target k0 = tx.point_ecef(pos_vec) gain_tx = tx.beam.gain(k0) # for all receivers for rxi,rx in enumerate(radar._rx): # position vector pos_rx_now=pos_rx[rxi][:,det_idx] # distance rx_dist=n.linalg.norm(pos_rx_now) # unit vector pos_rx_now0=pos_rx_now/rx_dist if sanity_check: pos = radar._rx[rxi].ecef + pos_rx_now0*rx_dist print("diff %d"%(rxi)) print((ecef[:,det_idx] - pos)) zenith = zenith_rx[rxi] # rx zenith -> target angle z_angle_rx=180.0*n.arccos(pos_rx_now0[0]*zenith[0]+pos_rx_now0[1]*zenith[1]+pos_rx_now0[2]*zenith[2])/n.pi # point towards object k0 = rx.point_ecef(pos_rx_now) gain_rx = rx.beam.gain(k0) snr=debris.hard_target_enr(gain_tx, gain_rx, rx.wavelength, tx.tx_power, tx_dist, rx_dist, diameter_m=o.diam, bandwidth=tx.coh_int_bandwidth, rx_noise_temp=rx.rx_noise) peak_snr[txi,rxi]=snr if snr >= tx.enr_thresh: n_rx_detections[txi]+=1.0 print("oid %d inc %1.2f diam %1.2f tx %d rx %d snr %1.2g min_range %1.2f (km) tx_dist %1.2f (km) rx_dist %1.2f (km) tx_zenith angle %1.2f rx zenith angle %1.2f"%(o.oid,o.i,o.diam,txi,rxi,snr,((1.0-o.e)*o.a)-6371.0,tx_dist/1e3,rx_dist/1e3,min_z,z_angle_rx)) else: print("oid {} not detected, SNR = {}".format(o.oid, snr)) return peak_snr, n_rx_detections, t[det_idx]
def get_detections(obj, radar, t0, t1, max_dpos=10.0e3, logger=None, pass_dt=None): '''Find all detections of a object by input radar between two times relative the object Epoch. :param SpaceObject obj: Space object to find detections of. :param RadarSystem radar: Radar system that scans for the object. :param float t0: Start time for scan relative space object epoch. :param float t1: End time for scan relative space object epoch. :param float max_dpos: Maximum separation between evaluation points in meters for finding the pass interval. :param Logger logger: Logger object for logging the execution of the function. :param float pass_dt: The time step used when evaluating pass. Default is the scan-minimum dwell time but can be forces to a setting by this variable. :return: Detections data structure in form of a list of dictionaries, see description below. :rtype: list **Return data:** List of same length as radar system TX antennas. Each entry in the list is a dictionary with the following items: * t0: List of pass start times. Length is equal the number of detection but unique times are equal to the number of passes.. * t1: List of pass end times, i.e. when the space object passes below the FOV. Same list configuration as "t0" * snr: List of lists of SNR's for each TX-RX pair for each detection. I.e. the top list length is equal the number of detections and the elements are lists of length equal to the number of TX-RX pairs. * tm: List of times corresponding to each detection, same length as "snr" item. * range: Same structure as the "snr" item but with ranges between the TX and the RX antenna trough the object, i.e. two way range. Unit is meters. * range_rate: Same structure as the "range" item but with range-rates, i.e. rate of change of two way range. Unit is meters per second. * tx_gain: List of gains from the TX antenna for the detection, length of list is equal the number of detections. * rx_gain: List of lists in the same structure as the "snr" item but with receiver gains instead of signal to noise ratios. * on_axis_angle: List of angles between the space object and the pointing direction for each detection, length of list is equal to the number of detections. ''' # list of transmitters txs = radar._tx # list of receivers rxs = radar._rx zenith = n.array([0, 0, 1], dtype=n.float) # list of detections for each transmitter-receiver pair # return detections, and also r, rr, tx gain, and rx gain detections = [] for tx in txs: detections.append({ "t0": [], "t1": [], "snr": [], 'tm': [], "range": [], "range_rate": [], "tx_gain": [], "rx_gain": [], "on_axis_angle": [] }) num_t = simulate_tracking.find_linspace_num(t0, t1, obj.a * 1e3, obj.e, max_dpos=max_dpos) if logger is not None: logger.debug("n_points {} at {} m resolution".format(num_t, max_dpos)) # time vector t = n.linspace(t0, t1, num=num_t, dtype=n.float) passes, passes_id, _, _, _ = simulate_tracking.find_pass_interval( t, obj, radar) for txi, pas in enumerate(passes): if pas is None: passes[txi] = [] for txi, pas in enumerate(passes_id): if pas is None: passes_id[txi] = [] #format: passes # [tx num][pass num][0 = above, 1 = below] if logger is not None: for txi in range(len(txs)): logger.debug('passes cnt: {}'.format(len(passes[txi]))) for txi, tx in enumerate(txs): for pas in passes[txi]: if pass_dt is None: num_pass = int((pas[1] - pas[0]) / tx.scan.min_dwell_time) else: num_pass = int((pas[1] - pas[0]) / pass_dt) t_pass = n.linspace(pas[0], pas[1], num=num_pass, dtype=n.float) if logger is not None: logger.debug('tx{} - pass{} - num_pass: {}'.format( txi, len(detections[txi]["t0"]), num_pass)) states = obj.get_state(t_pass) ecef = states[:3, :] vels = states[3:, :] pos_rel_tx = (ecef.T - tx.ecef).T snrs = n.empty((num_pass, len(rxs)), dtype=n.float) angles = n.empty((num_pass, ), dtype=n.float) ks_obj = n.empty((3, num_pass), dtype=n.float) ksr_obj = n.empty((3, num_pass, len(rxs)), dtype=n.float) k0s = n.empty((3, num_pass), dtype=n.float) range_tx = n.empty((num_pass, ), dtype=n.float) vel_tx = n.empty((num_pass, ), dtype=n.float) gain_tx = n.empty((num_pass, ), dtype=n.float) gain_rx = n.empty((num_pass, len(rxs)), dtype=n.float) r_rad = n.empty((num_pass, len(rxs)), dtype=n.float) v_rad = n.empty((num_pass, len(rxs)), dtype=n.float) snrs_mask = n.full(snrs.shape, False, dtype=n.bool) zenith_mask = n.full(snrs.shape, False, dtype=n.bool) inds_mask = n.full((num_pass, ), True, dtype=n.bool) inds = n.arange(num_pass, dtype=n.int) for I in range(num_pass): k0 = tx.get_scan(t_pass[I]).local_pointing(t_pass[I]) k0s[:, I] = k0 ks_obj[:, I] = coord.ecef2local( lat=tx.lat, lon=tx.lon, alt=tx.alt, x=pos_rel_tx[0, I], y=pos_rel_tx[1, I], z=pos_rel_tx[2, I], ) angles[I] = coord.angle_deg(k0s[:, I], ks_obj[:, I]) if angles[I] > radar.max_on_axis: inds_mask[I] = False inds_tmp = inds[inds_mask] if logger is not None: logger.debug('f1 inds left {}/{}'.format( inds_mask.shape, inds.shape)) for rxi, rx in enumerate(rxs): pos_rel_rx = (ecef.T - rx.ecef).T for I in inds_tmp: k_obj_rx = coord.ecef2local( lat=rx.lat, lon=rx.lon, alt=rx.alt, x=pos_rel_rx[0, I], y=pos_rel_rx[1, I], z=pos_rel_rx[2, I], ) ksr_obj[:, I, rxi] = k_obj_rx elevation_angle_rx = 90.0 - coord.angle_deg( zenith, k_obj_rx) if elevation_angle_rx < rx.el_thresh: continue zenith_mask[I, rxi] = True rx_dist = n.linalg.norm(pos_rel_rx[:, I]) rx_vel = n.dot(vels[:, I], pos_rel_rx[:, I] / rx_dist) r_rad[I, rxi] = rx_dist v_rad[I, rxi] = rx_vel for I in inds: if n.any(zenith_mask[I, :]): tx.beam.point_k0(k0s[:, I]) range_tx[I] = n.linalg.norm(pos_rel_tx[:, I]) vel_tx[I] = n.dot(vels[:, I], pos_rel_tx[:, I] / range_tx[I]) gain_tx[I] = tx.beam.gain(ks_obj[:, I]) for rxi, rx in enumerate(rxs): if logger is not None: logger.debug('f2_rx{} inds left {}/{}'.format( rxi, inds.shape, inds[zenith_mask[:, rxi]].shape)) for I in inds[zenith_mask[:, rxi]]: # TODO: We need to change this # Probably we need to define additional parameters in the RadarSystem class that defines the constraints on each receiver transmitter, and defines if any of them are at the same location. # what we need to do is give the RX a scan also that describes the pointing for detections when there is no after the fact beam-steering to do grid searches # if rx.phased: # point receiver towards object (post event beam forming) rx.beam.point_k0(ksr_obj[:, I, rxi]) else: #point according to receive pointing k0 = rx.get_scan(t_pass[I]).local_pointing(t_pass[I]) rx.beam.point_k0(k0) gain_rx[I, rxi] = rx.beam.gain(ksr_obj[:, I, rxi]) snr = debris.hard_target_enr( gain_tx[I], gain_rx[I, rxi], rx.wavelength, tx.tx_power, range_tx[I], r_rad[I, rxi], diameter_m=obj.d, bandwidth=tx.coh_int_bandwidth, rx_noise_temp=rx.rx_noise, ) #if logger is not None: # logger.debug('angles[{}] {} deg, gain_tx[{}] = {}, gain_rx[{}, {}] = {}'.format( # I, angles[I], # I, gain_tx[I], # I, rxi, gain_rx[I, rxi], # )) snrs[I, rxi] = snr if snr < tx.enr_thresh: continue snrs_mask[I, rxi] = True for I in inds: if n.any(snrs_mask[I, :]): inst_snrs = snrs[I, snrs_mask[I, :]] if 10.0 * n.log10(n.max(inst_snrs)) > radar.min_SNRdb: if logger is not None: logger.debug( 'adding detection at {} sec with {} SNR'. format(t_pass[I], snrs[I, :])) detections[txi]["t0"].append(pas[0]) detections[txi]["t1"].append(pas[1]) detections[txi]["snr"].append(snrs[I, :]) detections[txi]["range"].append(r_rad[I, :] + range_tx[I]) detections[txi]["range_rate"].append(v_rad[I, :] + vel_tx[I]) detections[txi]["tx_gain"].append(gain_tx[I]) detections[txi]["rx_gain"].append(gain_rx[I, :]) detections[txi]["tm"].append(t_pass[I]) detections[txi]["on_axis_angle"].append(angles[I]) return detections
def plot_scan_for_object(obj, radar, t0, t1, plot_full_scan=False): # list of transmitters txs = radar._tx # list of receivers rxs = radar._rx num_t = simulate_tracking.find_linspace_num(t0, t1, obj.a * 1e3, obj.e, max_dpos=10e3) # time vector t = n.linspace(t0, t1, num=num_t, dtype=n.float) passes, _, _, _, _ = simulate_tracking.find_pass_interval(t, obj, radar) #format: passes # [tx num][pass num][0 = above, 1 = below] fig = plt.figure(figsize=(15, 15)) ax = fig.add_subplot(111, projection='3d') ax.grid(False) ax.view_init(15, 5) plothelp.draw_earth_grid(ax) plot_radar_earth(ax, radar) scan_range = 1200e3 lab_done = False lab_done_so = False cycle_complete = False for txi, tx in enumerate(txs): for pas in passes[txi]: num_pass = int((pas[1] - pas[0]) / tx.scan.min_dwell_time) t_pass = n.linspace(pas[0], pas[1], num=num_pass, dtype=n.float) ecefs = obj.get_orbit(t_pass) if lab_done_so: ax.plot(ecefs[0, :], ecefs[1, :], ecefs[2, :], '-k') else: lab_done_so = True ax.plot(ecefs[0, :], ecefs[1, :], ecefs[2, :], '-k', label='Space Object') for I in range(num_pass): scan = tx.get_scan(t_pass[I]) if scan._scan_time is not None: if t_pass[I] - pas[0] > scan._scan_time: cycle_complete = True txp0, k0 = scan.antenna_pointing(t_pass[I]) if not plot_full_scan: if cycle_complete: continue if lab_done: ax.plot( [txp0[0], txp0[0] + k0[0] * scan_range], [txp0[1], txp0[1] + k0[1] * scan_range], [txp0[2], txp0[2] + k0[2] * scan_range], '-g', alpha=0.2, ) else: ax.plot( [txp0[0], txp0[0] + k0[0] * scan_range], [txp0[1], txp0[1] + k0[1] * scan_range], [txp0[2], txp0[2] + k0[2] * scan_range], '-g', alpha=0.01, label='Scan', ) lab_done = True max_range = 1500e3 ax.set_xlim(txs[0].ecef[0] - max_range, txs[0].ecef[0] + max_range) ax.set_ylim(txs[0].ecef[1] - max_range, txs[0].ecef[1] + max_range) ax.set_zlim(txs[0].ecef[2] - max_range, txs[0].ecef[2] + max_range) plt.legend() plt.show()