def test_geodetic(): xyz = pm.geodetic2ecef(*lla0) assert xyz == approx(xyz0) assert pm.geodetic2ecef(*rlla0, deg=False) == approx(xyz) with pytest.raises(ValueError): pm.geodetic2ecef(lla0[0], lla0[1], -1) with pytest.raises(ValueError): pm.geodetic2ecef(-100, lla0[1], lla0[2]) with pytest.raises(ValueError): pm.geodetic2ecef(lla0[0], -200, lla0[2]) assert pm.ecef2geodetic(*xyz) == approx(lla0) assert pm.ecef2geodetic(*xyz, deg=False) == approx(rlla0) lla2 = pm.aer2geodetic(*aer0, *lla0) rlla2 = pm.aer2geodetic(*raer0, *rlla0, deg=False) with pytest.raises(ValueError): pm.aer2geodetic(aer0[0], aer0[1], -1, *lla0) assert lla2 == approx(lla1) assert rlla2 == approx(rlla1) assert pm.geodetic2aer(*lla2, *lla0) == approx(aer0) assert pm.geodetic2aer(*rlla2, *rlla0, deg=False) == approx(raer0) anan = np.empty((10, 10)) anan.fill(np.nan) assert np.isnan(pm.geodetic2aer(anan, anan, anan, *lla0)).all() assert np.isnan(pm.aer2geodetic(anan, anan, anan, *lla0)).all()
def getIonosphericPiercingPoints(rx_xyz, sv, obstimes, ipp_alt, navfn, cs='wsg84', rx_xyz_coords='xyz', el0=0): """ Sebastijan Mrak Function returns a list of Ionospheric Piersing Point (IPP) trajectory in WSG84 coordinate system (CS). Function takes as parameter a receiver location in ECEF CS, satellite number, times ob observation and desired altitude of a IPP trajectory. You also have to specify a full path to th navigation data file. It returns IPP location in either WSG84 or AER coordinate system. """ ipp_alt = ipp_alt * 1E3 if rx_xyz_coords == 'xyz': rec_lat, rec_lon, rec_alt = ecef2geodetic(rx_xyz[0], rx_xyz[1], rx_xyz[2]) else: if not isinstance(rx_xyz, list): rx_xyz = list(rx_xyz) if len(rx_xyz) == 2: rx_xyz.append(0) assert len(rx_xyz) == 3 rec_lat = rx_xyz[0] rec_lon = rx_xyz[1] rec_alt = rx_xyz[2] rx_xyz = geodetic2ecef(lat = rec_lon, lon = rec_lat, alt = rec_alt) if sv[0] == 'G': if navfn.endswith('n'): xyz = gpsSatPosition(navfn, obstimes, sv=sv, rx_position=rx_xyz, coords='xyz') elif navfn.endswith('sp3'): xyz = gpsSatPositionSP3(navfn, obstimes, sv=sv, rx_position=rx_xyz, coords='xyz') az,el,r = ecef2aer(xyz[0,:],xyz[1,:],xyz[2,:],rec_lat, rec_lon, rec_alt) aer_vector = np.array([az, el, r]) r_new = [] for i in range(len(el)): if el[i] > el0: fm = np.sin(np.radians(el[i])) r_new.append(ipp_alt / fm) else: r_new.append(np.nan) lla_vector = np.array(aer2geodetic(az, el, r_new, rec_lat, rec_lon, rec_alt)) elif sv[0] == 'R': aer_vector = gloSatPosition(navfn=navfn, sv=sv, obstimes=obstimes, rx_position=[rec_lon, rec_lat, rec_alt], cs='aer') fm = np.sin(np.radians(aer_vector[1])) r_new = ipp_alt / fm lla_vector = np.array(aer2geodetic(aer_vector[0], aer_vector[1], r_new, rec_lat, rec_lon, rec_alt)) else: print ('Type in valid sattype initial. "G" for GPS and "R" for GLONASS') if (cs == 'wsg84'): return lla_vector elif (cs == 'aer'): return aer_vector else: print ('Enter either "wsg84" or "aer" as coordinate system. "wsg84" is default one.')
def test_aer(): lla2 = pm.aer2geodetic(*aer0, *lla0) rlla2 = pm.aer2geodetic(*raer0, *rlla0, deg=False) with pytest.raises(ValueError): pm.aer2geodetic(aer0[0], aer0[1], -1, *lla0) assert lla2 == approx(lla1) assert rlla2 == approx(rlla1) assert pm.geodetic2aer(*lla2, *lla0) == approx(aer0) assert pm.geodetic2aer(*rlla2, *rlla0, deg=False) == approx(raer0)
def test_aer(): lla2 = pm.aer2geodetic(*aer0, *lla0) rlla2 = pm.aer2geodetic(*raer0, *rlla0, deg=False) with pytest.raises(ValueError): pm.aer2geodetic(aer0[0], aer0[1], -1, *lla0) assert lla2 == approx(lla1) assert rlla2 == approx(rlla1) assert pm.geodetic2aer(*lla2, *lla0) == approx(aer0) assert pm.geodetic2aer(*rlla2, *rlla0, deg=False) == approx(raer0)
def test_aer_geodetic(aer, lla, lla0): assert pm.aer2geodetic(*aer, *lla0) == approx(lla) raer = (radians(aer[0]), radians(aer[1]), aer[2]) rlla0 = (radians(lla0[0]), radians(lla0[1]), lla0[2]) assert pm.aer2geodetic(*raer, *rlla0, deg=False) == approx((radians(lla[0]), radians(lla[1]), lla[2])) with pytest.raises(ValueError): pm.aer2geodetic(aer[0], aer[1], -1, *lla0) assert pm.geodetic2aer(*lla, *lla0) == approx(aer, rel=1e-3) assert pm.geodetic2aer(radians(lla[0]), radians(lla[1]), lla[2], *rlla0, deg=False) == approx(raer, rel=1e-3)
def test_ned(): xyz = pm.aer2ecef(*aer0, *lla0) enu = pm.aer2enu(*aer0) ned = (enu[1], enu[0], -enu[2]) lla = pm.aer2geodetic(*aer0, *lla0) assert pm.aer2ned(*aer0) == approx(ned0) with pytest.raises(ValueError): pm.aer2ned(aer0[0], aer0[1], -1) assert pm.enu2aer(*enu) == approx(aer0) assert pm.enu2aer(*enu, deg=False) == approx(raer0) assert pm.ned2aer(*ned) == approx(aer0) assert pm.ecef2ned(*xyz, *lla0) == approx(ned) assert pm.ned2ecef(*ned, *lla0) == approx(xyz) # %% assert pm.ecef2enuv(vx, vy, vz, *lla0[:2]) == approx((ve, vn, vu)) assert pm.ecef2nedv(vx, vy, vz, *lla0[:2]) == approx((vn, ve, -vu)) # %% enu3 = pm.geodetic2enu(*lla, *lla0) ned3 = (enu3[1], enu3[0], -enu3[2]) assert pm.geodetic2ned(*lla, *lla0) == approx(ned3) assert pm.enu2geodetic(*enu3, *lla0) == approx(lla) assert pm.ned2geodetic(*ned3, *lla0) == approx(lla)
def test_ned(): xyz = pm.aer2ecef(*aer0, *lla0) enu = pm.aer2enu(*aer0) ned = (enu[1], enu[0], -enu[2]) lla = pm.aer2geodetic(*aer0, *lla0) assert pm.aer2ned(*aer0) == approx(ned0) with pytest.raises(ValueError): pm.aer2ned(aer0[0], aer0[1], -1) assert pm.enu2aer(*enu) == approx(aer0) assert pm.enu2aer(*enu, deg=False) == approx(raer0) assert pm.ned2aer(*ned) == approx(aer0) assert pm.ecef2ned(*xyz, *lla0) == approx(ned) assert pm.ned2ecef(*ned, *lla0) == approx(xyz) # %% assert pm.ecef2enuv(vx, vy, vz, *lla0[:2]) == approx((ve, vn, vu)) assert pm.ecef2nedv(vx, vy, vz, *lla0[:2]) == approx((vn, ve, -vu)) # %% enu3 = pm.geodetic2enu(*lla, *lla0) ned3 = (enu3[1], enu3[0], -enu3[2]) assert pm.geodetic2ned(*lla, *lla0) == approx(ned3) assert pm.enu2geodetic(*enu3, *lla0) == approx(lla) assert pm.ned2geodetic(*ned3, *lla0) == approx(lla)
def asi_projection(dat: xarray.Dataset, projalt_m: float = None, min_el: float = 10.0, ofn: Path = None): """ plots ASI projected to altitude * dat: Xarray containing image stack and metadata (az, el, lat, lon) * projalt_m: projection altitude in meters * min_el: minimum elevation angle (degrees). Data near the horizon is poorly calibrated (large angular error). * ofn: filename to write of plot (optional) """ if projalt_m is None: logging.error("projection altitude must be specified") return if dat["imgs"].shape[0] == 0: return if ofn: ofn = Path(ofn).expanduser() odir = ofn.parent # %% censor pixels near the horizon with large calibration error do to poor skymap fits badpix = dat["el"] < min_el az = dat["az"].values el = dat["el"].values az[badpix] = np.nan el[badpix] = np.nan # %% coordinate transformation, let us know if error occurs slant_range = projalt_m / np.sin(np.radians(el)) lat, lon, alt = pm.aer2geodetic(az, el, slant_range, dat.lat.item(), dat.lon.item(), dat.alt_m.item()) # %% plots fg = figure() ax = fg.gca() hi = pcolormesh_nan(lon, lat, dat["imgs"][0], cmap="gray", axis=ax) # priming ttxt = f"Themis ASI {dat.site} projected to altitude {projalt_m/1e3} km\n" # FOV vs. HST0,HST1: green,red ' ht = ax.set_title(ttxt, color="g") ax.set_xlabel("longitude") ax.set_ylabel("latitude") ax.autoscale(True, tight=True) ax.grid(False) # %% plot narrow FOV outline if "imgs2" in dat: overlayrowcol(ax, dat.rows, dat.cols) # %% play video try: for im in dat["imgs"]: ts = im.time.values.astype(str)[:-6] hi.set_array(im.values.ravel()) # for pcolormesh ht.set_text(ttxt + ts) draw() pause(0.01) if ofn: fn = odir / (ofn.stem + ts + ofn.suffix) print("saving", fn, end="\r") fg.savefig(fn, bbox_inches="tight", facecolor="k") except KeyboardInterrupt: return
def aer2ipp(aer, rxp, H=350): H*=1e3 aer_new = np.copy(aer) fm = np.sin(np.radians(aer[:,:,1])) aer_new[:,:,2] = (H / fm) rxp ipp = np.array(aer2geodetic(aer_new[:,:,0], aer_new[:,:,1], aer_new[:,:,2], rxp[0], rxp[1], rxp[2])) return ipp
def projected_coord(imgs: xarray.Dataset, ind: np.ndarray, lla: Tuple[float, float, float]): az = imgs.az[ind[:, 0], ind[:, 1]].values.squeeze() el = imgs.el[ind[:, 0], ind[:, 1]].values.squeeze() alt_m = lla[2] * 1000 if lla is not None else 100e3 plat, plon, palt_m = pm.aer2geodetic(az, el, alt_m / np.sin(np.radians(el)), imgs.lat, imgs.lon, imgs.alt_m) return az, el, plat, plon, palt_m
def dataFromNC(fnc, fnav, sv, fsp3=None, el_mask=30, tlim=None, satpos=False, ipp=False, ipp_alt=None): leap_seconds = uf.getLeapSeconds(fnav) D = gr.load(fnc, useindicators=True).sel(sv=sv) D['time'] = np.array([np.datetime64(ttt) for ttt in D.time.values]) if tlim is not None: if len(tlim) == 2: D = D.where(np.logical_and(D.time >= np.datetime64(tlim[0]), D.time <= np.datetime64(tlim[1])), drop=True) obstimes64 = D.time.values dt = np.array([Timestamp(t).to_pydatetime() for t in obstimes64]) - \ datetime.timedelta(seconds = leap_seconds) if fsp3 is None: aer = gpsSatPosition(fnav, dt, sv=sv, rx_position=D.position, coords='aer') else: aer = gpsSatPositionSP3(fsp3, dt, sv=sv, rx_position=D.position_geodetic, coords='aer') idel = (aer[1, :] >= el_mask) aer[:, ~idel] = np.nan D['time'] = dt if satpos: D['az'] = aer[0, :] D['el'] = aer[1, :] D['idel'] = idel if ipp: if ipp_alt is None: print('Auto assigned altitude of the IPP: 250 km') ipp_alt = 250e3 else: ipp_alt *= 1e3 rec_lat, rec_lon, rec_alt = D.position_geodetic fm = np.sin(np.radians(aer[1])) r_new = ipp_alt / fm lla_vector = np.array( aer2geodetic(aer[0], aer[1], r_new, rec_lat, rec_lon, rec_alt)) D['ipp_lon'] = lla_vector[1] D['ipp_lat'] = lla_vector[0] D['ipp_alt'] = ipp_alt return D
def getPP(satpos, sv, recpos, pph, err=1.0): """ get az and el to the satellite and repeatedly increase the range, converting to LLA each time to check the altitude. Stop when all the altitudes are within err of pph. Inputs satellite position array in ECEF, satellite number, receiver position in ECEF, pierce point height in km and error in km if you want. """ rlat, rlon, ralt = ecef2geodetic(recpos) sataz, satel, satr = ecef2aer(satpos[:, 0], satpos[:, 1], satpos[:, 2], rlat, rlon, ralt) r = np.zeros(len(satr)) pplat, pplon, ppalt = aer2geodetic(sataz, satel, r, rlat, rlon, ralt) mask = (ppalt / 1000 - pph) < 0 while np.sum(mask) > 0: r[mask] += 100 pplat, pplon, ppalt = aer2geodetic(sataz, satel, r * 1000, rlat, rlon, ralt) mask = (ppalt / 1000 - pph) < 0 sRange = r - 100.0 eRange = r count = 0 while not np.all(abs(ppalt / 1000 - pph) < err): count += 1 mRange = (sRange + eRange) / 2.0 pplat, pplon, ppalt = aer2geodetic(sataz, satel, mRange * 1000, rlat, rlon, ralt) mask = ppalt / 1000 > pph eRange[mask] = mRange[mask] sRange[~mask] = mRange[~mask] if (count > 100): raise TypeError('going too long') break ppalt = pph * 1000 return pplat, pplon, ppalt
def plot4d(atmos: xarray.Dataset, rodir: Path = None): if aer2geodetic is None: return for i, t in enumerate(atmos.time): time = Time(str(t.values)) obs = EarthLocation(0, 0) # geodetic lat,lon = 0,0 arbitrary sun = get_sun(time=time) aaf = AltAz(obstime=time, location=obs) sloc = sun.transform_to(aaf) slat, slon = aer2geodetic(sloc.az.value, sloc.alt.value, sloc.distance.value, 0, 0, 0)[:2] plot2dlatlon(atmos.sel(time=t), rodir, slat, slon)
def _toLLT(rxp=None, az=None, el=None, H=350): """ Default height of the IPP is 350 km. """ H *= 1e3 r = H / np.sin(np.radians(el)) lat, lon, alt = aer2geodetic(az=az, el=el, srange=r, lat0=rxp[0], lon0=rxp[1], h0=rxp[2]) return lat, lon
def test_aer_geodetic(aer, lla, lla0): lat1, lon1, alt1 = pm.aer2geodetic(*aer, *lla0) assert lat1 == approx(lla[0]) assert lon1 == approx(lla[1]) assert alt1 == approx(lla[2]) assert isinstance(lat1, float) assert isinstance(lon1, float) assert isinstance(alt1, float) raer = (radians(aer[0]), radians(aer[1]), aer[2]) rlla0 = (radians(lla0[0]), radians(lla0[1]), lla0[2]) assert pm.aer2geodetic(*raer, *rlla0, deg=False) == approx( (radians(lla[0]), radians(lla[1]), lla[2])) with pytest.raises(ValueError): pm.aer2geodetic(aer[0], aer[1], -1, *lla0) assert pm.geodetic2aer(*lla, *lla0) == approx(aer, rel=1e-3) assert pm.geodetic2aer(radians(lla[0]), radians(lla[1]), lla[2], *rlla0, deg=False) == approx(raer, rel=1e-3)
def makeImage(dtec, xgrid, ygrid, longitude=None, latitude=None, azimuth=None, elevation=None, rxp=None, altkm=None, im=np.nan): imout = np.nan * np.ones(im.shape, dtype=np.float32) lonlim = [np.min(xgrid), np.max(xgrid)] latlim = [np.min(ygrid), np.max(ygrid)] if azimuth is not None and elevation is not None and rxp is not None and altkm is not None: r1 = (altkm * 1e3) / np.sin(np.radians(elevation)) h0 = rxp[:, 2] #if rxp[2] >= 0 else 0 ipp_lla = aer2geodetic(az=azimuth, el=elevation, srange=r1, lat0=rxp[:, 0], lon0=rxp[:, 1], h0=h0) longitude = ipp_lla[1] latitude = ipp_lla[0] assert (longitude is not None) and (latitude is not None), "Lat/Lon coordinates invalid!" for isv in range(dtec.shape[0]): for irx in np.where(np.isfinite(dtec[isv]))[0]: idx, idy = getImageIndex(x=longitude[isv, irx], y=latitude[isv, irx], xlim=lonlim, ylim=latlim, xgrid=xgrid, ygrid=ygrid) # If image indexes are valid if np.isfinite(idx) and np.isfinite(idy): if im[idx, idy] is None: im[idx, idy] = [dtec[isv, irx]] else: im[idx, idy].append(dtec[isv, irx]) for i in range(im.shape[0]): for j in range(im.shape[1]): if im[i, j] is not None: imout[i, j] = np.nanmedian(im[i, j]) return imout
def test_ned_geodetic(): lla = pm.aer2geodetic(*aer0, *lla0) enu3 = pm.geodetic2enu(*lla, *lla0) ned3 = (enu3[1], enu3[0], -enu3[2]) assert pm.geodetic2ned(*lla, *lla0) == approx(ned3) lat, lon, alt = pm.enu2geodetic(*enu3, *lla0) assert lat == approx(lla[0]) assert lon == approx(lla[1]) assert alt == approx(lla[2]) lat, lon, alt = pm.ned2geodetic(*ned3, *lla0) assert lat == approx(lla[0]) assert lon == approx(lla[1]) assert alt == approx(lla[2])
def test_ned_geodetic(): lat1, lon1, alt1 = pm.aer2geodetic(*aer0, *lla0) enu3 = pm.geodetic2enu(lat1, lon1, alt1, *lla0) ned3 = (enu3[1], enu3[0], -enu3[2]) assert pm.geodetic2ned(lat1, lon1, alt1, *lla0) == approx(ned3) lat, lon, alt = pm.enu2geodetic(*enu3, *lla0) assert lat == approx(lat1) assert lon == approx(lon1) assert alt == approx(alt1) assert isinstance(lat, float) assert isinstance(lon, float) assert isinstance(alt, float) lat, lon, alt = pm.ned2geodetic(*ned3, *lla0) assert lat == approx(lat1) assert lon == approx(lon1) assert alt == approx(alt1) assert isinstance(lat, float) assert isinstance(lon, float) assert isinstance(alt, float)
def test_ecefenu(): assert_allclose(pm.aer2ecef(taz, tel, tsrange, tlat, tlon, talt), (a2x, a2y, a2z), rtol=0.01, err_msg='aer2ecef: {}'.format( pm.aer2ecef(taz, tel, tsrange, tlat, tlon, talt))) assert_allclose(pm.aer2enu(taz, tel, tsrange), (a2e, a2n, a2u), rtol=0.01, err_msg='aer2enu: ' + str(pm.aer2enu(taz, tel, tsrange))) assert_allclose(pm.aer2ned(taz, tel, tsrange), (a2n, a2e, -a2u), rtol=0.01, err_msg='aer2ned: ' + str(pm.aer2ned(taz, tel, tsrange))) assert_allclose(pm.ecef2enu(tx, ty, tz, tlat, tlon, talt), (e2e, e2n, e2u), rtol=0.01, err_msg='ecef2enu: {}'.format( pm.ecef2enu(tx, ty, tz, tlat, tlon, talt))) assert_allclose(pm.ecef2enuv(vx, vy, vz, tlat, tlon), (ve, vn, vu)) assert_allclose(pm.ecef2ned(tx, ty, tz, tlat, tlon, talt), (e2n, e2e, -e2u), rtol=0.01, err_msg='ecef2ned: {}'.format( pm.ecef2enu(tx, ty, tz, tlat, tlon, talt))) assert_allclose(pm.ecef2nedv(vx, vy, vz, tlat, tlon), (vn, ve, -vu)) assert_allclose(pm.aer2geodetic(taz, tel, tsrange, tlat, tlon, talt), (a2la, a2lo, a2a), rtol=0.01, err_msg='aer2geodetic {}'.format( pm.aer2geodetic(taz, tel, tsrange, tlat, tlon, talt))) assert_allclose(pm.ecef2aer(tx, ty, tz, tlat, tlon, talt), (ec2az, ec2el, ec2rn), rtol=0.01, err_msg='ecef2aer {}'.format( pm.ecef2aer(a2x, a2y, a2z, tlat, tlon, talt))) #%% assert_allclose(pm.enu2aer(te, tn, tu), (e2az, e2el, e2rn), rtol=0.01, err_msg='enu2aer: ' + str(pm.enu2aer(te, tn, tu))) assert_allclose(pm.ned2aer(tn, te, -tu), (e2az, e2el, e2rn), rtol=0.01, err_msg='enu2aer: ' + str(pm.enu2aer(te, tn, tu))) assert_allclose(pm.enu2geodetic(te, tn, tu, tlat, tlon, talt), (lat2, lon2, alt2), rtol=0.01, err_msg='enu2geodetic: ' + str(pm.enu2geodetic(te, tn, tu, tlat, tlon, talt))) assert_allclose(pm.ned2geodetic(tn, te, -tu, tlat, tlon, talt), (lat2, lon2, alt2), rtol=0.01, err_msg='enu2geodetic: ' + str(pm.enu2geodetic(te, tn, tu, tlat, tlon, talt))) assert_allclose(pm.enu2ecef(te, tn, tu, tlat, tlon, talt), (e2x, e2y, e2z), rtol=0.01, err_msg='enu2ecef: ' + str(pm.enu2ecef(te, tn, tu, tlat, tlon, talt))) assert_allclose( pm.ned2ecef(tn, te, -tu, tlat, tlon, talt), (e2x, e2y, e2z), rtol=0.01, err_msg='ned2ecef: ' + str(pm.ned2ecef(tn, te, -tu, tlat, tlon, talt)))
def test_allnan(): np = pytest.importorskip("numpy") anan = np.empty((10, 10)) anan.fill(nan) assert np.isnan(pm.geodetic2aer(anan, anan, anan, *lla0)).all() assert np.isnan(pm.aer2geodetic(anan, anan, anan, *lla0)).all()
def test_scalar_nan(): a, e, r = pm.geodetic2aer(nan, nan, nan, *lla0) assert isnan(a) and isnan(e) and isnan(r) lat, lon, alt = pm.aer2geodetic(nan, nan, nan, *lla0) assert isnan(lat) and isnan(lon) and isnan(alt)
def calculate_geo_point(point, az, el, srange): target = pymap3d.aer2geodetic(az, el, srange, point['lat'], point['lon'], point['alt']) return { 'lat': target[0], 'lon': target[1], 'alt': target[2] }
def plotgtd(dens,temp,dtime,altkm, ap, f107,glat,glon,rodir=None): # if rodir: rodir = Path(rodir).expanduser() dtime = atleast_1d(dtime) sfmt = ScalarFormatter(useMathText=True) #for 10^3 instead of 1e3 sfmt.set_powerlimits((-2, 2)) sfmt.set_scientific(True) sfmt.set_useOffset(False) if dens.ndim==2: #altitude 1-D plot1d(dens,temp,glat,glon,ap,f107) elif dens.ndim==4: #lat/lon grid #%% sun lat/lon ttime = Time(dtime) obs = EarthLocation(0,0) # geodetic lat,lon = 0,0 arbitrary sun = get_sun(time=ttime) aaf = AltAz(obstime=ttime,location=obs) sloc = sun.transform_to(aaf) slat,slon = aer2geodetic(sloc.az.value, sloc.alt.value, sloc.distance.value,0,0,0)[:2] #%% #iterate over time for k,d in enumerate(dens): #dens is a 4-D array time x species x lat x lon fg,ax = subplots(4,2,sharex=True, figsize=(8,8)) fg.suptitle(datetime.fromtimestamp(d.time.item()/1e9, tz=UTC).isoformat(timespec='minutes') + f' alt.(km) {altkm}\nAp={ap[0]} F10.7={f107}') ax=ax.ravel(); i = 0 #don't use enumerate b/c of skip #iterate over species for s in d: thisspecies = s.species.values if thisspecies != 'Total': a = ax[i] hi = a.imshow(s.values, aspect='auto', interpolation='none',cmap='viridis', extent=(glon[0,0],glon[0,-1],glat[0,0],glat[-1,0])) fg.colorbar(hi,ax=a, format=sfmt) a.plot(slon[k],slat[k],linestyle='none', marker='o',markersize=5,color='w') a.set_title(f'Density: {thisspecies}') a.set_xlim(-180,180) a.set_ylim(-90,90) a.autoscale(False) i+=1 for i in range(0,6+2,2): ax[i].set_ylabel('latitude (deg)') for i in (6,7): ax[i].set_xlabel('longitude (deg)') if rodir: thisofn = rodir / f'{altkm[0]:.1f}_{k:03d}.png' print('writing',thisofn) fg.savefig(str(thisofn), dpi=100, bbox_inches='tight') close() else: print('densities',dens) print('temperatures',temp)
def map_target(tx, rx, az, el, rf, dop, wavelength): """ Find the scatter location given tx location, rx location, total rf distance, and target angle-of-arrival using the 'WGS84' Earth model. Also determines the bistatic velocity vector and bistatic radar wavelength. Parameters ---------- tx : float np.array [latitude, longitude, altitude] of tx array in degrees and kilometers rx : float np.array [latitude, longitude, altitude] of rx array in degrees and kilometers az : float np.array angle-of-arrival azimuth in degrees el : float np.array angle-of-arrival elevation in degrees rf : float np.array total rf path distance rf = c * tau in kilometers dop : float np.array doppler shift in hertz wavelength : float radar signal center wavelength Returns ------- sx : float np.array [latitude, longitude, altitude] of scatter in degrees and kilometers sa : float np.array [azimuth, elevation, slant range] of scatter in degrees and kilometers sv : float np.array [azimuth, elevation, velocity] the bistatic Doppler velocity vector in degrees and kilometers. Coordinates given in the scattering targets local frame (azimuth from North, elevation up from the plane normal to zenith, Doppler [Hz] * lambda / (2 cos(e/2)) ) Notes ----- tx : transmitter location rx : receiver location sx : scatter location gx : geometric center of Earth, origin u_rt : unit vector rx to tx u_rs : unit vector rx to sx u_gt : unit vector gx to tx u_gr : unit vector gx to rx u_gs : unit vector gx to sx """ # Initialize output arrays sx = np.zeros((3, len(rf)), dtype=float) sa = np.zeros((3, len(rf)), dtype=float) sv = np.zeros((3, len(rf)), dtype=float) # Setup variables in correct units for pymap3d rf = rf * 1.0e3 az = np.where(az < 0.0, az + 360.0, az) az = np.deg2rad(az) el = np.deg2rad(np.abs(el)) # Determine the slant range, r bx1, by1, bz1 = pm.geodetic2ecef(rx[0], rx[1], rx[2], ell=pm.Ellipsoid("wgs84"), deg=True) v_gr = np.array([bx1, by1, bz1]) bx2, by2, bz2 = pm.geodetic2ecef(tx[0], tx[1], tx[2], ell=pm.Ellipsoid("wgs84"), deg=True) v_gt = np.array([bx2, by2, bz2]) raz, rel, b = pm.ecef2aer(bx2, by2, bz2, rx[0], rx[1], rx[2], ell=pm.Ellipsoid("wgs84"), deg=True) u_rt = np.array([ np.sin(np.deg2rad(raz)) * np.cos(np.deg2rad(rel)), np.cos(np.deg2rad(raz)) * np.cos(np.deg2rad(rel)), np.sin(np.deg2rad(rel)) ]) el -= relaxation_elevation(el, rf, az, b, u_rt) u_rs = np.array( [np.sin(az) * np.cos(el), np.cos(az) * np.cos(el), np.sin(el)]) r = (rf**2 - b**2) / (2 * (rf - b * np.dot(u_rt, u_rs))) # WGS84 Model for lat, long, alt sx[:, :] = pm.aer2geodetic(np.rad2deg(az), np.rad2deg(el), np.abs(r), np.repeat(rx[0], len(az)), np.repeat(rx[1], len(az)), np.repeat(rx[2], len(az)), ell=pm.Ellipsoid("wgs84"), deg=True) # Determine the bistatic Doppler velocity vector x, y, z = pm.geodetic2ecef(sx[0, :], sx[1, :], sx[2, :], ell=pm.Ellipsoid('wgs84'), deg=True) v_gs = np.array([x, y, z]) v_bi = (-1 * v_gs.T + v_gt / 2.0 + v_gr / 2.0).T u_bi = v_bi / np.linalg.norm(v_bi, axis=0) v_sr = (v_gr - v_gs.T).T u_sr = v_sr / np.linalg.norm(v_sr, axis=0) radar_wavelength = wavelength / np.abs( 2.0 * np.einsum('ij,ij->j', u_sr, u_bi)) # doppler_sign = np.sign(dop) # 1 for positive, -1 for negative, and 0 for zero doppler_sign = np.where( dop >= 0, 1, -1) # 1 for positive, -1 for negative, and 0 for zero vaz, vel, _ = pm.ecef2aer(doppler_sign * u_bi[0, :] + x, doppler_sign * u_bi[1, :] + y, doppler_sign * u_bi[2, :] + z, sx[0, :], sx[1, :], sx[2, :], ell=pm.Ellipsoid("wgs84"), deg=True) # Convert back to conventional units sx[2, :] /= 1.0e3 az = np.rad2deg(az) el = np.rad2deg(el) sa[:, :] = np.array([az, el, r / 1.0e3]) sv[:, :] = np.array([vaz, vel, dop * radar_wavelength]) return sx, sa, sv
def plotgtd(atmos: xarray.Dataset, rodir=None): # if rodir: rodir = Path(rodir).expanduser() sfmt = ScalarFormatter(useMathText=True) #for 10^3 instead of 1e3 sfmt.set_powerlimits((-2, 2)) sfmt.set_scientific(True) sfmt.set_useOffset(False) if atmos['N2'].squeeze().ndim == 1: #altitude 1-D plot1d(atmos) elif atmos['N2'].squeeze().ndim == 4: #lat/lon grid #%% sun lat/lon time = Time(str(atmos.time[0].values)) obs = EarthLocation(0, 0) # geodetic lat,lon = 0,0 arbitrary sun = get_sun(time=time) aaf = AltAz(obstime=time, location=obs) sloc = sun.transform_to(aaf) slat, slon = aer2geodetic(sloc.az.value, sloc.alt.value, sloc.distance.value, 0, 0, 0)[:2] slat = np.atleast_1d(slat) slon = np.atleast_1d(slon) #%% tableau for i, t in enumerate(atmos.time): fg = figure(figsize=(8, 8)) ax = fg.subplots(4, 2, sharex=True) fg.suptitle( str(t.values)[:-13] + f' alt.(km) {atmos.alt_km[0]}\n' f'Ap={atmos.Ap[0]} F10.7={atmos.f107.item()}') ax = ax.ravel() j = 0 for s in atmos.species: if s == 'Total': continue a = ax[j] hi = a.imshow(atmos[s][i][0], aspect='auto', interpolation='none', cmap='viridis', extent=(atmos.lon[0], atmos.lon[-1], atmos.lat[0], atmos.lat[-1])) fg.colorbar(hi, ax=a, format=sfmt) # %% sun icon moving a.plot(slon[i], slat[i], linestyle='none', marker='o', markersize=5, color='w') a.set_title(f'Density: {s}') a.set_xlim(-180, 180) a.set_ylim(-90, 90) a.autoscale(False) j += 1 for k in range(0, 6 + 2, 2): ax[k].set_ylabel('latitude (deg)') for k in (6, 7): ax[k].set_xlabel('longitude (deg)') if rodir: ofn = rodir / f'{atmos.alt_km[0].item():.1f}_{i:03d}.png' print('writing', ofn) fg.savefig(ofn, dpi=100, bbox_inches='tight') close() else: print(atmos)
def test_geodetic(self): if pyproj: ecef = pyproj.Proj(proj='geocent', ellps='WGS84', datum='WGS84') lla = pyproj.Proj(proj='latlong', ellps='WGS84', datum='WGS84') xyz1 = pm.geodetic2ecef(*lla0) assert_allclose(pm.geodetic2ecef(*rlla0, deg=False), xyz1, err_msg='geodetic2ecef: rad') assert_allclose(xyz1, xyz0, err_msg='geodetic2ecef: deg') assert_allclose(pm.ecef2geodetic(*xyz1), lla0, err_msg='ecef2geodetic: deg') assert_allclose(pm.ecef2geodetic(*xyz1, deg=False), rlla0, err_msg='ecef2geodetic: rad') if pyproj: assert_allclose( pyproj.transform(lla, ecef, lla0[1], lla0[0], lla0[2]), xyz1) assert_allclose(pyproj.transform(ecef, lla, *xyz1), (lla0[1], lla0[0], lla0[2])) lla2 = pm.aer2geodetic(*aer0, *lla0) rlla2 = pm.aer2geodetic(*raer0, *rlla0, deg=False) assert_allclose(lla2, lla1, err_msg='aer2geodetic: deg') assert_allclose(rlla2, rlla1, err_msg='aer2geodetic:rad') assert_allclose(pm.geodetic2aer(*lla2, *lla0), aer0, err_msg='geodetic2aer: deg') assert_allclose(pm.geodetic2aer(*rlla2, *rlla0, deg=False), raer0, err_msg='geodetic2aer: rad') # %% aer-ecef xyz2 = pm.aer2ecef(*aer0, *lla0) assert_allclose(pm.aer2ecef(*raer0, *rlla0, deg=False), axyz0, err_msg='aer2ecef:rad') assert_allclose(xyz2, axyz0, err_msg='aer2ecef: deg') assert_allclose(pm.ecef2aer(*xyz2, *lla0), aer0, err_msg='ecef2aer:deg') assert_allclose(pm.ecef2aer(*xyz2, *rlla0, deg=False), raer0, err_msg='ecef2aer:rad') # %% aer-enu enu1 = pm.aer2enu(*aer0) ned1 = (enu1[1], enu1[0], -enu1[2]) assert_allclose(enu1, enu0, err_msg='aer2enu: deg') assert_allclose(pm.aer2enu(*raer0, deg=False), enu0, err_msg='aer2enu: rad') assert_allclose(pm.aer2ned(*aer0), ned0, err_msg='aer2ned') assert_allclose(pm.enu2aer(*enu1), aer0, err_msg='enu2aer: deg') assert_allclose(pm.enu2aer(*enu1, deg=False), raer0, err_msg='enu2aer: rad') assert_allclose(pm.ned2aer(*ned1), aer0, err_msg='ned2aer') # %% enu-ecef assert_allclose(pm.enu2ecef(*enu1, *lla0), xyz2, err_msg='enu2ecef: deg') assert_allclose(pm.enu2ecef(*enu1, *rlla0, deg=False), xyz2, err_msg='enu2ecef: rad') assert_allclose(pm.ecef2enu(*xyz2, *lla0), enu1, err_msg='ecef2enu:deg') assert_allclose(pm.ecef2enu(*xyz2, *rlla0, deg=False), enu1, err_msg='ecef2enu:rad') assert_allclose(pm.ecef2ned(*xyz2, *lla0), ned1, err_msg='ecef2ned') assert_allclose(pm.ned2ecef(*ned1, *lla0), xyz2, err_msg='ned2ecef') # %% assert_allclose(pm.ecef2enuv(vx, vy, vz, *lla0[:2]), (ve, vn, vu)) assert_allclose(pm.ecef2nedv(vx, vy, vz, *lla0[:2]), (vn, ve, -vu)) # %% enu3 = pm.geodetic2enu(*lla2, *lla0) ned3 = (enu3[1], enu3[0], -enu3[2]) assert_allclose(pm.geodetic2ned(*lla2, *lla0), ned3, err_msg='geodetic2ned: deg') assert_allclose(pm.enu2geodetic(*enu3, *lla0), lla2, err_msg='enu2geodetic') assert_allclose(pm.ned2geodetic(*ned3, *lla0), lla2, err_msg='ned2geodetic')
def test_allnan(): anan = np.empty((10, 10)) anan.fill(nan) assert np.isnan(pm.geodetic2aer(anan, anan, anan, *lla0)).all() assert np.isnan(pm.aer2geodetic(anan, anan, anan, *lla0)).all()
aaf = AltAz(obstime=time, location=obs) sloc = sun.transform_to(aaf) # %% time = time.to_datetime() fg = figure() ax = fg.subplots(2, 1, sharex=True) ax[0].plot(time, sloc.alt) ax[0].set_title('sun elevation') ax[0].set_ylabel('elevation [deg]') ax[1].plot(time, sloc.az) ax[1].set_title('sun azimuth') ax[1].set_ylabel('azimuth [deg]') ax[1].set_xlabel('time') fg.suptitle(f'sun over 1 year @ lat,lon,alt: {obslla}') # %% lat, lon, alt = aer2geodetic(sloc.az.value, sloc.alt.value, sloc.distance.value, *obslla) ax = figure().gca() ax.plot(time, lat) ax.set_title('subsolar latitude vs. time') ax.set_ylabel('latitude [deg]') ax.set_xlabel('time') show()
import cartopy.crs as ccrs import dascutils.io as dio import pymap3d as pm from pathlib import Path PC = ccrs.PlateCarree() ST = ccrs.Stereographic() MR = ccrs.Mercator() R = Path('~/code/dascutils/').expanduser() dat = dio.load(R / 'tests', R / 'cal/PKR_DASC_20110112') img = dat[428].isel(time=1).values # %% coord conv. alt = 100000 srange = alt / np.sin(np.radians(dat.el.values)) lat, lon, alt = pm.aer2geodetic(dat.az.values, dat.el.values, srange, dat.lat, dat.lon, 0) mask = np.isfinite(lat) top = None for i, m in enumerate(mask): good = m.nonzero()[0] if good.size == 0: continue elif top is None: top = i lat[i, good[-1]:] = lat[i, good[-1]] lat[i, :good[0]] = lat[i, good[0]] lon[i, good[-1]:] = lon[i, good[-1]]
cax = fig.add_axes([posn.x0, posn.y0 - 0.03, posn.width, 0.02]) fig.colorbar(tecax, cax=cax, label='TEC [TECu]', orientation='horizontal') # Plot image gpsdata = h5py.File(gpsfn, 'r') el = np.nanmedian(gpsdata['el'][i - average:i + 1], axis=0) az = np.nanmedian(gpsdata['az'][i - average:i + 1], axis=0) r1 = (altkm * 1e3) / np.sin(np.radians(el)) h0 = np.nan_to_num(rxp[:, 2]) h0[h0 < 0] = 0 ipp_lla = aer2geodetic(az=az, el=el, srange=r1, lat0=rxp[:, 0], lon0=rxp[:, 1], h0=h0) glon = ipp_lla[1] glat = ipp_lla[0] try: # Convert coordinates # rotia = np.nanmedian(gpsdata['roti'][i-average:i+1, :, :], axis=0) * 60 idf = np.isfinite(glon) & np.isfinite(glat) if i > average and i < dt.size: rotia = np.nanmedian(gpsdata['roti'][i - average:i + 1], axis=0) * 60 elif average > i: rotia = np.nanmedian(gpsdata['roti'][i:i + 2], axis=0) * 60 else: rotia = np.nanmedian(gpsdata['roti'][i - average:i],
def create_start_point(origin_offset, target): point = pymap3d.aer2geodetic(origin_offset['az'], origin_offset['el'], origin_offset['srange'], target['lat'], target['lon'], target['alt']) return { 'lat': point[0], 'lon': point[1], 'alt': point[2] }
if el_filter is not None: el = np.where(el >= el_filter, el, np.nan) az = np.where(el >= el_filter, az, np.nan) # Reshape calibration files if im_test.shape != el.shape: el = pa.interpolateCoordinate(el, N=im_test.shape[0]) az = pa.interpolateCoordinate(az, N=im_test.shape[0]) # LLA # Map to altitude mapping_alt = 100000 r = mapping_alt / np.sin(np.deg2rad(el)) # Convert to WSG lat0 = data.lat lon0 = data.lon alt0 = data.alt_m lat, lon, alt = aer2geodetic(az, el, r, lat0, lon0, alt0) # Image for i in range(T.shape[0]): im = data[wl][i].values XG, YG, Zlla = pa.interpolateAS(lon, lat, im, N=N) asiplot.plotIMmap(XG, YG, Zlla, title=T[i], cmap='Greys_r', clim=[500, 4000]) if cfg == 'lla': t, xgrid, ygrid, im, [lon, lat, alt] = pa.returnASLatLonAlt(folder, azfn=azfn,
def test_allnan(): anan = np.empty((10, 10)) anan.fill(nan) assert np.isnan(pm.geodetic2aer(anan, anan, anan, *lla0)).all() assert np.isnan(pm.aer2geodetic(anan, anan, anan, *lla0)).all()