Skip to content

Commit 7676e74

Browse files
committed
enu2aer and ecef2geodetic singularity handling
normal singularity fix geodetic2ecef singularity addtest aer2ecef singularity test
1 parent 42394cf commit 7676e74

File tree

2 files changed

+111
-30
lines changed

2 files changed

+111
-30
lines changed

src/maptran.F90

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ elemental subroutine ecef2geodetic(x, y, z, lat, lon, alt, spheroid, deg)
114114
real(wp), intent(out) :: lat, lon
115115
real(wp), intent(out), optional :: alt
116116

117-
real(wp) :: ea, eb, r, E, u, Q, huE, Beta, eps
117+
real(wp) :: ea, eb, r, E, u, Q, huE, Beta, eps, sinBeta, cosBeta
118118
type(Ellipsoid) :: ell
119119
logical :: d, inside
120120

@@ -135,21 +135,29 @@ elemental subroutine ecef2geodetic(x, y, z, lat, lon, alt, spheroid, deg)
135135

136136
huE = hypot(u, E)
137137

138-
! eqn. 4b
138+
!> eqn. 4b
139139
Beta = atan(huE / u * z, hypot(x, y))
140140

141-
! eqn. 13
142-
eps = ((eb * u - ea * huE + E**2) * sin(Beta)) / (ea * huE * 1 / cos(Beta) - E**2 * cos(Beta))
143-
144-
Beta = Beta + eps
145-
! final output
146-
lat = atan(ea / eb * tan(Beta))
141+
!> final output
142+
if (abs(beta-pi/2) <= epsilon(pi/2)) then !< singularity
143+
lat = pi/2
144+
cosBeta = 0._wp
145+
sinBeta = 1._wp
146+
else
147+
!> eqn. 13
148+
eps = ((eb * u - ea * huE + E**2) * sin(Beta)) / (ea * huE * 1 / cos(Beta) - E**2 * cos(Beta))
149+
Beta = Beta + eps
150+
151+
lat = atan(ea / eb * tan(Beta))
152+
cosBeta = cos(Beta)
153+
sinBeta = sin(Beta)
154+
endif
147155

148156
lon = atan(y, x)
149157

150158
! eqn. 7
151159
if (present(alt)) then
152-
alt = hypot(z - eb * sin(Beta), Q - ea * cos(Beta))
160+
alt = hypot(z - eb * sinBeta, Q - ea * cosBeta)
153161

154162
!> inside ellipsoid?
155163
inside = x**2 / ea**2 + y**2 / ea**2 + z**2 / eb**2 < 1._wp
@@ -187,7 +195,7 @@ elemental subroutine geodetic2ecef(lat,lon,alt, x,y,z, spheroid, deg)
187195
type(Ellipsoid), intent(in), optional :: spheroid
188196
logical, intent(in), optional :: deg
189197

190-
real(wp) :: N
198+
real(wp) :: N, sinLat, cosLat, cosLon, sinLon
191199
type(Ellipsoid) :: ell
192200
logical :: d
193201

@@ -202,13 +210,37 @@ elemental subroutine geodetic2ecef(lat,lon,alt, x,y,z, spheroid, deg)
202210
lon = radians(lon)
203211
endif
204212

205-
! Radius of curvature of the prime vertical section
213+
!> Radius of curvature of the prime vertical section
206214
N = radius_normal(lat, ell)
207-
! Compute cartesian (geocentric) coordinates given (curvilinear) geodetic coordinates.
208215

209-
x = (N + alt) * cos(lat) * cos(lon)
210-
y = (N + alt) * cos(lat) * sin(lon)
211-
z = (N * (ell%SemiminorAxis / ell%SemimajorAxis)**2 + alt) * sin(lat)
216+
!> Compute cartesian (geocentric) coordinates given (curvilinear) geodetic coordinates.
217+
218+
if (abs(lat) <= epsilon(lat)) then
219+
cosLat = 1._wp
220+
sinLat = 0._wp
221+
elseif (abs(lat-pi/2) <= epsilon(lat)) then
222+
cosLat = 0._wp
223+
sinLat = 1._wp
224+
else
225+
cosLat = cos(lat)
226+
sinLat = sin(lat)
227+
endif
228+
229+
if (abs(lon) <= epsilon(lon)) then
230+
cosLon = 1._wp
231+
sinLon = 0._wp
232+
elseif (abs(lon-pi/2) <= epsilon(lon)) then
233+
cosLon = 0._wp
234+
sinLon = 1._wp
235+
else
236+
cosLon = cos(lon)
237+
sinLon = sin(lon)
238+
endif
239+
240+
241+
x = (N + alt) * cosLat * cosLon
242+
y = (N + alt) * cosLat * sinLon
243+
z = (N * (ell%SemiminorAxis / ell%SemimajorAxis)**2 + alt) * sinLat
212244

