def plot_radar_earth(ax, radar): for tx in radar._tx: ecef = coord.geodetic2ecef(tx.lat, tx.lon, tx.alt) ax.plot([ecef[0]], [ecef[1]], [ecef[2]], "x", color='r', label=tx.name) for rx in radar._rx: ecef = coord.geodetic2ecef(rx.lat, rx.lon, rx.alt) ax.plot([ecef[0]], [ecef[1]], [ecef[2]], "x", color='b', label=rx.name)
def test_ecef_geo_inverse(self): dec = 3 y = n.array((90.0, 0.0, 0.0)) x = coord.geodetic2ecef(y[0], y[1], y[2]) y_ref = coord.ecef2geodetic(x[0], x[1], x[2]) nt.assert_array_almost_equal(y, y_ref, decimal=dec) y = n.array((-90.0, 0.0, 0.0)) x = coord.geodetic2ecef(y[0], y[1], y[2]) y_ref = coord.ecef2geodetic(x[0], x[1], x[2]) nt.assert_array_almost_equal(y, y_ref, decimal=dec) y = n.array((0.0, 0.0, 0.0)) x = coord.geodetic2ecef(y[0], y[1], y[2]) y_ref = coord.ecef2geodetic(x[0], x[1], x[2]) nt.assert_array_almost_equal(y, y_ref, decimal=dec) y = n.array((0.0, 90.0, 0.0)) x = coord.geodetic2ecef(y[0], y[1], y[2]) y_ref = coord.ecef2geodetic(x[0], x[1], x[2]) nt.assert_array_almost_equal(y, y_ref, decimal=dec) y = n.array((90.0, 0.0, 100.0)) x = coord.geodetic2ecef(y[0], y[1], y[2]) y_ref = coord.ecef2geodetic(x[0], x[1], x[2]) nt.assert_array_almost_equal(y, y_ref, decimal=dec)
def test_geodetic2ecef(self): dec = 3 x = coord.geodetic2ecef(90.0, 0.0, 0.0) nt.assert_almost_equal(x[2], coord.b, decimal=dec) x = coord.geodetic2ecef(-90.0, 0.0, 0.0) nt.assert_almost_equal(x[2], -coord.b, decimal=dec) x = coord.geodetic2ecef(0.0, 0.0, 0.0) nt.assert_almost_equal(x[0], coord.a, decimal=dec) x = coord.geodetic2ecef(0.0, 90.0, 0.0) nt.assert_almost_equal(x[1], coord.a, decimal=dec) x = coord.geodetic2ecef(90.0, 0.0, 100.) nt.assert_almost_equal(x[2], coord.b + 100., decimal=dec)
def __init__(self, name, lat, lon, alt, el_thresh, freq, rx_noise, beam, scan=None, phased=True): self.name = name self.lat = lat self.lon = lon self.alt = alt self.el_thresh = el_thresh self.rx_noise = rx_noise self.beam = beam self.freq = freq self.phased = phased self.wavelength = c.c / freq self.ecef = coord.geodetic2ecef(lat, lon, alt) self.scan = scan self.extra_scans = None self.scan_controler = None
def antenna_pointing(self, t): '''Returns the instantaneous WGS84 ECEF pointing direction and the radar geographical location in WGS84 ECEF coordinates. :param float t: Seconds past a reference epoch to retrieve the pointing at. ''' p0 = coord.geodetic2ecef(self._lat, self._lon, self._alt) point = self._pointing(t) if self._pointing_coord == 'ned': k0 = coord.ned2ecef(self._lat, self._lon, self._alt, point[0], point[1], point[2]) elif self._pointing_coord == 'enu': k0 = coord.enu2ecef(self._lat, self._lon, self._alt, point[0], point[1], point[2]) elif self._pointing_coord == 'azel': k0 = coord.azel_ecef(self._lat, self._lon, self._alt, point[0], point[1]) return p0, k0
def ray_trace(dn=datetime(2016, 6, 21, 12, 00), f=233e6, lat=e3d._tx[0].lat, lon=e3d._tx[0].lon, elevation=30.0, az=180.0, fpref="", plot=False): np = 1000 alts = n.linspace(0, 4000, num=np) distance = n.linspace(0, 4000, num=np) ne = n.zeros(np) ne2 = n.zeros(np) dnex = n.zeros(np) dtheta = n.zeros(np) dalt = n.zeros(np) dney = n.zeros(np) dnez = n.zeros(np) xyz_prev = 0.0 px = n.zeros(np) dk = n.zeros(np) py = n.zeros(np) pz = n.zeros(np) p0x = n.zeros(np) p0y = n.zeros(np) p0z = n.zeros(np) # initial direction and position k = coord.azel_ecef(lat, lon, 10e3, az, elevation) k0 = k p = coord.geodetic2ecef(lat, lon, 10e3) pe = coord.geodetic2ecef(lat, lon, 10e3) p0 = coord.geodetic2ecef(lat, lon, 10e3) dh = 4e3 vg = 1.0 p_orig = p ray_time = 0.0 for ai, a in enumerate(alts): p = p + k * dh * vg p0 = p0 + k0 * dh ray_time += dh / c.c dpx = p + n.array([1.0, 0.0, 0.0]) * dh dpy = p + n.array([0.0, 1.0, 0.0]) * dh dpz = p + n.array([0.0, 0.0, 1.0]) * dh llh = coord.ecef2geodetic(p[0], p[1], p[2]) llh_1 = coord.ecef2geodetic(p0[0], p0[1], p0[2]) dalt[ai] = llh_1[2] - llh[2] if llh[2] / 1e3 > 1900: break alts[ai] = llh[2] / 1e3 pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: ne[ai] = pt.ne * 1e6 f_p = 8.98 * n.sqrt(ne[ai]) v_g = n.sqrt(1.0 - (f_p / f)**2.0) else: ne[ai] = 0.0 llh = coord.ecef2geodetic(dpx[0], dpx[1], dpx[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dnex[ai] = (ne[ai] - pt.ne * 1e6) / dh else: dnex[ai] = 0.0 llh = coord.ecef2geodetic(dpy[0], dpy[1], dpy[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dney[ai] = (ne[ai] - pt.ne * 1e6) / dh else: dney[ai] = 0.0 llh = coord.ecef2geodetic(dpz[0], dpz[1], dpz[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dnez[ai] = (ne[ai] - pt.ne * 1e6) / dh else: dnez[ai] = 0.0 grad = n.array([dnex[ai], dney[ai], dnez[ai]]) px[ai] = p[0] py[ai] = p[1] pz[ai] = p[2] p0x[ai] = p0[0] p0y[ai] = p0[1] p0z[ai] = p0[2] # print(ai) dk[ai] = n.arccos( n.dot(k0, k) / (n.sqrt(n.dot(k0, k0)) * n.sqrt(n.dot(k, k)))) # no bending if gradient too small if n.dot(grad, grad) > 100.0: grad1 = grad / n.sqrt(n.dot(grad, grad)) p2 = p + k * dh llh = coord.ecef2geodetic(p2[0], p2[1], p2[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: ne2 = pt.ne * 1e6 else: ne2 = 0.0 f0 = 8.98 * n.sqrt(ne[ai]) n0 = n.sqrt(1.0 - (f0 / f)**2.0) f1 = 8.98 * n.sqrt(ne2) n1 = n.sqrt(1.0 - (f1 / f)**2.0) theta0 = n.arccos( n.dot(grad, k) / (n.sqrt(n.dot(grad, grad)) * n.sqrt(n.dot(k, k)))) # angle cannot be over 90 if theta0 > n.pi / 2.0: theta0 = n.pi - theta0 sin_theta_1 = (n0 / n1) * n.sin(theta0) dtheta[ai] = 180.0 * n.arcsin( sin_theta_1) / n.pi - 180.0 * theta0 / n.pi # print("n0/n1 %1.10f theta0 %1.2f theta1-theta0 %1.10f"%(n0/n1,180.0*theta0/n.pi,dtheta[ai])) cos_theta_1 = n.sqrt(1.0 - sin_theta_1**2.0) k_ref = (n0 / n1) * k + ( (n0 / n1) * n.cos(theta0) - cos_theta_1) * grad1 # normalize k_ref / n.sqrt(n.dot(k_ref, k_ref)) k = k_ref angle = n.arccos( n.dot(grad, k) / n.sqrt(n.dot(grad, grad)) * n.sqrt(n.dot(k, k))) los_time = n.sqrt(n.dot(p_orig - p, p_orig - p)) / c.c excess_ionospheric_delay = ray_time - los_time print("Excess propagation time %1.20f mus" % ((1e6 * (ray_time - los_time)))) theta = n.arccos( n.dot(k0, k) / (n.sqrt(n.dot(k0, k0)) * n.sqrt(n.dot(k, k)))) theta_p = n.arccos( n.dot(p0, p) / (n.sqrt(n.dot(p0, p0)) * n.sqrt(n.dot(p, p)))) llh0 = coord.ecef2geodetic(px[ai - 2], py[ai - 2], pz[ai - 2]) llh1 = coord.ecef2geodetic(p0x[ai - 2], p0y[ai - 2], p0z[ai - 2]) print("d_coord") print(llh0 - llh1) if plot: print(p0 - p) print(180.0 * theta_p / n.pi) fig = plt.figure(figsize=(14, 8)) plt.clf() plt.subplot(131) plt.title("Elevation=%1.0f" % (elevation)) plt.plot(n.sqrt((p0x - px)**2.0 + (p0y - py)**2.0 + (p0z - pz)**2.0), alts, label="Total error") plt.plot(dalt, alts, label="Altitude error") plt.ylim([0, 1900]) # plt.xlim([-50,800.0]) plt.grid() plt.legend() plt.xlabel("Position error (m)") plt.ylabel("Altitude km") plt.subplot(132) plt.plot(dtheta * 1e6, alts) # plt.plot(1e6*180.0*dk/n.pi,alts) plt.xlabel("Ray-bending ($\mu$deg/km)") plt.ylabel("Altitude km") plt.title("Total error=%1.2g (deg)" % (180.0 * theta_p / n.pi)) plt.ylim([0, 1900]) plt.subplot(133) plt.plot(ne, alts) plt.xlabel("$N_{\mathrm{e}}$ ($\mathrm{m}^{-3}$)") plt.ylabel("Altitude km") plt.ylim([0, 1900]) # ax.plot(px,py,pz) plt.tight_layout() plt.savefig("ref-%s-%d-%d.png" % (fpref, f / 1e6, elevation)) plt.close() return (p0, p, 180.0 * theta_p / n.pi, excess_ionospheric_delay)
def ray_trace_error(dn=datetime(2016, 6, 21, 12, 00), f=233e6, lat=e3d._tx[0].lat, lon=e3d._tx[0].lon, elevation=30.0, az=180.0, fpref="", ionosphere=False, error_std=0.05, plot=False): np = 2000 alts = n.repeat(1e99, np) distance = n.linspace(0, 4000, num=np) ne = n.zeros(np) ne2 = n.zeros(np) dtheta = n.zeros(np) dalt = n.zeros(np) dnex = n.zeros(np) dney = n.zeros(np) dnez = n.zeros(np) xyz_prev = 0.0 dk = n.zeros(np) px = n.zeros(np) py = n.zeros(np) pz = n.zeros(np) t_vec = n.zeros(np) t_i_vec = n.zeros(np) k_vecs = [] # initial direction and position k = coord.azel_ecef(lat, lon, 10e3, az, elevation) k0 = k p = coord.geodetic2ecef(lat, lon, 10e3) dh = 4e3 dt = 20e-6 # correlated errors std=1, 100 km correlation length scale_length = 40.0 ne_errors_x = n.convolve( n.repeat(1.0 / n.sqrt(scale_length), scale_length), n.random.randn(10000)) ne_errors_y = n.convolve( n.repeat(1.0 / n.sqrt(scale_length), scale_length), n.random.randn(10000)) ne_errors_z = n.convolve( n.repeat(1.0 / n.sqrt(scale_length), scale_length), n.random.randn(10000)) p_orig = p ray_time = 0.0 v_c = c.c for ai, a in enumerate(alts): # go forward in time dhp = v_c * dt p = p + k * dhp ray_time += dt print(ray_time * 1e6) t_vec[ai + 1] = dt k_vecs.append(k) dpx = p + n.array([1.0, 0.0, 0.0]) * dh dpy = p + n.array([0.0, 1.0, 0.0]) * dh dpz = p + n.array([0.0, 0.0, 1.0]) * dh llh = coord.ecef2geodetic(p[0], p[1], p[2]) if llh[2] / 1e3 > 2100: break alts[ai] = llh[2] / 1e3 pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: ne[ai] = pt.ne * (1.0 + error_std * ne_errors_x[ai]) * 1e6 if ionosphere: f0 = 8.98 * n.sqrt(ne[ai]) f_p = 8.98 * n.sqrt(ne[ai]) # update group velocity v_c = c.c * n.sqrt(1.0 - (f0 / f)**2.0) else: ne[ai] = 0.0 llh = coord.ecef2geodetic(dpx[0], dpx[1], dpx[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dnex[ai] = (ne[ai] - pt.ne * (1.0 + error_std * ne_errors_x[ai]) * 1e6) / dh else: dnex[ai] = 0.0 llh = coord.ecef2geodetic(dpy[0], dpy[1], dpy[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dney[ai] = (ne[ai] - pt.ne * (1.0 + error_std * ne_errors_x[ai]) * 1e6) / dh else: dney[ai] = 0.0 llh = coord.ecef2geodetic(dpz[0], dpz[1], dpz[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: dnez[ai] = (ne[ai] - pt.ne * (1.0 + error_std * ne_errors_x[ai]) * 1e6) / dh else: dnez[ai] = 0.0 grad = n.array([dnex[ai], dney[ai], dnez[ai]]) px[ai] = p[0] py[ai] = p[1] pz[ai] = p[2] dk[ai] = n.arccos( n.dot(k0, k) / (n.sqrt(n.dot(k0, k0)) * n.sqrt(n.dot(k, k)))) # no bending if gradient too small if n.dot(grad, grad) > 100.0 and ionosphere: grad1 = grad / n.sqrt(n.dot(grad, grad)) p2 = p + k * dh llh = coord.ecef2geodetic(p2[0], p2[1], p2[2]) pt = Point(dn, llh[0], llh[1], llh[2] / 1e3) pt.run_iri() if pt.ne > 0.0: ne2 = pt.ne * (1.0 + error_std * ne_errors_x[ai]) * 1e6 else: ne2 = 0.0 f0 = 8.98 * n.sqrt(ne[ai]) n0 = n.sqrt(1.0 - (f0 / f)**2.0) f1 = 8.98 * n.sqrt(ne2) n1 = n.sqrt(1.0 - (f1 / f)**2.0) theta0 = n.arccos( n.dot(grad, k) / (n.sqrt(n.dot(grad, grad)) * n.sqrt(n.dot(k, k)))) # angle cannot be over 90 if theta0 > n.pi / 2.0: theta0 = n.pi - theta0 sin_theta_1 = (n0 / n1) * n.sin(theta0) dtheta[ai] = 180.0 * n.arcsin( sin_theta_1) / n.pi - 180.0 * theta0 / n.pi # print("n0/n1 %1.10f theta0 %1.2f theta1-theta0 %1.10f"%(n0/n1,180.0*theta0/n.pi,dtheta[ai])) cos_theta_1 = n.sqrt(1.0 - sin_theta_1**2.0) k_ref = (n0 / n1) * k + ( (n0 / n1) * n.cos(theta0) - cos_theta_1) * grad1 # normalize k_ref / n.sqrt(n.dot(k_ref, k_ref)) k = k_ref angle = n.arccos( n.dot(grad, k) / n.sqrt(n.dot(grad, grad)) * n.sqrt(n.dot(k, k))) return (t_vec, px, py, pz, alts, ne, k_vecs)
def __init__(self,N=1000,R_e=6378e3,h_e=100e3,plot=False): self.N=N self.R_e=R_e # get uniformly distributed points on a sphere # these will act ast grid points points=fibonacci_sphere(samples=N)*(R_e+h_e) # triangulate before shifting points onto a geoid tri = Delaunay(points) # shift points to fit geoid if True: points2=n.copy(points) llhs=[] for p in range(points.shape[0]): if p == 0: llh=[0,0,0] points[p,:]=n.array([0,0,0]) else: llh=coord.ecef2geodetic(points[p,0],points[p,1],points[p,2]) points2[p,:]=coord.geodetic2ecef(llh[0],llh[1],h_e) llhs.append(llh) llhs=n.array(llhs) points=points2 # triangulate the grid, so that we know # the neighbouring points # the list "tri" contains the wire mesh # TBD: make sure that there are no duplicated wires! s=n.copy(tri.simplices) # these are the triangles tris = [] # these are unique current carrying elements of the mesh wires={} wire_num=0 # these are the connections between node points, in order to allow us to regularize current continuity. self.connections={} for i in range(s.shape[0]): pidx=s[i,:] if 0 in pidx: pidx=n.setdiff1d(pidx,[0]) tri={} tri["edges"]=[[pidx[0],pidx[1]],[pidx[0],pidx[2]],[pidx[1],pidx[2]]] id_pairs=[[0,1],[0,2],[1,2]] wire_ids=[] for id_pair in id_pairs: e0=n.min([pidx[id_pair[0]],pidx[id_pair[1]]]) e1=n.max([pidx[id_pair[0]],pidx[id_pair[1]]]) self.add_connection(e0,e1) wire_id = "%d-%d"%(e0,e1) wire_ids.append(wire_id) if wire_id not in wires.keys(): del_l=points[e0,:]-points[e1,:] r=points[e0,:]+0.5*del_l llh=coord.ecef2geodetic(r[0],r[1],r[2]) east=coord.enu2ecef(llh[0],llh[1],0.0,1.0,0,0) north=coord.enu2ecef(llh[0],llh[1],0.0,0,1.0,0) wires[wire_id]={"del_l":del_l, "del_e":n.dot(del_l/n.linalg.norm(del_l),east), "del_n":n.dot(del_l/n.linalg.norm(del_l),north), "r":r, "llh":llh, "wire_num":wire_num, "e0":e0, "e1":e1} wire_num+=1 else: pass tri["wire_ids"]=wire_ids tris.append(tri) else: print("middle point not in tri!") self.llhs=llhs self.tris=tris self.points=points self.wires=wires
def create_theory_matrix(wg,meas,h_e=100e3,R_e=6378e3,alpha=1e-11,tik=1e-13): """ Given measurements, determine the current pattern at h_e km """ n_stat=len(meas["glon"]) n_meas=3*n_stat n_par=len(wg.wires) print("n_meas %d n_par %d"%(n_meas,n_par)) # how many points can we regularize using current continuity n_cur_reg=wg.points.shape[0]-1 n_tik=n_par n_reg=n_cur_reg#+n_tik # theory matrix A=n.zeros([n_meas+n_reg,n_par]) # measurements m=n.zeros(n_meas+n_reg) wire_ids=wg.wires.keys() const=sc.mu_0/4.0/n.pi for mi in range(n_stat): p=coord.geodetic2ecef(meas["glat"][mi],meas["glon"][mi],0) # convert the n,e,u meas to ecef in Tesla E N U B_meas=coord.enu2ecef(meas["glat"][mi],meas["glon"][mi],0,meas["neu"][mi,1],meas["neu"][mi,0],meas["neu"][mi,2])*1e-9 up=coord.enu2ecef(meas["glat"][mi],meas["glon"][mi],0,0,0,1) for li in range(n_par): k=wire_ids[li] # for each wire, get the Biot-Savart law contribution # is ionosphere above the horizon? rp = wg.wires[k]["r"]-p rpn=n.linalg.norm(rp) rp0 = rp/rpn zen_angle=180.0*n.arccos(n.dot(rp0,up))/n.pi if zen_angle < 90.0: # print("station lat %1.2f lon %1.2f grid lat %1.2f lon %1.2f"%(meas["glat"][mi], # meas["glon"][mi], # wg.wires[k]["llh"][0], # wg.wires[k]["llh"][1])) # biot-savart law cp=const*n.cross(wg.wires[k]["del_l"],rp)/rpn**3.0 wire_idx=wg.wires[k]["wire_num"] # x A[mi*3+0,wire_idx]=cp[0] # y A[mi*3+1,wire_idx]=cp[1] # z A[mi*3+2,wire_idx]=cp[2] # measurement m[mi*3+0]=B_meas[0] m[mi*3+1]=B_meas[1] m[mi*3+2]=B_meas[2] # current continuity nodes=wg.connections.keys() for ri in range(n_cur_reg): e0=nodes[ri] conns=wg.connections[e0] for e1 in conns: wire_idx=wg.get_wire_idx(e0,e1) # if index is reversed, current negative if e1 > e0: A[ri+n_stat*3,wire_idx]=alpha else: A[ri+n_stat*3,wire_idx]=-alpha # for ri in range(n_tik): # A[ri+n_stat*3+n_cur_reg,ri]=tik return(A,m) print("n_meas %d n_reg %d n_par %d"%(n_meas,n_reg,n_par))
def create_theory_matrix(meas, h_e=100e3, R_e=6378e3, min_lat=60, max_lat=80, min_lon=3, max_lon=30, n_lat_wires=40, n_seg=50): """ Given measurements, determine the current pattern at h_e km """ lats = n.linspace(min_lat, max_lat, num=n_lat_wires + 1) lons_seg = n.linspace(min_lon, max_lon, num=n_seg + 1) wlens = n.zeros([n_lat_wires, n_seg]) rl = [] dls = [] dAs = [] for wi in range(n_lat_wires): r = [] dl = [] dA = [] for si in range(n_seg): l0 = coord.geodetic2ecef(lats[wi], lons_seg[si], h_e) l0u = coord.geodetic2ecef(lats[wi + 1], lons_seg[si], h_e) l1 = coord.geodetic2ecef(lats[wi], lons_seg[si + 1], h_e) l1u = coord.geodetic2ecef(lats[wi + 1], lons_seg[si + 1], h_e) # area in this pixel A = n.linalg.norm(l0u - l0) * n.linalg.norm(l1u - l1) del_l = (l1 - l0) # unit vector del_l = del_l / n.linalg.norm(del_l) dl.append(del_l) r.append(0.5 * (l0 + l1)) dA.append(A) rl.append(r) dls.append(dl) dAs.append(dA) lats = lats[0:(len(lats) - 1)] n_stat = len(meas["glon"]) n_meas = 3 * n_stat n_par = n_lat_wires print("n_meas %d n_par %d" % (n_meas, n_par)) # theory matrix # A=n.zeros([n_meas,n_par]) # measurements # m=n.zeros(n_meas) const = sc.mu_0 / 4.0 / n.pi A = {} for mi in range(n_stat): p = coord.geodetic2ecef(meas["glat"][mi], meas["glon"][mi], 0) up = coord.enu2ecef(meas["glat"][mi], meas["glon"][mi], 0, 0, 0, 1) Ax = n.zeros(n_lat_wires) Ay = n.zeros(n_lat_wires) Az = n.zeros(n_lat_wires) for li in range(n_lat_wires): for si in range(n_seg): # for each wire segment, get the Biot-Savart law contribution # is ionosphere above the horizon? rp = rl[li][si] - p rpn = n.linalg.norm(rp) rp0 = rp / rpn zen_angle = 180.0 * n.arccos(n.dot(rp0, up)) / n.pi if zen_angle < 85.0: print( "station %s lat %1.2f lon %1.2f grid lat %1.2f lon %1.2f" % (meas["stat"][mi], meas["glat"][mi], meas["glon"][mi], lats[li], lons_seg[si])) # biot-savart law (current density per unit surface area) cp = dAs[li][si] * const * n.cross(dls[li][si], rp) / rpn**3.0 # x Ax[li] += cp[0] # y Ay[li] += cp[1] # z Az[li] += cp[2] if n.sum(Ax) != 0: stat = meas["stat"][mi] A[stat] = {"x": Ax, "y": Ay, "z": Az} n_stations = len(A.keys()) print("n_stat %d" % (n_stations)) return (A, lats, lons_seg)
def draw_radar(ax, lat, lon, name="radar", color="black"): n_earth = 1000 earth = coord.geodetic2ecef(lat, lon, 0.0) # print(earth) ax.plot([earth[0]], [earth[1]], [earth[2]], "x", color=color, label=name)
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)