Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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)))
Exemplo n.º 3
0
    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)
Exemplo n.º 4
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 ()
Exemplo n.º 5
0
 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)
Exemplo n.º 6
0
    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)
Exemplo n.º 7
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)
Exemplo n.º 8
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)
Exemplo n.º 9
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)
Exemplo n.º 10
0
    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,
Exemplo n.º 11
0
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)
Exemplo n.º 12
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)
Exemplo n.º 13
0
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