213245
end subroutine geodetic2ecef
214246

@@ -460,14 +492,19 @@ elemental subroutine enu2aer(east, north, up, az, elev, slantRange, deg)
460492
logical, intent(in), optional :: deg
461493
real(wp), intent(out) :: az, elev, slantRange
462494

463-
real(wp) :: r
495+
real(wp) :: r, e
464496
logical :: d
497+
498+
!> singularity, 1mm precision
499+
e = east
500+
if (abs(e) < 1e-3_wp) e = 0._wp
465501

466-
r = hypot(east, north)
502+
r = hypot(e, north)
467503
slantRange = hypot(r, up)
468-
! radians
504+
505+
!> radians
469506
elev = atan(up, r)
470-
az = modulo(atan(east, north), 2._wp * atan(0._wp, -1._wp))
507+
az = modulo(atan(e, north), 2._wp * pi)
471508

472509
d=.true.
473510
if (present(deg)) d = deg
@@ -612,10 +649,18 @@ end subroutine enu2uvw
612649

613650

614651
elemental real(wp) function radius_normal(lat,E)
615-
real(wp), intent(in) :: lat
616-
type(Ellipsoid), intent(in) :: E
617652

618-
radius_normal = E%SemimajorAxis**2 / sqrt( E%SemimajorAxis**2 * cos(lat)**2 + E%SemiminorAxis**2 * sin(lat)**2 )
653+
real(wp), intent(in) :: lat
654+
type(Ellipsoid), intent(in) :: E
655+
656+
!> singularity
657+
if (abs(lat) <= epsilon(lat)) then
658+
radius_normal = E%SemimajorAxis
659+
!elseif (abs(lat-pi/2) <= epsilon(lat)) then
660+
! radius_normal = E%SemiminorAxis
661+
else
662+
radius_normal = E%SemimajorAxis**2 / sqrt( E%SemimajorAxis**2 * cos(lat)**2 + E%SemiminorAxis**2 * sin(lat)**2 )
663+
endif
619664

620665
end function radius_normal
621666

tests/test_mod.f90

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ program Test
2929

3030

3131
real(wp) :: lat2, lon2, alt2,lat3,lon3,alt3,lat4,lon4,alt4, &
32-
x1,y1,z1,x2,y2,z2,x3,y3,z3,&
32+
x1,y1,z1,x2,y2,z2,x3,y3,z3, x7,y7,z7,&
3333
az2,el2,rng2,az3,el3,rng3,az4,el4,rng4,azrd,elrd,rae,dae,jd, &
3434
e1,n1,u1,e2,n2,u2,e3,n3,u3, &
3535
lat5(3), lon5(3), rng5(3), az5(3), tilt(3)
@@ -67,37 +67,72 @@ program Test
6767
call geodetic2ecef(lat,lon,alt, x1,y1,z1)
6868
call assert_allclose([x1,y1,z1],[x0,y0,z0], &
6969
err_msg='geodetic2ecef-degrees')
70+
71+
call geodetic2ecef(0._wp, 0._wp, -1._wp, x7, y7, z7)
72+
call assert_allclose([x7,y7,z7], [spheroid%SemimajorAxis-1, 0._wp, 0._wp])
73+
74+
call geodetic2ecef(0._wp, 90._wp, -1._wp, x7, y7, z7)
75+
call assert_allclose([x7,y7,z7], [0._wp, spheroid%SemimajorAxis-1, 0._wp])
76+
77+
call geodetic2ecef(90._wp, 0._wp, -1._wp, x7, y7, z7)
78+
call assert_allclose([x7,y7,z7], [0._wp, 0._wp, spheroid%SemiminorAxis-1])
79+
7080

7181
call aer2enu(az,el,rng, e1,n1,u1)
7282
call assert_allclose([e1,n1,u1], [er,nr,ur])
7383

84+
7485
call aer2ecef(az,el,rng,lat,lon,alt,x2,y2,z2)
7586
call assert_allclose([x2,y2,z2],[xl,yl,zl])
7687

88+
call aer2ecef(0._wp, -90._wp, 1._wp, 0._wp, 0._wp, -1._wp, x7, y7, z7)
89+
call assert_allclose([x7,y7,z7],[spheroid%SemimajorAxis-1._wp, 0._wp, 0._wp], atol=1e-6_wp)
90+
91+
call aer2ecef(0._wp, -90._wp, 1._wp, 0._wp, 90._wp, -1._wp, x7, y7, z7)
92+
call assert_allclose([x7,y7,z7],[0._wp, spheroid%SemimajorAxis-1._wp, 0._wp], atol=1e-6_wp)
93+
94+
call aer2ecef(0._wp, -90._wp, 1._wp, 90._wp, 0._wp, -1._wp, x7, y7, z7)
95+
call assert_allclose([x7,y7,z7],[0._wp, 0._wp,spheroid%SemiminorAxis-1._wp], atol=1e-6_wp)
96+
7797
!> ECEF2GEODETIC tests
7898
call ecef2geodetic(x1,y1,z1,lat2,lon2,alt2)
7999
call assert_allclose([lat2,lon2,alt2],[lat,lon,alt], &
80100
rtol=0.01_wp, err_msg='ecef2geodetic-degrees')
81101

