def angle_k(self, k): '''Get angle between azimuth and elevation and pointing direction. :param numpy.array k: Direction to evaluate angle to. :return: Angle in degrees. :rtype: float ''' return coord.angle_deg(self.on_axis, k)
def uhf_meas(k_in, beam): '''Measured UHF beam pattern ''' theta = coord.angle_deg(beam.on_axis, k_in) # scale beam width by frequency sf = beam.f / 930e6 return (beam.I_0 * beam.gf(sf * np.abs(theta)))
def test_angle_deg(self): x = n.array([1, 0, 0]) y = n.array([1, 1, 0]) theta = coord.angle_deg(x, y) self.assertAlmostEqual(theta, 45.0) y = n.array([0, 1, 0]) theta = coord.angle_deg(x, y) self.assertAlmostEqual(theta, 90.0) theta = coord.angle_deg(x, x) self.assertAlmostEqual(theta, 0.0) theta = coord.angle_deg(x, -x) self.assertAlmostEqual(theta, 180.0) X = n.array([0.11300039, -0.85537661, 0.50553118]) theta = coord.angle_deg(X, X) self.assertAlmostEqual(theta, 0.0)
def elliptic_airy(k_in, beam): '''# TODO: Descriptive doc string. a = radius f = frequency I_0 = gain at center ''' phi = np.pi * coord.angle_deg(beam.on_axis, beam.plane_normal) / 180.0 #F[ g(ct) ] = G(f/c)/|c| theta = np.pi * coord.angle_deg(beam.on_axis, k_in) / 180.0 lam = c.c / beam.f k = 2.0 * np.pi / lam f_circ = lambda r, phi: beam.I_0 * ( (2.0 * s.jn(1, k * beam.a * np.sin(theta)) / (k * beam.a * np.sin(theta))))**2.0 return ()
def angle(self, az, el): '''Get angle between azimuth and elevation and pointing direction. :param float az: Azimuth in dgreees east of north to measure from. :param float el: Elevation in degrees from horizon to measure from. :return: Angle in degrees. :rtype: float ''' direction = coord.azel_to_cart(az, el, 1.0) return coord.angle_deg(self.on_axis, direction)
def test_gain(self): x_vecs = n.random.rand(3, 1000) * 2.0 - 1 for ind in range(1000): self.assertAlmostEqual(self.beam_u.gain(x_vecs[:, ind]), 1.0) self.assertAlmostEqual(self.beam_quad.gain( x_vecs[:, ind]), (coord.angle_deg( x_vecs[:, ind], n.array([0, 0, 1], dtype=n.float)) / 180.0)**2) self.assertLess(self.beam_quad.gain(x_vecs[:, ind]), self.beam_quad.I_0)
def airy(k_in, beam): '''# TODO: Descriptive doc string. a = radius f = frequency I_0 = gain at center ''' theta = np.pi * coord.angle_deg(beam.on_axis, k_in) / 180.0 lam = c.c / beam.f k = 2.0 * np.pi / lam return (beam.I_0 * ((2.0 * s.jn(1, k * beam.a * np.sin(theta)) / (k * beam.a * np.sin(theta))))**2.0)
def planar(k_in, beam): '''Gaussian tapered planar array ''' if np.abs(1 - np.dot(beam.on_axis, beam.plane_normal)) < 1e-6: rd = np.random.randn(3) rd = rd / np.sqrt(np.dot(rd, rd)) ct = np.cross(beam.on_axis, rd) else: ct = np.cross(beam.on_axis, beam.plane_normal) ct = ct / np.sqrt(np.dot(ct, ct)) ht = np.cross(beam.plane_normal, ct) ht = ht / np.sqrt(np.dot(ht, ht)) angle = coord.angle_deg(beam.on_axis, ht) ot = np.cross(beam.on_axis, ct) ot = ot / np.sqrt(np.dot(ot, ot)) beam.I_1 = np.sin(np.pi * angle / 180.0) * beam.I_0 beam.a0p = np.sin(np.pi * angle / 180.0) * beam.a0 beam.ct = ct beam.ht = ht beam.ot = ot beam.angle = angle beam.sigma1 = 0.7 * beam.a0p / beam.lam beam.sigma2 = 0.7 * beam.a0 / beam.lam k0 = k_in / np.sqrt(np.dot(k_in, k_in)) A = np.dot(k0, beam.on_axis) kda = A * beam.on_axis l1 = np.dot(k0, beam.ct) kdc = l1 * beam.ct m1 = np.dot(k0, beam.ot) kdo = m1 * beam.ot l2 = l1 * l1 m2 = m1 * m1 return beam.I_1 * np.exp( -np.pi * m2 * 2.0 * np.pi * beam.sigma1**2.0) * np.exp( -np.pi * l2 * 2.0 * np.pi * beam.sigma2**2.0)
def cassegrain(k_in, beam): '''# TODO: Descriptive doc string. A better model of the EISCAT UHF antenna ''' theta = np.pi * coord.angle_deg(beam.on_axis, k_in) / 180.0 lam = c.c / beam.f k = 2.0 * np.pi / lam A = (beam.I_0 * ((lam / (np.pi * np.sin(theta)))**2.0)) / ( (beam.a0**2.0 - beam.a1**2.0)**2.0) B = (beam.a0 * s.jn(1, beam.a0 * np.pi * np.sin(theta) / lam) - beam.a1 * s.jn(1, beam.a1 * np.pi * np.sin(theta) / lam))**2.0 A0 = (beam.I_0 * ((lam / (np.pi * np.sin(1e-6)))**2.0)) / ( (beam.a0**2.0 - beam.a1**2.0)**2.0) B0 = (beam.a0 * s.jn(1, beam.a0 * np.pi * np.sin(1e-6) / lam) - beam.a1 * s.jn(1, beam.a1 * np.pi * np.sin(1e-6) / lam))**2.0 const = beam.I_0 / (A0 * B0) return (A * B * const)
for tracklet in tracklets: s_obj = sim.population.get_object(tracklet['index']) states = s_obj.get_state(tracklet['t']) num = len(tracklet['t']) rel_pos = states[:3,:] rel_vel = states[3:,:] range_v = np.empty((num,), dtype=np.float64) velocity_v = np.empty((num,), dtype=np.float64) angle_v = np.empty((num,), dtype=np.float64) _, k0 = radar._tx[0].scan.antenna_pointing(0.0) for ind in range(num): rel_pos[:,ind] -= station angle_v[ind] = coord.angle_deg(rel_pos[:,ind], k0) range_v[ind] = np.linalg.norm(rel_pos[:,ind]) velocity_v[ind] = -np.dot(rel_pos[:,ind],rel_vel[:,ind])/range_v[ind] min_ind = np.argmin(angle_v) style = 'xb' alpha = 0.3 axall[0].plot(tracklet['t']/3600.0, range_v*1e-3, '-b', alpha=0.5, ) axall[1].plot(tracklet['t']/3600.0, velocity_v*1e-3, '-b', alpha=0.5,
def elliptic(k_in, beam): '''# TODO: Description. TDB: sqrt(u**2 + c**2 v**2) http://www.iue.tuwien.ac.at/phd/minixhofer/node59.html https://en.wikipedia.org/wiki/Fraunhofer_diffraction_equation x=n.linspace(-2,2,num=1024) xx,yy=n.meshgrid(x,x) A=n.zeros([1024,1024]) A[xx**2.0/0.25**2 + yy**2.0/0.0625**2.0 < 1.0]=1.0 plt.pcolormesh(10.0*n.log10(n.fft.fftshift(n.abs(B)))) plt.colorbar() plt.axis("equal") plt.show() Variable substitution ''' if np.abs(1 - np.dot(beam.on_axis, beam.plane_normal)) < 1e-6: rd = np.random.randn(3) rd = rd / np.sqrt(np.dot(rd, rd)) ct = np.cross(beam.on_axis, rd) else: ct = np.cross(beam.on_axis, beam.plane_normal) ct = ct / np.sqrt(np.dot(ct, ct)) ht = np.cross(beam.plane_normal, ct) ht = ht / np.sqrt(np.dot(ht, ht)) angle = coord.angle_deg(beam.on_axis, ht) ot = np.cross(beam.on_axis, ct) ot = ot / np.sqrt(np.dot(ot, ot)) beam.I_1 = np.sin(np.pi * angle / 180.0) * beam.I_0 beam.a0p = np.sin(np.pi * angle / 180.0) * beam.a0 beam.ct = ct beam.ht = ht beam.ot = ot beam.angle = angle beam.sigma1 = 0.7 * beam.a0p / beam.lam beam.sigma2 = 0.7 * beam.a0 / beam.lam k0 = k_in / np.sqrt(np.dot(k_in, k_in)) A = np.dot(k0, beam.on_axis) kda = A * beam.on_axis l1 = np.dot(k0, beam.ct) kdc = l1 * beam.ct m1 = np.dot(k0, beam.ot) kdo = m1 * beam.ot l2 = l1 * l1 m2 = m1 * m1 return beam.I_1 * np.exp( -np.pi * m2 * 2.0 * np.pi * beam.sigma1**2.0) * np.exp( -np.pi * l2 * 2.0 * np.pi * beam.sigma2**2.0)
def create_tracklet(o, radar, t_obs, hdf5_out=True, ccsds_out=True, dname="./tracklets", noise=False, dx=10.0, dv=10.0, dt=0.01, ignore_elevation_thresh=False): '''Simulate tracks of objects. ionospheric limit is a lower limit on precision after ionospheric corrections ''' if noise: noise = 1.0 else: noise = 0.0 # TDB, kludge, this should be allowed to change as a function of time bw = radar._tx[0].tx_bandwidth txlen = radar._tx[0].pulse_length * 1e6 # pulse length in microseconds ipp = radar._tx[0].ipp # pulse length in microseconds n_ipp = int(radar._tx[0].n_ipp) # pulse length in microseconds rfun, dopfun = debris.precalculate_dr(txlen, bw, ipp=ipp, n_ipp=n_ipp) t0_unix = dpt.jd_to_unix(dpt.mjd_to_jd(o.mjd0)) if debug_low: for tx in radar._tx: print("TX %s" % (tx.name)) for rx in radar._tx: print("RX %s" % (rx.name)) rx_p = [] tx_p = [] for tx in radar._tx: tx_p.append(coord.geodetic2ecef(tx.lat, tx.lon, tx.alt)) for rxi, rx in enumerate(radar._rx): rx_p.append(coord.geodetic2ecef(rx.lat, rx.lon, rx.alt)) ecefs = o.get_orbit(t_obs) state = o.get_state(t_obs) ecefs_p_dt = o.get_orbit(t_obs + dt) ecefs_m_dt = o.get_orbit(t_obs - dt) # velocity in ecef ecef_vel = (0.5 * ((ecefs_p_dt - ecefs) + (ecefs - ecefs_m_dt)) / dt) # linearized error estimates for ecef state vector error std_dev, when three or more # delays and doppl er shifts are measured ecef_stdevs = [] meas = [] for tx in radar._tx: ecef_stdevs.append({"time_idx": [], "m_time": [], "ecef_stdev": []}) for tx in radar._tx: m_rx = [] for rx in radar._rx: m_rx.append({ "time_idx": [], "m_time": [], "m_delay": [], "m_delay_std": [], "m_range": [], "m_range_std": [], "m_range_rate": [], "m_range_rate_std": [], "m_doppler": [], "m_doppler_std": [], "gain_tx": [], "gain_rx": [], "enr": [], "true_state": [], "true_time": [], "g_lat": [], "g_lon": [] }) meas.append(m_rx) # largest possible number of state vector measurements n_state_meas = len(radar._tx) * len(radar._rx) # jacobian for error covariance calc J = n.zeros([2 * n_state_meas, 6]) Sigma_Lin = n.zeros([2 * n_state_meas, 2 * n_state_meas]) # error standard deviation for state vector estimates at each position state_vector_errors = n.zeros([6, len(t_obs)]) # go through all times for ti, t in enumerate(t_obs): p = n.array([ecefs[0, ti], ecefs[1, ti], ecefs[2, ti]]) p_p = n.array( [ecefs_p_dt[0, ti], ecefs_p_dt[1, ti], ecefs_p_dt[2, ti]]) p_m = n.array( [ecefs_m_dt[0, ti], ecefs_m_dt[1, ti], ecefs_m_dt[2, ti]]) # for linearized state vector error determination p_dx0 = n.array([ecefs[0, ti] + dx, ecefs[1, ti], ecefs[2, ti]]) p_dx1 = n.array([ecefs[0, ti], ecefs[1, ti] + dx, ecefs[2, ti]]) p_dx2 = n.array([ecefs[0, ti], ecefs[1, ti], ecefs[2, ti] + dx]) # doppler error comes from linear least squares # initialize jacobian J[:, :] = 0.0 Sigma_Lin[:, :] = 0.0 state_meas_idx = 0 # go through all transmitters for txi, tx in enumerate(radar._tx): pos_vec_tx = -tx_p[txi] + p pos_vec_tx_p = -tx_p[txi] + p_p pos_vec_tx_m = -tx_p[txi] + p_m # for linearized errors pos_vec_tx_dx0 = -tx_p[txi] + p_dx0 pos_vec_tx_dx1 = -tx_p[txi] + p_dx1 pos_vec_tx_dx2 = -tx_p[txi] + p_dx2 # incident k-vector k_inc = -2.0 * n.pi * pos_vec_tx / n.linalg.norm( pos_vec_tx) / tx.wavelength elevation_tx = 90.0 - coord.angle_deg(tx_p[txi], pos_vec_tx) if elevation_tx > tx.el_thresh or ignore_elevation_thresh: k0 = tx.point_ecef( pos_vec_tx) # we are pointing at the object when tracking gain_tx = tx.beam.gain(k0) # get antenna gain range_tx = n.linalg.norm(pos_vec_tx) range_tx_p = n.linalg.norm(pos_vec_tx_p) range_tx_m = n.linalg.norm(pos_vec_tx_m) range_tx_dx0 = n.linalg.norm(pos_vec_tx_dx0) range_tx_dx1 = n.linalg.norm(pos_vec_tx_dx1) range_tx_dx2 = n.linalg.norm(pos_vec_tx_dx2) tx_to_target_time = range_tx / c.c # go through all receivers for rxi, rx in enumerate(radar._rx): pos_vec_rx = -rx_p[rxi] + p pos_vec_rx_p = -rx_p[rxi] + p_p pos_vec_rx_m = -rx_p[rxi] + p_m # rx k-vector k_rec = 2.0 * n.pi * pos_vec_rx / n.linalg.norm( pos_vec_rx) / tx.wavelength # scattered k-vector k_scat = k_rec - k_inc # for linearized pos error pos_vec_rx_dx0 = -rx_p[rxi] + p_dx0 pos_vec_rx_dx1 = -rx_p[rxi] + p_dx1 pos_vec_rx_dx2 = -rx_p[rxi] + p_dx2 elevation_rx = 90.0 - coord.angle_deg( rx_p[rxi], pos_vec_rx) if elevation_rx > rx.el_thresh or ignore_elevation_thresh: k0 = rx.point_ecef( pos_vec_rx ) # we are pointing at the object when tracking gain_rx = rx.beam.gain(k0) # get antenna gain range_rx = n.linalg.norm(pos_vec_rx) range_rx_p = n.linalg.norm(pos_vec_rx_p) range_rx_m = n.linalg.norm(pos_vec_rx_m) range_rx_dx0 = n.linalg.norm(pos_vec_rx_dx0) range_rx_dx1 = n.linalg.norm(pos_vec_rx_dx1) range_rx_dx2 = n.linalg.norm(pos_vec_rx_dx2) target_to_rx_time = range_rx / c.c # SNR of object at measured location enr_rx = debris.hard_target_enr( gain_tx, gain_rx, tx.wavelength, tx.tx_power, range_tx, range_rx, o.diam, bandwidth=tx. coh_int_bandwidth, # coherent integration bw rx_noise_temp=rx.rx_noise) if enr_rx > 1e8: enr_rx = 1e8 if enr_rx < 0.1: enr_rx = 0.1 #print("snr %1.2f"%(enr_rx)) dr = 10.0**(rfun(n.log10(enr_rx))) ddop = 10.0**(dopfun(n.log10(enr_rx))) # Unknown doppler shift due to ionosphere can be up to 0.1 Hz, # estimate based on typical GNU Ionospheric tomography receiver phase curves. if ddop < 0.1: ddop = 0.1 dr = n.sqrt(dr**2.0 + iono_errfun(range_tx / 1e3)** 2.0) # add ionospheric error if dr < o.diam: # if object diameter is larger than range error, make it at least as big as target dr = o.diam r0 = range_tx + range_rx rp = range_tx_p + range_rx_p rm = range_tx_m + range_rx_m range_rate_d = 0.5 * ( (rp - r0) + (r0 - rm)) / dt # symmetric numerical derivative # doppler (m/s) using scattering k-vector range_rate = n.dot( pos_vec_rx / range_rx, state[3:6, ti]) + n.dot( pos_vec_tx / range_tx, state[3:6, ti]) doppler = range_rate / tx.wavelength # print("rr1 %1.1f rr2 %1.1f"%(range_rate_d,range_rate)) doppler_k = n.dot(k_scat, ecef_vel[:, ti]) / 2.0 / n.pi range_rate_k = doppler_k * tx.wavelength # for linearized errors, range rate at small perturbations to state vector velocity parameters range_rate_k_dv0 = tx.wavelength * n.dot( k_scat, ecef_vel[:, ti] + n.array([dv, 0, 0])) / 2.0 / n.pi range_rate_k_dv1 = tx.wavelength * n.dot( k_scat, ecef_vel[:, ti] + n.array([0, dv, 0])) / 2.0 / n.pi range_rate_k_dv2 = tx.wavelength * n.dot( k_scat, ecef_vel[:, ti] + n.array([0, 0, dv])) / 2.0 / n.pi # full range for error calculation, with small perturbations to position state full_range_dx0 = range_rx_dx0 + range_tx_dx0 full_range_dx1 = range_rx_dx1 + range_tx_dx1 full_range_dx2 = range_rx_dx2 + range_tx_dx2 if enr_rx > tx.enr_thresh: # calculate jacobian row for state vector errors # range J[2 * state_meas_idx, 0:3] = n.array([(full_range_dx0 - r0) / dx, (full_range_dx1 - r0) / dx, (full_range_dx2 - r0) / dx]) # range inverse variance Sigma_Lin[2 * state_meas_idx, 2 * state_meas_idx] = 1.0 / dr**2.0 # range-rate J[2 * state_meas_idx + 1, 3:6] = n.array([ (range_rate_k_dv0 - range_rate_k) / dv, (range_rate_k_dv1 - range_rate_k) / dv, (range_rate_k_dv2 - range_rate_k) / dv ]) # range-rate inverse variance Sigma_Lin[2 * state_meas_idx + 1, 2 * state_meas_idx + 1] = 1.0 / (tx.wavelength * ddop)**2.0 state_meas_idx += 1 # detection! if debug_low: print( "rx %d tx el %1.2f rx el %1.2f gain_tx %1.2f gain_rx %1.2f enr %1.2f rr %1.2f prop time %1.6f dr %1.2f" % (rxi, elevation_tx, elevation_rx, gain_tx, gain_rx, enr_rx, range_rate, tx_to_target_time, dr)) # ground foot point in geodetic llh = coord.ecef2geodetic(p[0], p[1], p[2]) # time is time of transmit pulse meas[txi][rxi]["time_idx"].append(ti) meas[txi][rxi]["m_time"].append(t + t0_unix) meas[txi][rxi]["m_range"].append( (range_tx + range_rx) / 1e3 + noise * n.random.randn() * dr / 1e3) meas[txi][rxi]["m_range_std"].append(dr / 1e3) rr_std = c.c * ddop / radar._tx[ txi].freq / 2.0 / 1e3 meas[txi][rxi]["m_range_rate"].append( range_rate / 1e3 + noise * n.random.randn() * rr_std) # 0.1 m/s error due to ionosphere meas[txi][rxi]["m_range_rate_std"].append(rr_std) meas[txi][rxi]["m_doppler"].append( doppler + noise * n.random.randn() * ddop / 1e3) meas[txi][rxi]["m_doppler_std"].append(ddop) meas[txi][rxi]["m_delay"].append( tx_to_target_time + target_to_rx_time) meas[txi][rxi]["g_lat"].append(llh[0]) meas[txi][rxi]["g_lon"].append(llh[1]) meas[txi][rxi]["enr"].append(enr_rx) meas[txi][rxi]["gain_tx"].append(gain_tx) meas[txi][rxi]["gain_rx"].append(gain_rx) true_state = n.zeros(6) true_state[3:6] = (0.5 * ((p_p - p) + (p - p_m)) / dt) / 1e3 true_state[0:3] = p / 1e3 meas[txi][rxi]["true_state"].append(true_state) meas[txi][rxi]["true_time"].append(t_obs[ti] + t0_unix) else: if debug_high: print("not detected: enr_rx {}".format(enr_rx)) else: if debug_high: print("not detected: elevation_rx {}".format( elevation_rx)) else: if debug_high: print("not detected: elevation_tx {}".format(elevation_tx)) # if more than three measurements of range and range-rate, then we maybe able to # observe true state. if so, calculate the linearized covariance matrix if state_meas_idx > 2: # use only the number of measurements that were good JJ = J[0:(2 * state_meas_idx), :] try: Sigma_post = n.linalg.inv( n.dot(n.dot(n.transpose(JJ), Sigma_Lin), JJ)) ecef_stdevs[txi]["time_idx"].append(ti) ecef_stdevs[txi]["m_time"].append(t) ecef_stdevs[txi]["ecef_stdev"].append(Sigma_post) except: print("Singular posterior covariance...") if debug_low: print(meas) fnames = write_tracklets(o, meas, radar, dname, hdf5_out=hdf5_out, ccsds_out=ccsds_out) return (meas, fnames, ecef_stdevs)
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