| 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
|
|---|