82-
call ecef2geodetic(spheroid%SemimajorAxis-1._wp, 0._wp, 0._wp, lat2, lon2, alt2)
102+
call ecef2geodetic(spheroid%SemimajorAxis-1, 0._wp, 0._wp, lat2, lon2, alt2)
83103
call assert_allclose([lat2,lon2,alt2],[0._wp, 0._wp, -1._wp], &
84-
rtol=0.01_wp, err_msg='ecef2geodetic-degrees')
104+
err_msg='ecef2geodetic-degrees')
85105

86-
call ecef2geodetic(0._wp, spheroid%SemimajorAxis-1._wp, 0._wp, lat2, lon2, alt2)
106+
call ecef2geodetic(0._wp, spheroid%SemimajorAxis-1, 0._wp, lat2, lon2, alt2)
87107
call assert_allclose([lat2,lon2,alt2],[0._wp, 90._wp, -1._wp], &
88-
rtol=0.01_wp, err_msg='ecef2geodetic-degrees')
108+
err_msg='ecef2geodetic-degrees')
89109

90-
call ecef2geodetic(0._wp, 0._wp, spheroid%SemiminorAxis-1._wp, lat2, lon2, alt2)
110+
call ecef2geodetic(0._wp, 0._wp, spheroid%SemiminorAxis-1, lat2, lon2, alt2)
91111
call assert_allclose([lat2,lon2,alt2],[90._wp, 0._wp, -1._wp], &
92-
rtol=0.01_wp, err_msg='ecef2geodetic-degrees')
112+
err_msg='ecef2geodetic-degrees')
93113

94114
call enu2aer(e1,n1,u1, az2, el2, rng2)
95115
call assert_allclose([az2,el2,rng2],[az,el,rng],err_msg='enu2aer-degrees')
96116

117+
!> ECEF2AER
97118
call ecef2aer(x2,y2,z2, lat,lon,alt, az3, el3, rng3)
98119
call assert_allclose([az3,el3,rng3],[az,el,rng], &
99120
rtol=1e-3_wp, err_msg='ecef2aer-degrees')
100121

122+
call ecef2aer(spheroid%SemimajorAxis-1._wp, 0._wp, 0._wp, 0._wp, 0._wp, 0._wp, az3, el3, rng3)
123+
call assert_allclose([az3,el3,rng3], [0._wp, -90._wp, 1._wp], &
124+
err_msg='ecef2aer-degrees')
125+
126+
call ecef2aer(0._wp, spheroid%SemimajorAxis-1._wp, 0._wp, 0._wp, 90._wp, 0._wp, az3, el3, rng3)
127+
call assert_allclose([az3,el3,rng3], [0._wp, -90._wp, 1._wp], &
128+
err_msg='ecef2aer-degrees')
129+
130+
call ecef2aer(0._wp, 0._wp, spheroid%SemimajorAxis-1._wp, 90._wp, 0._wp, 0._wp, az3, el3, rng3)
131+
call assert_allclose([az3,el3,rng3], [0._wp, -90._wp, 1._wp], &
132+
rtol=1e6_wp, err_msg='ecef2aer-degrees')
133+
134+
135+
101136
call aer2geodetic(az,el,rng,lat,lon,alt, lat3,lon3,alt3)
102137
call assert_allclose([lat3,lon3,alt3],[lat1,lon1,alt1], &
103138
rtol=1e-3_wp, err_msg='aer2geodetic-degrees')
@@ -123,7 +158,7 @@ program Test
123158

124159
call assert_allclose(degrees(radians(deg0)), deg0, err_msg='deg<->rad')
125160

126-
! ------ scalar radians
161+
!> scalar radians
127162

128163
call geodetic2ecef(radians(lat),radians(lon),alt, x1,y1,z1, deg=.false.)
129164
call assert_allclose([x1,y1,z1],[x0,y0,z0])
@@ -142,6 +177,7 @@ program Test
142177
call assert_allclose([degrees(az2),degrees(el2),rng2],[az,el,rng], &
143178
err_msg='enu2aer: rad')
144179

180+
!> ECEF2AER
145181
call ecef2aer(x2,y2,z2, radians(lat),radians(lon),alt, az3,el3,rng3, deg=.false.)
146182
call assert_allclose([degrees(az3),degrees(el3),rng3],[az,el,rng], &
147183
rtol=1e-3_wp, err_msg='ecef2aer-radians')

0 commit comments

Comments
 (0)