SoftGNSS/geoFunctions/__init__.py
2025-10-22 16:08:12 +07:00

1196 lines
31 KiB
Python

import numpy as np
# cart2geo.m
def cart2geo(X, Y, Z, i, *args, **kwargs):
# CART2GEO Conversion of Cartesian coordinates (X,Y,Z) to geographical
# coordinates (phi, lambda, h) on a selected reference ellipsoid.
# [phi, lambda, h] = cart2geo(X, Y, Z, i);
# Choices i of Reference Ellipsoid for Geographical Coordinates
# 1. International Ellipsoid 1924
# 2. International Ellipsoid 1967
# 3. World Geodetic System 1972
# 4. Geodetic Reference System 1980
# 5. World Geodetic System 1984
# Kai Borre 10-13-98
# Copyright (c) by Kai Borre
# Revision: 1.0 Date: 1998/10/23
# ==========================================================================
a = np.array([6378388.0, 6378160.0, 6378135.0, 6378137.0, 6378137.0])
f = np.array([1 / 297, 1 / 298.247, 1 / 298.26, 1 / 298.257222101, 1 / 298.257223563])
lambda_ = np.arctan2(Y, X)
ex2 = (2 - f[i]) * f[i] / ((1 - f[i]) ** 2)
c = a[i] * np.sqrt(1 + ex2)
phi = np.arctan(Z / (np.sqrt(X ** 2 + Y ** 2) * (1 - (2 - f[i])) * f[i]))
h = 0.1
oldh = 0
iterations = 0
while abs(h - oldh) > 1e-12:
oldh = h
N = c / np.sqrt(1 + ex2 * np.cos(phi) ** 2)
phi = np.arctan(Z / (np.sqrt(X ** 2 + Y ** 2) * (1 - (2 - f[i]) * f[i] * N / (N + h))))
h = np.sqrt(X ** 2 + Y ** 2) / np.cos(phi) - N
iterations += 1
if iterations > 100:
print ('Failed to approximate h with desired precision. h-oldh: %e.' % (h - oldh))
break
phi *= (180 / np.pi)
# b = zeros(1,3);
# b(1,1) = fix(phi);
# b(2,1) = fix(rem(phi,b(1,1))*60);
# b(3,1) = (phi-b(1,1)-b(1,2)/60)*3600;
lambda_ *= (180 / np.pi)
# l = zeros(1,3);
# l(1,1) = fix(lambda);
# l(2,1) = fix(rem(lambda,l(1,1))*60);
# l(3,1) = (lambda-l(1,1)-l(1,2)/60)*3600;
# fprintf('\n phi =#3.0f #3.0f #8.5f',b(1),b(2),b(3))
# fprintf('\n lambda =#3.0f #3.0f #8.5f',l(1),l(2),l(3))
# fprintf('\n h =#14.3f\n',h)
return phi, lambda_, h
############## end cart2geo.m ###################
# clsin.m
def clsin(ar, degree, argument, *args, **kwargs):
# Clenshaw summation of sinus of argument.
# result = clsin(ar, degree, argument);
# Written by Kai Borre
# December 20, 1995
# See also WGS2UTM or CART2UTM
# ==========================================================================
cos_arg = 2 * np.cos(argument)
hr1 = 0
hr = 0
# TODO fix index range of t
for t in range(degree, 0, -1):
hr2 = hr1
hr1 = hr
hr = ar[t - 1] + cos_arg * hr1 - hr2
result = hr * np.sin(argument)
return result
####################### end clsin.m #####################
# clksin.m
def clksin(ar, degree, arg_real, arg_imag, *args, **kwargs):
# Clenshaw summation of sinus with complex argument
# [re, im] = clksin(ar, degree, arg_real, arg_imag);
# Written by Kai Borre
# December 20, 1995
# See also WGS2UTM or CART2UTM
# ==========================================================================
sin_arg_r = np.sin(arg_real)
cos_arg_r = np.cos(arg_real)
sinh_arg_i = np.sinh(arg_imag)
cosh_arg_i = np.cosh(arg_imag)
r = 2 * cos_arg_r * cosh_arg_i
i = - 2 * sin_arg_r * sinh_arg_i
hr1 = 0
hr = 0
hi1 = 0
hi = 0
# TODO fix index range of t
for t in range(degree, 0, - 1):
hr2 = hr1
hr1 = hr
hi2 = hi1
hi1 = hi
z = ar[t - 1] + r * hr1 - i * hi - hr2
hi = i * hr1 + r * hi1 - hi2
hr = z
r = sin_arg_r * cosh_arg_i
i = cos_arg_r * sinh_arg_i
re = r * hr - i * hi
im = r * hi + i * hr
return re, im
# cart2utm.m
def cart2utm(X, Y, Z, zone, *args, **kwargs):
# CART2UTM Transformation of (X,Y,Z) to (N,E,U) in UTM, zone 'zone'.
# [E, N, U] = cart2utm(X, Y, Z, zone);
# Inputs:
# X,Y,Z - Cartesian coordinates. Coordinates are referenced
# with respect to the International Terrestrial Reference
# Frame 1996 (ITRF96)
# zone - UTM zone of the given position
# Outputs:
# E, N, U - UTM coordinates (Easting, Northing, Uping)
# Kai Borre -11-1994
# Copyright (c) by Kai Borre
# This implementation is based upon
# O. Andersson & K. Poder (1981) Koordinattransformationer
# ved Geod\ae{}tisk Institut. Landinspekt\oe{}ren
# Vol. 30: 552--571 and Vol. 31: 76
# An excellent, general reference (KW) is
# R. Koenig & K.H. Weise (1951) Mathematische Grundlagen der
# h\"oheren Geod\"asie und Kartographie.
# Erster Band, Springer Verlag
# Explanation of variables used:
# f flattening of ellipsoid
# a semi major axis in m
# m0 1 - scale at central meridian; for UTM 0.0004
# Q_n normalized meridian quadrant
# E0 Easting of central meridian
# L0 Longitude of central meridian
# bg constants for ellipsoidal geogr. to spherical geogr.
# gb constants for spherical geogr. to ellipsoidal geogr.
# gtu constants for ellipsoidal N, E to spherical N, E
# utg constants for spherical N, E to ellipoidal N, E
# tolutm tolerance for utm, 1.2E-10*meridian quadrant
# tolgeo tolerance for geographical, 0.00040 second of arc
# B, L refer to latitude and longitude. Southern latitude is negative
# International ellipsoid of 1924, valid for ED50
a = 6378388.0
f = 1.0 / 297.0
ex2 = (2 - f) * f / (1 - f) ** 2
c = a * np.sqrt(1 + ex2)
vec = np.array([X, Y, Z - 4.5])
alpha = 7.56e-07
R = np.array([[1, - alpha, 0],
[alpha, 1, 0],
[0, 0, 1]])
trans = np.array([89.5, 93.8, 127.6])
scale = 0.9999988
v = scale * R.dot(vec) + trans
L = np.arctan2(v[1], v[0])
N1 = 6395000.0
B = np.arctan2(v[2] / ((1 - f) ** 2 * N1), np.linalg.norm(v[0:2]) / N1)
U = 0.1
oldU = 0
iterations = 0
while abs(U - oldU) > 0.0001:
oldU = U
N1 = c / np.sqrt(1 + ex2 * (np.cos(B)) ** 2)
B = np.arctan2(v[2] / ((1 - f) ** 2 * N1 + U), np.linalg.norm(v[0:2]) / (N1 + U))
U = np.linalg.norm(v[0:2]) / np.cos(B) - N1
iterations += 1
if iterations > 100:
print ('Failed to approximate U with desired precision. U-oldU: %e.' % (U - oldU))
break
# Normalized meridian quadrant, KW p. 50 (96), p. 19 (38b), p. 5 (21)
m0 = 0.0004
n = f / (2 - f)
m = n ** 2 * (1.0 / 4.0 + n ** 2 / 64)
w = (a * (-n - m0 + m * (1 - m0))) / (1 + n)
Q_n = a + w
# Easting and longitude of central meridian
E0 = 500000.0
L0 = (zone - 30) * 6 - 3
# Check tolerance for reverse transformation
tolutm = np.pi / 2 * 1.2e-10 * Q_n
tolgeo = 4e-05
# Coefficients of trigonometric series
# ellipsoidal to spherical geographical, KW p. 186--187, (51)-(52)
# bg[1] = n*(-2 + n*(2/3 + n*(4/3 + n*(-82/45))));
# bg[2] = n^2*(5/3 + n*(-16/15 + n*(-13/9)));
# bg[3] = n^3*(-26/15 + n*34/21);
# bg[4] = n^4*1237/630;
# spherical to ellipsoidal geographical, KW p. 190--191, (61)-(62)
# gb[1] = n*(2 + n*(-2/3 + n*(-2 + n*116/45)));
# gb[2] = n^2*(7/3 + n*(-8/5 + n*(-227/45)));
# gb[3] = n^3*(56/15 + n*(-136/35));
# gb[4] = n^4*4279/630;
# spherical to ellipsoidal N, E, KW p. 196, (69)
# gtu[1] = n*(1/2 + n*(-2/3 + n*(5/16 + n*41/180)));
# gtu[2] = n^2*(13/48 + n*(-3/5 + n*557/1440));
# gtu[3] = n^3*(61/240 + n*(-103/140));
# gtu[4] = n^4*49561/161280;
# ellipsoidal to spherical N, E, KW p. 194, (65)
# utg[1] = n*(-1/2 + n*(2/3 + n*(-37/96 + n*1/360)));
# utg[2] = n^2*(-1/48 + n*(-1/15 + n*437/1440));
# utg[3] = n^3*(-17/480 + n*37/840);
# utg[4] = n^4*(-4397/161280);
# With f = 1/297 we get
bg = np.array([- 0.00337077907, 4.73444769e-06, -8.2991457e-09, 1.5878533e-11])
gb = np.array([0.00337077588, 6.6276908e-06, 1.78718601e-08, 5.49266312e-11])
gtu = np.array([0.000841275991, 7.67306686e-07, 1.2129123e-09, 2.48508228e-12])
utg = np.array([-0.000841276339, -5.95619298e-08, -1.69485209e-10, -2.20473896e-13])
# Ellipsoidal latitude, longitude to spherical latitude, longitude
neg_geo = False
if B < 0:
neg_geo = True
Bg_r = np.abs(B)
res_clensin = clsin(bg, 4, 2 * Bg_r)
Bg_r = Bg_r + res_clensin
L0 = L0 * np.pi / 180
Lg_r = L - L0
# Spherical latitude, longitude to complementary spherical latitude
# i.e. spherical N, E
cos_BN = np.cos(Bg_r)
Np = np.arctan2(np.sin(Bg_r), np.cos(Lg_r) * cos_BN)
Ep = np.arctanh(np.sin(Lg_r) * cos_BN)
# Spherical normalized N, E to ellipsoidal N, E
Np *= 2
Ep *= 2
dN, dE = clksin(gtu, 4, Np, Ep)
Np /= 2
Ep /= 2
Np += dN
Ep += dE
N = Q_n * Np
E = Q_n * Ep + E0
if neg_geo:
N = -N + 20000000
return E, N, U
#################### end cart2utm.m ####################
# deg2dms.m
def deg2dms(deg, *args, **kwargs):
# DEG2DMS Conversion of degrees to degrees, minutes, and seconds.
# The output format (dms format) is: (degrees*100 + minutes + seconds/100)
# Written by Kai Borre
# February 7, 2001
# Updated by Darius Plausinaitis
# Save the sign for later processing
neg_arg = False
if deg < 0:
# Only positive numbers should be used while spliting into deg/min/sec
deg = -deg
neg_arg = True
# Split degrees minutes and seconds
int_deg = np.floor(deg)
decimal = deg - int_deg
min_part = decimal * 60
min_ = np.floor(min_part)
sec_part = min_part - np.floor(min_part)
sec = sec_part * 60
# Check for overflow
if sec == 60.0:
min_ = min_ + 1
sec = 0.0
if min_ == 60.0:
int_deg = int_deg + 1
min_ = 0.0
# Construct the output
dmsOutput = int_deg * 100 + min_ + sec / 100
# Correct the sign
if neg_arg:
dmsOutput = -dmsOutput
return dmsOutput
################### end deg2dms.m ################
# dms2mat.m
def dms2mat(dmsInput, n, *args, **kwargs):
# DMS2MAT Splits a real a = dd*100 + mm + s/100 into[dd mm s.ssss]
# where n specifies the power of 10, to which the resulting seconds
# of the output should be rounded. E.g.: if a result is 23.823476
# seconds, and n = -3, then the output will be 23.823.
# Written by Kai Borre
# January 7, 2007
# Updated by Darius Plausinaitis
neg_arg = False
if dmsInput < 0:
# Only positive numbers should be used while spliting into deg/min/sec
dmsInput = -dmsInput
neg_arg = True
# Split degrees minutes and seconds
int_deg = np.floor(dmsInput / 100)
mm = np.floor(dmsInput - 100 * int_deg)
# we assume n<7; hence #2.10f is sufficient to hold ssdec
ssdec = '%2.10f' % (dmsInput - 100 * int_deg - mm) * 100
# Check for overflow
if ssdec == 60.0:
mm = mm + 1
ssdec = 0.0
if mm == 60.0:
int_deg = int_deg + 1
mm = 0.0
# Corect the sign
if neg_arg:
int_deg = -int_deg
# Compose the output
matOutput = []
matOutput[0] = int_deg
matOutput[1] = mm
matOutput[2] = float(ssdec[0:- n + 3])
return matOutput
################### end dms2mat.m ################
# e_r_corr.m
def e_r_corr(traveltime, X_sat, *args, **kwargs):
# E_R_CORR Returns rotated satellite ECEF coordinates due to Earth
# rotation during signal travel time
# X_sat_rot = e_r_corr(traveltime, X_sat);
# Inputs:
# travelTime - signal travel time
# X_sat - satellite's ECEF coordinates
# Outputs:
# X_sat_rot - rotated satellite's coordinates (ECEF)
# Written by Kai Borre
# Copyright (c) by Kai Borre
# ==========================================================================
Omegae_dot = 7.292115147e-05
# --- Find rotation angle --------------------------------------------------
omegatau = Omegae_dot * traveltime
# --- Make a rotation matrix -----------------------------------------------
R3 = np.array([[np.cos(omegatau), np.sin(omegatau), 0.0],
[-np.sin(omegatau), np.cos(omegatau), 0.0],
[0.0, 0.0, 1.0]])
# --- Do the rotation ------------------------------------------------------
X_sat_rot = R3.dot(X_sat)
return X_sat_rot
######## end e_r_corr.m ####################
# findUtmZone.m
def findUtmZone(latitude, longitude, *args, **kwargs):
# Function finds the UTM zone number for given longitude and latitude.
# The longitude value must be between -180 (180 degree West) and 180 (180
# degree East) degree. The latitude must be within -80 (80 degree South) and
# 84 (84 degree North).
# utmZone = findUtmZone(latitude, longitude);
# Latitude and longitude must be in decimal degrees (e.g. 15.5 degrees not
# 15 deg 30 min).
# Check value bounds =====================================================
if longitude > 180 or longitude < - 180:
raise IOError('Longitude value exceeds limits (-180:180).')
if latitude > 84 or latitude < - 80:
raise IOError('Latitude value exceeds limits (-80:84).')
# Find zone ==============================================================
# Start at 180 deg west = -180 deg
utmZone = np.fix((180 + longitude) / 6) + 1
# Correct zone numbers for particular areas ==============================
if latitude > 72:
# Corrections for zones 31 33 35 37
if 0 <= longitude < 9:
utmZone = 31
elif 9 <= longitude < 21:
utmZone = 33
elif 21 <= longitude < 33:
utmZone = 35
elif 33 <= longitude < 42:
utmZone = 37
elif 56 <= latitude < 64:
# Correction for zone 32
if 3 <= longitude < 12:
utmZone = 32
return utmZone
# geo2cart.m
def geo2cart(phi, lambda_, h, i=4, *args, **kwargs):
# GEO2CART Conversion of geographical coordinates (phi, lambda, h) to
# Cartesian coordinates (X, Y, Z).
# [X, Y, Z] = geo2cart(phi, lambda, h, i);
# Format for phi and lambda: [degrees minutes seconds].
# h, X, Y, and Z are in meters.
# Choices i of Reference Ellipsoid
# 1. International Ellipsoid 1924
# 2. International Ellipsoid 1967
# 3. World Geodetic System 1972
# 4. Geodetic Reference System 1980
# 5. World Geodetic System 1984
# Inputs:
# phi - geocentric latitude (format [degrees minutes seconds])
# lambda - geocentric longitude (format [degrees minutes seconds])
# h - height
# i - reference ellipsoid type
# Outputs:
# X, Y, Z - Cartesian coordinates (meters)
# Kai Borre 10-13-98
# Copyright (c) by Kai Borre
# ==========================================================================
b = phi[0] + phi[1] / 60. + phi[2] / 3600.
b = b * np.pi / 180.
l = lambda_[1] + lambda_[2] / 60. + lambda_[3] / 3600.
l = l * np.pi / 180.
a = [6378388, 6378160, 6378135, 6378137, 6378137]
f = [1 / 297, 1 / 298.247, 1 / 298.26, 1 / 298.257222101, 1 / 298.257223563]
ex2 = (2. - f[i]) * f[i] / (1. - f[i]) ** 2
c = a[i] * np.sqrt(1. + ex2)
N = c / np.sqrt(1. + ex2 * np.cos(b) ** 2)
X = (N + h) * np.cos(b) * np.cos(l)
Y = (N + h) * np.cos(b) * np.sin(l)
Z = ((1. - f[i]) ** 2 * N + h) * np.sin(b)
return X, Y, Z
# leastSquarePos.m
def leastSquarePos(satpos_, obs, settings, *args, **kwargs):
# Function calculates the Least Square Solution.
# [pos, el, az, dop] = leastSquarePos(satpos, obs, settings);
# Inputs:
# satpos - Satellites positions (in ECEF system: [X; Y; Z;] -
# one column per satellite)
# obs - Observations - the pseudorange measurements to each
# satellite:
# (e.g. [20000000 21000000 .... .... .... .... ....])
# settings - receiver settings
# Outputs:
# pos - receiver position and receiver clock error
# (in ECEF system: [X, Y, Z, dt])
# el - Satellites elevation angles (degrees)
# az - Satellites azimuth angles (degrees)
# dop - Dilutions Of Precision ([GDOP PDOP HDOP VDOP TDOP])
# === Initialization =======================================================
nmbOfIterations = 7
dtr = np.pi / 180
pos = np.zeros(4)
X = satpos_.copy()
nmbOfSatellites = satpos_.shape[1]
A = np.zeros((nmbOfSatellites, 4))
omc = np.zeros(nmbOfSatellites)
az = np.zeros(nmbOfSatellites)
el = np.zeros(nmbOfSatellites)
dop = np.zeros(5)
# === Iteratively find receiver position ===================================
for iter_ in range(nmbOfIterations):
for i in range(nmbOfSatellites):
if iter_ == 0:
# --- Initialize variables at the first iteration --------------
Rot_X = X[:, i].copy()
trop = 2
else:
# --- Update equations -----------------------------------------
rho2 = (X[0, i] - pos[0]) ** 2 + (X[1, i] - pos[1]) ** 2 + (X[2, i] - pos[2]) ** 2
traveltime = np.sqrt(rho2) / settings.c
Rot_X = e_r_corr(traveltime, X[:, i])
az[i], el[i], dist = topocent(pos[0:3], Rot_X - pos[0:3])
if settings.useTropCorr:
# --- Calculate tropospheric correction --------------------
trop = tropo(np.sin(el[i] * dtr), 0.0, 1013.0, 293.0, 50.0, 0.0, 0.0, 0.0)
else:
# Do not calculate or apply the tropospheric corrections
trop = 0
# --- Apply the corrections ----------------------------------------
omc[i] = obs[i] - np.linalg.norm(Rot_X - pos[0:3]) - pos[3] - trop
A[i, :] = np.array([-(Rot_X[0] - pos[0]) / obs[i],
-(Rot_X[1] - pos[1]) / obs[i],
-(Rot_X[2] - pos[2]) / obs[i],
1])
# These lines allow the code to exit gracefully in case of any errors
if np.linalg.matrix_rank(A) != 4:
pos = np.zeros((4, 1))
return pos, el, az, dop
# --- Find position update ---------------------------------------------
x = np.linalg.lstsq(A, omc, rcond=None)[0]
pos = pos + x.flatten()
pos = pos.T
# === Calculate Dilution Of Precision ======================================
# --- Initialize output ------------------------------------------------
# dop = np.zeros((1, 5))
Q = np.linalg.inv(A.T.dot(A))
dop[0] = np.sqrt(np.trace(Q))
dop[1] = np.sqrt(Q[0, 0] + Q[1, 1] + Q[2, 2])
dop[2] = np.sqrt(Q[0, 0] + Q[1, 1])
dop[3] = np.sqrt(Q[2, 2])
dop[4] = np.sqrt(Q[3, 3])
return pos, el, az, dop
# check_t.m
def check_t(time, *args, **kwargs):
# CHECK_T accounting for beginning or end of week crossover.
# corrTime = check_t(time);
# Inputs:
# time - time in seconds
# Outputs:
# corrTime - corrected time (seconds)
# Kai Borre 04-01-96
# Copyright (c) by Kai Borre
# ==========================================================================
half_week = 302400.0
corrTime = time
if time > half_week:
corrTime = time - 2 * half_week
elif time < - half_week:
corrTime = time + 2 * half_week
return corrTime
####### end check_t.m #################
# satpos.m
def satpos(transmitTime, prnList, eph, settings, *args, **kwargs):
# SATPOS Computation of satellite coordinates X,Y,Z at TRANSMITTIME for
# given ephemeris EPH. Coordinates are computed for each satellite in the
# list PRNLIST.
# [satPositions, satClkCorr] = satpos(transmitTime, prnList, eph, settings);
# Inputs:
# transmitTime - transmission time
# prnList - list of PRN-s to be processed
# eph - ephemerides of satellites
# settings - receiver settings
# Outputs:
# satPositions - position of satellites (in ECEF system [X; Y; Z;])
# satClkCorr - correction of satellite clocks
# Initialize constants ===================================================
numOfSatellites = prnList.size
# GPS constatns
gpsPi = 3.14159265359
# system
# --- Constants for satellite position calculation -------------------------
Omegae_dot = 7.2921151467e-05
GM = 3.986005e+14
# the mass of the Earth, [m^3/s^2]
F = - 4.442807633e-10
# Initialize results =====================================================
satClkCorr = np.zeros(numOfSatellites)
satPositions = np.zeros((3, numOfSatellites))
# Process each satellite =================================================
for satNr in range(numOfSatellites):
prn = prnList[satNr] - 1
# Find initial satellite clock correction --------------------------------
# --- Find time difference ---------------------------------------------
dt = check_t(transmitTime - eph[prn].t_oc)
satClkCorr[satNr] = (eph[prn].a_f2 * dt + eph[prn].a_f1) * dt + eph[prn].a_f0 - eph[prn].T_GD
time = transmitTime - satClkCorr[satNr]
# Find satellite's position ----------------------------------------------
# Restore semi-major axis
a = eph[prn].sqrtA * eph[prn].sqrtA
tk = check_t(time - eph[prn].t_oe)
n0 = np.sqrt(GM / a ** 3)
n = n0 + eph[prn].deltan
M = eph[prn].M_0 + n * tk
M = np.remainder(M + 2 * gpsPi, 2 * gpsPi)
E = M
for ii in range(10):
E_old = E
E = M + eph[prn].e * np.sin(E)
dE = np.remainder(E - E_old, 2 * gpsPi)
if abs(dE) < 1e-12:
# Necessary precision is reached, exit from the loop
break
# Reduce eccentric anomaly to between 0 and 360 deg
E = np.remainder(E + 2 * gpsPi, 2 * gpsPi)
dtr = F * eph[prn].e * eph[prn].sqrtA * np.sin(E)
nu = np.arctan2(np.sqrt(1 - eph[prn].e ** 2) * np.sin(E), np.cos(E) - eph[prn].e)
phi = nu + eph[prn].omega
phi = np.remainder(phi, 2 * gpsPi)
u = phi + eph[prn].C_uc * np.cos(2 * phi) + eph[prn].C_us * np.sin(2 * phi)
r = a * (1 - eph[prn].e * np.cos(E)) + eph[prn].C_rc * np.cos(2 * phi) + eph[prn].C_rs * np.sin(2 * phi)
i = eph[prn].i_0 + eph[prn].iDot * tk + eph[prn].C_ic * np.cos(2 * phi) + eph[prn].C_is * np.sin(2 * phi)
Omega = eph[prn].omega_0 + (eph[prn].omegaDot - Omegae_dot) * tk - Omegae_dot * eph[prn].t_oe
Omega = np.remainder(Omega + 2 * gpsPi, 2 * gpsPi)
satPositions[0, satNr] = np.cos(u) * r * np.cos(Omega) - np.sin(u) * r * np.cos(i) * np.sin(Omega)
satPositions[1, satNr] = np.cos(u) * r * np.sin(Omega) + np.sin(u) * r * np.cos(i) * np.cos(Omega)
satPositions[2, satNr] = np.sin(u) * r * np.sin(i)
# Include relativistic correction in clock correction --------------------
satClkCorr[satNr] = (eph[prn].a_f2 * dt + eph[prn].a_f1) * dt + eph[prn].a_f0 - eph[prn].T_GD + dtr
return satPositions, satClkCorr
# topocent.m
# togeod.m
def togeod(a, finv, X, Y, Z, *args, **kwargs):
# TOGEOD Subroutine to calculate geodetic coordinates latitude, longitude,
# height given Cartesian coordinates X,Y,Z, and reference ellipsoid
# values semi-major axis (a) and the inverse of flattening (finv).
# [dphi, dlambda, h] = togeod(a, finv, X, Y, Z);
# The units of linear parameters X,Y,Z,a must all agree (m,km,mi,ft,..etc)
# The output units of angular quantities will be in decimal degrees
# (15.5 degrees not 15 deg 30 min). The output units of h will be the
# same as the units of X,Y,Z,a.
# Inputs:
# a - semi-major axis of the reference ellipsoid
# finv - inverse of flattening of the reference ellipsoid
# X,Y,Z - Cartesian coordinates
# Outputs:
# dphi - latitude
# dlambda - longitude
# h - height above reference ellipsoid
# Copyright (C) 1987 C. Goad, Columbus, Ohio
# Reprinted with permission of author, 1996
# Fortran code translated into MATLAB
# Kai Borre 03-30-96
# ==========================================================================
h = 0.0
tolsq = 1e-10
maxit = 10
# compute radians-to-degree factor
rtd = 180 / np.pi
# compute square of eccentricity
if finv < 1e-20:
esq = 0.0
else:
esq = (2 - 1 / finv) / finv
oneesq = 1 - esq
# first guess
# P is distance from spin axis
P = np.sqrt(X ** 2 + Y ** 2)
# direct calculation of longitude
if P > 1e-20:
dlambda = np.arctan2(Y, X) * rtd
else:
dlambda = 0.0
if dlambda < 0:
dlambda = dlambda + 360
# r is distance from origin (0,0,0)
r = np.sqrt(P ** 2 + Z ** 2)
if r > 1e-20:
sinphi = Z / r
else:
sinphi = 0.0
dphi = np.arcsin(sinphi)
# initial value of height = distance from origin minus
# approximate distance from origin to surface of ellipsoid
if r < 1e-20:
h = 0.0
return dphi, dlambda, h
h = r - a * (1 - sinphi * sinphi / finv)
# iterate
for i in range(maxit):
sinphi = np.sin(dphi)
cosphi = np.cos(dphi)
N_phi = a / np.sqrt(1 - esq * sinphi * sinphi)
dP = P - (N_phi + h) * cosphi
dZ = Z - (N_phi * oneesq + h) * sinphi
h = h + sinphi * dZ + cosphi * dP
dphi = dphi + (cosphi * dZ - sinphi * dP) / (N_phi + h)
if (dP * dP + dZ * dZ) < tolsq:
break
# Not Converged--Warn user
if i == maxit - 1:
print( ' Problem in TOGEOD, did not converge in %2.0f iterations' % i)
dphi *= rtd
return dphi, dlambda, h
######## end togeod.m ######################
def topocent(X, dx, *args, **kwargs):
# TOPOCENT Transformation of vector dx into topocentric coordinate
# system with origin at X.
# Both parameters are 3 by 1 vectors.
# [Az, El, D] = topocent(X, dx);
# Inputs:
# X - vector origin corrdinates (in ECEF system [X; Y; Z;])
# dx - vector ([dX; dY; dZ;]).
# Outputs:
# D - vector length. Units like units of the input
# Az - azimuth from north positive clockwise, degrees
# El - elevation angle, degrees
# Kai Borre 11-24-96
# Copyright (c) by Kai Borre
# ==========================================================================
dtr = np.pi / 180
phi, lambda_, h = togeod(6378137, 298.257223563, X[0], X[1], X[2])
cl = np.cos(lambda_ * dtr)
sl = np.sin(lambda_ * dtr)
cb = np.cos(phi * dtr)
sb = np.sin(phi * dtr)
F = np.array([[- sl, -sb * cl, cb * cl], [cl, -sb * sl, cb * sl], [0.0, cb, sb]])
local_vector = F.T.dot(dx)
E = local_vector[0]
N = local_vector[1]
U = local_vector[2]
hor_dis = np.sqrt(E ** 2 + N ** 2)
if hor_dis < 1e-20:
Az = 0.0
El = 90.0
else:
Az = np.arctan2(E, N) / dtr
El = np.arctan2(U, hor_dis) / dtr
if Az < 0:
Az = Az + 360
D = np.sqrt(dx[0] ** 2 + dx[1] ** 2 + dx[2] ** 2)
return Az, El, D
######### end topocent.m #########
# tropo.m
def tropo(sinel, hsta, p, tkel, hum, hp, htkel, hhum):
# TROPO Calculation of tropospheric correction.
# The range correction ddr in m is to be subtracted from
# pseudo-ranges and carrier phases
# ddr = tropo(sinel, hsta, p, tkel, hum, hp, htkel, hhum);
# Inputs:
# sinel - sin of elevation angle of satellite
# hsta - height of station in km
# p - atmospheric pressure in mb at height hp
# tkel - surface temperature in degrees Kelvin at height htkel
# hum - humidity in # at height hhum
# hp - height of pressure measurement in km
# htkel - height of temperature measurement in km
# hhum - height of humidity measurement in km
# Outputs:
# ddr - range correction (meters)
# Reference
# Goad, C.C. & Goodman, L. (1974) A Modified Tropospheric
# Refraction Correction Model. Paper presented at the
# American Geophysical Union Annual Fall Meeting, San
# Francisco, December 12-17
# A Matlab reimplementation of a C code from driver.
# Kai Borre 06-28-95
# ==========================================================================
a_e = 6378.137
b0 = 7.839257e-05
tlapse = -6.5
tkhum = tkel + tlapse * (hhum - htkel)
atkel = 7.5 * (tkhum - 273.15) / (237.3 + tkhum - 273.15)
e0 = 0.0611 * hum * 10 ** atkel
tksea = tkel - tlapse * htkel
em = -978.77 / (2870400.0 * tlapse * 1e-05)
tkelh = tksea + tlapse * hhum
e0sea = e0 * (tksea / tkelh) ** (4 * em)
tkelp = tksea + tlapse * hp
psea = p * (tksea / tkelp) ** em
if sinel < 0:
sinel = 0
tropo_ = 0.0
done = False
refsea = 7.7624e-05 / tksea
htop = 1.1385e-05 / refsea
refsea = refsea * psea
ref = refsea * ((htop - hsta) / htop) ** 4
while 1:
rtop = (a_e + htop) ** 2 - (a_e + hsta) ** 2 * (1 - sinel ** 2)
if rtop < 0:
rtop = 0
rtop = np.sqrt(rtop) - (a_e + hsta) * sinel
a = -sinel / (htop - hsta)
b = -b0 * (1 - sinel ** 2) / (htop - hsta)
rn = np.zeros(8)
for i in range(8):
rn[i] = rtop ** (i + 2)
alpha = np.array([2 * a, 2 * a ** 2 + 4 * b / 3, a * (a ** 2 + 3 * b),
a ** 4 / 5 + 2.4 * a ** 2 * b + 1.2 * b ** 2,
2 * a * b * (a ** 2 + 3 * b) / 3,
b ** 2 * (6 * a ** 2 + 4 * b) * 0.1428571, 0, 0])
if b ** 2 > 1e-35:
alpha[6] = a * b ** 3 / 2
alpha[7] = b ** 4 / 9
dr = rtop
dr = dr + alpha.dot(rn)
tropo_ += dr * ref * 1000
if done:
ddr = tropo_
break
done = True
refsea = (0.3719 / tksea - 1.292e-05) / tksea
htop = 1.1385e-05 * (1255.0 / tksea + 0.05) / refsea
ref = refsea * e0sea * ((htop - hsta) / htop) ** 4
return ddr
######### end tropo.m ###################
if __name__ == '__main__':
# print "This program is being run by itself"
pass
else:
print( 'Importing functions from ./geoFunctions/')