1 | SUBROUTINE ropp_fm_roprof2obs1dbangle(ro_data, y)
|
---|
2 |
|
---|
3 | ! 1.1 Declarations
|
---|
4 | ! ----------------
|
---|
5 |
|
---|
6 | USE typesizes, ONLY: wp => EightByteReal
|
---|
7 | USE ropp_utils
|
---|
8 | USE ropp_io
|
---|
9 | USE ropp_io_types, ONLY: ROprof
|
---|
10 | USE ropp_fm
|
---|
11 | USE ropp_fm_types, ONLY: Obs1dBangle
|
---|
12 | USE ropp_fm_copy, not_this => ropp_fm_roprof2obs1dbangle
|
---|
13 | USE geodesy
|
---|
14 |
|
---|
15 | IMPLICIT NONE
|
---|
16 |
|
---|
17 | TYPE(ROprof), INTENT(in) :: ro_data ! RO data structure
|
---|
18 | TYPE(Obs1dBangle), INTENT(inout) :: y ! Bending angle structure
|
---|
19 |
|
---|
20 | INTEGER :: i, n, ipn
|
---|
21 | INTEGER, DIMENSION(8) :: DT8
|
---|
22 |
|
---|
23 | CHARACTER(len = 256) :: routine
|
---|
24 | CHARACTER(len = 10) :: err_val
|
---|
25 | CHARACTER(LEN=6) :: sr_leo
|
---|
26 |
|
---|
27 | ! 1.2 Error handling
|
---|
28 | ! ------------------
|
---|
29 |
|
---|
30 | CALL message_get_routine(routine)
|
---|
31 | CALL message_set_routine('ropp_fm_roprof2obs (1D bending angles)')
|
---|
32 |
|
---|
33 | y%obs_ok = .TRUE.
|
---|
34 |
|
---|
35 | ! 1.3 Check and copy geolocation and time
|
---|
36 | ! ---------------------------------------
|
---|
37 |
|
---|
38 | IF (isinrange(ro_data%georef%lon, ro_data%georef%range%lon)) THEN
|
---|
39 | y%lon = ro_data%georef%lon
|
---|
40 | ELSE
|
---|
41 | WRITE(err_val, '(e8.1)') ro_data%georef%lon
|
---|
42 | CALL message(msg_warn, &
|
---|
43 | "Longitude data for observations out of range or missing. " // &
|
---|
44 | "(longitude value = " // TRIM(err_val) // ")")
|
---|
45 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
46 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
47 | y%obs_ok = .FALSE.
|
---|
48 | ENDIF
|
---|
49 |
|
---|
50 | IF (isinrange(ro_data%georef%lat, ro_data%georef%range%lat)) THEN
|
---|
51 | y%lat = ro_data%georef%lat
|
---|
52 | ELSE
|
---|
53 | WRITE(err_val, '(e8.1)') ro_data%georef%lat
|
---|
54 | CALL message(msg_warn, &
|
---|
55 | "Latitude data for observations out of range or missing. " // &
|
---|
56 | "(latitude value = " // TRIM(err_val) // ")")
|
---|
57 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
58 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
59 | y%obs_ok = .FALSE.
|
---|
60 | ENDIF
|
---|
61 |
|
---|
62 | IF (isinrange(ro_data%georef%azimuth, ro_data%georef%range%azimuth)) THEN
|
---|
63 | y%azimuth = ro_data%georef%azimuth
|
---|
64 | ELSE
|
---|
65 | WRITE(err_val, '(e8.1)') ro_data%georef%azimuth
|
---|
66 | CALL message(msg_warn, "Azimuth (of " // TRIM(err_val) // " deg) out of range or missing.")
|
---|
67 | CALL message(msg_warn, "Check input data and valid_range attributes.")
|
---|
68 | CALL message(msg_warn, "Continuing, as azimuth not needed for 1D bending angles.")
|
---|
69 | ENDIF
|
---|
70 |
|
---|
71 | IF ( isinrange(ro_data%DTocc%year, ro_data%DTocc%range%year) .AND. &
|
---|
72 | isinrange(ro_data%DTocc%month, ro_data%DTocc%range%month) .AND. &
|
---|
73 | isinrange(ro_data%DTocc%day, ro_data%DTocc%range%day) .AND. &
|
---|
74 | isinrange(ro_data%DTocc%hour, ro_data%DTocc%range%hour) .AND. &
|
---|
75 | isinrange(ro_data%DTocc%minute, ro_data%DTocc%range%minute) .AND. &
|
---|
76 | isinrange(ro_data%DTocc%second, ro_data%DTocc%range%second) ) THEN
|
---|
77 | DT8 = (/ro_data%DTocc%year, ro_data%DTocc%month, &
|
---|
78 | ro_data%DTocc%day, 0, &
|
---|
79 | ro_data%DTocc%hour, ro_data%DTocc%minute, &
|
---|
80 | ro_data%DTocc%second, ro_data%DTocc%msec/)
|
---|
81 | CALL TimeSince ( DT8, y%time, 1, Base="JS2000" )
|
---|
82 | ELSE
|
---|
83 | CALL message(msg_warn, &
|
---|
84 | "Time data for observations out of range or missing.")
|
---|
85 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
86 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
87 | y%obs_ok = .FALSE.
|
---|
88 | ENDIF
|
---|
89 |
|
---|
90 | ! 1.4 Check that profiles are increasing in height - 1st element towards surface
|
---|
91 | ! ------------------------------------------------------------------------------
|
---|
92 |
|
---|
93 | CALL ropp_io_ascend(ro_data)
|
---|
94 |
|
---|
95 | ! 1.5 Check and copy observation data
|
---|
96 | ! -----------------------------------
|
---|
97 |
|
---|
98 | IF (ro_data%Lev1b%Npoints == 0) THEN
|
---|
99 | CALL message(msg_warn, &
|
---|
100 | "RO data has no level 1b (bending angle) parameters.")
|
---|
101 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
102 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
103 | y%obs_ok = .FALSE.
|
---|
104 | RETURN
|
---|
105 | ENDIF
|
---|
106 |
|
---|
107 | n = ro_data%Lev1b%Npoints
|
---|
108 |
|
---|
109 | IF (.NOT. ro_data%Lev2c%direct_ion) THEN
|
---|
110 |
|
---|
111 | ALLOCATE(y%impact(n), y%bangle(n), y%weights(n))
|
---|
112 | y%impact = ro_data%Lev1b%impact(1:n)
|
---|
113 | y%bangle = ro_data%Lev1b%bangle(1:n)
|
---|
114 | y%weights = 1.0_wp
|
---|
115 | y%n_L1 = n
|
---|
116 |
|
---|
117 | ELSE
|
---|
118 |
|
---|
119 | ALLOCATE(y%impact(2*n), y%bangle(2*n), y%weights(2*n))
|
---|
120 |
|
---|
121 | IF (ALL(ro_data%Lev1b%impact_L1 < ropp_MDTV)) THEN ! Use neutral IP if no L1 IPs
|
---|
122 | y%impact(1:n) = ro_data%Lev1b%impact(1:n)
|
---|
123 | ELSE
|
---|
124 | y%impact(1:n) = ro_data%Lev1b%impact_L1(1:n)
|
---|
125 | END IF
|
---|
126 |
|
---|
127 | IF (ALL(ro_data%Lev1b%impact_L2 < ropp_MDTV)) THEN ! Use neutral IP if no L2 IPs
|
---|
128 | y%impact(n+1:2*n) = ro_data%Lev1b%impact(1:n)
|
---|
129 | ELSE
|
---|
130 | y%impact(n+1:2*n) = ro_data%Lev1b%impact_L2(1:n)
|
---|
131 | END IF
|
---|
132 |
|
---|
133 | y%bangle(1:n) = ro_data%Lev1b%bangle_L1(1:n)
|
---|
134 | y%bangle(n+1:2*n) = ro_data%Lev1b%bangle_L2(1:n)
|
---|
135 | y%weights = 1.0_wp
|
---|
136 | y%n_L1 = n
|
---|
137 |
|
---|
138 | END IF
|
---|
139 |
|
---|
140 | y%g_sfc = gravity(ro_data%georef%lat, 0.0_wp)
|
---|
141 |
|
---|
142 | y%r_earth = R_eff(ro_data%georef%lat)
|
---|
143 |
|
---|
144 | IF (isinrange(ro_data%georef%roc, ro_data%georef%range%roc)) THEN
|
---|
145 | y%r_curve = ro_data%georef%roc
|
---|
146 | ELSE
|
---|
147 | CALL message(msg_warn, "Radius of curvature out of range or missing.")
|
---|
148 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
149 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
150 | y%obs_ok = .FALSE.
|
---|
151 | ENDIF
|
---|
152 |
|
---|
153 | IF ( ro_data%Lev2c%direct_ion ) THEN ! Need the radius of the LEO orbit for single frequency work
|
---|
154 |
|
---|
155 | IF ( ANY ( ABS(ro_data%georef%leo_pod%pos - ropp_MDFV) < 1.0_wp ) .OR. &
|
---|
156 | ANY ( ABS(ro_data%georef%r_coc - ropp_MDFV) < 1.0_wp ) ) THEN
|
---|
157 |
|
---|
158 | CALL message (msg_warn, 'Missing LEO position or centre of curvature ... ' // &
|
---|
159 | 'will try to estimate r_LEO from satellite ID.')
|
---|
160 |
|
---|
161 | SELECT CASE (ro_data%leo_id(1:2)) ! Data from https://www.wmo-sat.info/oscar/
|
---|
162 |
|
---|
163 | CASE ('C0')
|
---|
164 | CALL message (msg_info, 'Assuming LEO height of 800 km (COSMIC-1 profile)')
|
---|
165 | y%r_leo = y%r_curve + 800.E3_wp
|
---|
166 |
|
---|
167 | CASE ('C2')
|
---|
168 | CALL message (msg_info, 'Assuming LEO height of 520 km (COSMIC-2a profile)')
|
---|
169 | y%r_leo = y%r_curve + 520.E3_wp
|
---|
170 |
|
---|
171 | CASE ('CH')
|
---|
172 | CALL message (msg_info, 'Assuming LEO height of 470 km (CHAMP profile)')
|
---|
173 | y%r_leo = y%r_curve + 470.E3_wp
|
---|
174 |
|
---|
175 | CASE ('FY')
|
---|
176 | CALL message (msg_info, 'Assuming LEO height of 840 km (FY-3C/D/... profile)')
|
---|
177 | y%r_leo = y%r_curve + 840.E3_wp
|
---|
178 |
|
---|
179 | CASE ('GR')
|
---|
180 | SELECT CASE (ro_data%leo_id(1:4))
|
---|
181 | CASE ('GRAA', 'GRAB')
|
---|
182 | CALL message (msg_info, 'Assuming LEO height of 340 km (GRACE-A/B profile)')
|
---|
183 | y%r_leo = y%r_curve + 340.E3_wp
|
---|
184 | CASE ('GRAC', 'GRAD')
|
---|
185 | CALL message (msg_info, 'Assuming LEO height of 490 km (GRACE-FO profile)')
|
---|
186 | y%r_leo = y%r_curve + 490.E3_wp
|
---|
187 | CASE DEFAULT
|
---|
188 | CALL message(msg_warn, 'Unrecognised LEO satellite ID (' // ro_data%leo_id // &
|
---|
189 | ') ... r_LEO cannot be estimated.')
|
---|
190 | CALL message(msg_warn, 'Check that LEO POD and CoC positions, or LEO ID, are valid.')
|
---|
191 | CALL message(msg_warn, 'Setting status flag obs%ok to FALSE')
|
---|
192 | y%r_leo = ropp_MDFV
|
---|
193 | y%obs_ok = .FALSE.
|
---|
194 | END SELECT
|
---|
195 |
|
---|
196 | CASE ('KO')
|
---|
197 | CALL message (msg_info, 'Assuming LEO height of 550 km (KOMPSAT-5 profile)')
|
---|
198 | y%r_leo = y%r_curve + 550.E3_wp
|
---|
199 |
|
---|
200 | CASE ('ME')
|
---|
201 | CALL message (msg_info, 'Assuming LEO height of 830 km (Metop-A/B/C profile)')
|
---|
202 | y%r_leo = y%r_curve + 830.E3_wp
|
---|
203 |
|
---|
204 | CASE ('MG')
|
---|
205 | CALL message (msg_info, 'Assuming LEO height of 870 km (Megha-Tropiques profile)')
|
---|
206 | y%r_leo = y%r_curve + 870.E3_wp
|
---|
207 |
|
---|
208 | CASE ('PA')
|
---|
209 | CALL message (msg_info, 'Assuming LEO height of 510 km (PAZ profile)')
|
---|
210 | y%r_leo = y%r_curve + 510.E3_wp
|
---|
211 |
|
---|
212 | CASE DEFAULT
|
---|
213 | CALL message(msg_warn, 'Unrecognised LEO satellite ID (' // ro_data%leo_id // &
|
---|
214 | ') ... r_LEO cannot be estimated.')
|
---|
215 | CALL message(msg_warn, 'Check that LEO POD and CoC positions, or LEO ID, are valid.')
|
---|
216 | CALL message(msg_warn, 'Setting status flag obs%ok to FALSE')
|
---|
217 | y%r_leo = ropp_MDFV
|
---|
218 | y%obs_ok = .FALSE.
|
---|
219 |
|
---|
220 | END SELECT
|
---|
221 |
|
---|
222 | ELSE
|
---|
223 |
|
---|
224 | y%r_leo = SQRT ( SUM ( (ro_data%georef%leo_pod%pos - ro_data%georef%r_coc)**2 ) )
|
---|
225 | WRITE (sr_leo, FMT='(F6.1)') (y%r_leo - y%r_curve) * 1.0E-3_wp
|
---|
226 | CALL message (msg_info, 'Using a LEO altitude of ' // sr_leo // ' km.')
|
---|
227 |
|
---|
228 | END IF
|
---|
229 |
|
---|
230 | ELSE
|
---|
231 |
|
---|
232 | y%r_leo = ropp_MDFV ! For safety's sake
|
---|
233 |
|
---|
234 | END IF ! Single frequency work
|
---|
235 |
|
---|
236 | IF (isinrange(ro_data%georef%undulation, ro_data%georef%range%undulation)) THEN
|
---|
237 | y%undulation = ro_data%georef%undulation
|
---|
238 | ELSE
|
---|
239 | y%undulation = ropp_MDFV
|
---|
240 | CALL message(msg_warn, "Undulation out of range or missing.")
|
---|
241 | CALL message(msg_warn, "Check input data and valid_range attributes")
|
---|
242 | CALL message(msg_warn, "Set status flag obs%ok to FALSE")
|
---|
243 | y%obs_ok = .FALSE.
|
---|
244 | END IF
|
---|
245 |
|
---|
246 | ! 1.6 Check and copy sigmas to diagonal error covariance
|
---|
247 | ! ------------------------------------------------------
|
---|
248 |
|
---|
249 | y%cov_ok = .TRUE.
|
---|
250 |
|
---|
251 | IF (ASSOCIATED(y%cov%d)) DEALLOCATE(y%cov%d)
|
---|
252 |
|
---|
253 | IF (.NOT. ro_data%Lev2c%direct_ion) THEN
|
---|
254 |
|
---|
255 | CALL callocate(y%cov%d, n*(n+1)/2)
|
---|
256 |
|
---|
257 | DO i = 1, n
|
---|
258 | IF (ro_data%Lev1b%bangle(i) > 0.0_wp) THEN
|
---|
259 | IF (ro_data%Lev1b%bangle_sigma(i) > 0.0_wp) THEN
|
---|
260 | ! matrix_pp type, uplo = 'U'
|
---|
261 | y%cov%d(i + i*(i-1)/2) = ro_data%Lev1b%bangle_sigma(i)**2
|
---|
262 | ELSE
|
---|
263 | y%cov_ok = .FALSE.
|
---|
264 | END IF
|
---|
265 | ELSE
|
---|
266 | y%cov%d(i + i*(i-1)/2) = 0.0003_wp ! = 1 deg**2
|
---|
267 | ENDIF
|
---|
268 | END DO
|
---|
269 |
|
---|
270 | ELSE
|
---|
271 |
|
---|
272 | CALL callocate(y%cov%d, n*(2*n+1))
|
---|
273 |
|
---|
274 | DO i = 1, n
|
---|
275 |
|
---|
276 | IF (ro_data%Lev1b%bangle_L1(i) > 0.0_wp) THEN
|
---|
277 | IF (ro_data%Lev1b%bangle_L1_sigma(i) > 0.0_wp) THEN
|
---|
278 | ! matrix_pp type, uplo = 'U'
|
---|
279 | y%cov%d(i + i*(i-1)/2) = ro_data%Lev1b%bangle_L1_sigma(i)**2
|
---|
280 | ELSE
|
---|
281 | y%cov_ok = .FALSE.
|
---|
282 | END IF
|
---|
283 | ELSE
|
---|
284 | y%cov%d(i + i*(i-1)/2) = 0.0003_wp ! = 1 deg**2
|
---|
285 | END IF
|
---|
286 |
|
---|
287 | ipn = i + n
|
---|
288 | IF (ro_data%Lev1b%bangle_L2(i) > 0.0_wp) THEN
|
---|
289 | IF (ro_data%Lev1b%bangle_L2_sigma(i) > 0.0_wp) THEN
|
---|
290 | ! matrix_pp type, uplo = 'U'
|
---|
291 | y%cov%d(ipn + ipn*(ipn-1)/2) = ro_data%Lev1b%bangle_L2_sigma(i)**2
|
---|
292 | ELSE
|
---|
293 | y%cov_ok = .FALSE.
|
---|
294 | END IF
|
---|
295 | ELSE
|
---|
296 | y%cov%d(ipn + ipn*(ipn-1)/2) = 0.0003_wp ! = 1 deg**2
|
---|
297 | END IF
|
---|
298 |
|
---|
299 | END DO
|
---|
300 |
|
---|
301 | END IF
|
---|
302 |
|
---|
303 | IF (ASSOCIATED(y%cov%e)) DEALLOCATE(y%cov%e)
|
---|
304 | IF (ASSOCIATED(y%cov%f)) DEALLOCATE(y%cov%f)
|
---|
305 | IF (ASSOCIATED(y%cov%s)) DEALLOCATE(y%cov%s)
|
---|
306 |
|
---|
307 | y%cov%fact_chol = .FALSE.
|
---|
308 | y%cov%equi_chol = 'N'
|
---|
309 |
|
---|
310 | ! 1.7 Clean up
|
---|
311 | ! ------------
|
---|
312 |
|
---|
313 | CALL message_set_routine(routine)
|
---|
314 |
|
---|
315 | END SUBROUTINE ropp_fm_roprof2obs1dbangle
|
---|