bgckSSMIS_mod sourceΒΆ

   1module bgckSSMIS_mod
   2  ! MODULE bgckSSMIS_mod (prefix='ssbg' category='1. High-level functionality')
   3  !
   4  !:Purpose: To perform background check and quality control for SSMIS radiance observations.
   5  !
   6  use midasMpi_mod
   7  use MathPhysConstants_mod
   8  use utilities_mod
   9  use obsSpaceData_mod
  10  use tovsNL_mod
  11  use obsErrors_mod
  12
  13  implicit none
  14  save
  15  private
  16
  17  ! Public functions/subroutines
  18  public :: ssbg_computeSsmisSurfaceType
  19  public :: ssbg_bgCheckSSMIS
  20  real    :: ssbg_clwQcThreshold
  21  logical :: ssbg_debug
  22
  23  real,    parameter :: ssbg_realMissing=-99. 
  24  integer, parameter :: ssbg_intMissing=-1
  25  ! Other variables:
  26  real,    parameter :: ssbg_rmisg=-999.0
  27  real,    parameter :: ssbg_clwThresh=0.02
  28  integer, parameter :: ssbg_mxval=30
  29  integer, parameter :: ssbg_maxObsNum=2500
  30  real,    parameter :: clw_amsu_rej=0.3
  31  real,    parameter :: clw_amsu_rej_ch3=0.1
  32  !  Highest peaking AMSU-A like SSMIS channel for ocean-only and CLW filtering
  33  !    3 = mid-troposphere   (AMSU/operations -- AMSU chan. 5)
  34  !    4 = upper-troposphere (scat. index used in AMSU/operations -- AMSU chan. 6)
  35  !       (AMSU-A scat. index cannot be computed here; need AMSU-A channels 1,2)
  36  integer, parameter :: ipc=4
  37  ! Module variable
  38
  39  character(len=128), parameter :: fileMgLg='fstglmg'  ! glace de mer file
  40  character(len=128), parameter :: fileGlace='bicefil'  ! binaire 0.1degre ice file
  41  character(len=128), parameter :: fileWentz='wentz_surf.std'  ! surface wentz file
  42  character(len=128), parameter :: algOption = 'fwentz'
  43  ! Other NRL thresholds
  44
  45  integer, parameter :: ssbg_maxNumSat  = 4
  46  integer, parameter :: ssbg_maxNumChan = 24
  47  integer, parameter :: ssbg_maxNumTest = 16
  48
  49  ! namelist variables
  50  logical                       :: RESETQC                       ! reset Qc flags option
  51  logical                       :: debug                         ! debug mode
  52
  53
  54  namelist /nambgck/debug, RESETQC
  55
  56contains
  57
  58  subroutine ssbg_init()
  59    !:Purpose: This subroutine reads the namelist section NAMBGCK
  60    !           for the module.
  61
  62    implicit none
  63
  64    ! Locals:
  65    integer           :: ierr, nulnam
  66    ! External functions
  67    integer, external :: fclos, fnom
  68
  69    ! Default values for namelist variables
  70    debug = .false.
  71    RESETQC = .false.
  72
  73    nulnam = 0
  74    ierr = fnom(nulnam, './flnml','FTN+SEQ+R/O', 0)
  75    read(nulnam, nml=nambgck, iostat=ierr)
  76    if (ierr /= 0) call utl_abort('ssbg_init: Error reading namelist')
  77    if (mmpi_myid == 0) write(*, nml=nambgck)
  78    ierr = fclos(nulnam)
  79
  80    ssbg_debug = debug
  81
  82  end subroutine ssbg_init 
  83
  84  !--------------------------------------------------------------------
  85  ! ssmis_tb2ta
  86  !--------------------------------------------------------------------
  87  subroutine ssmis_tb2ta(numObsToProcess, grossRej, ztb, zta)
  88    !:Purpose: Convert Tbs received from UKMO to Tas, by reversing Ta to Tb
  89    !           spillover correction applied in B. Bell's pre-processing.
  90
  91    implicit none
  92
  93    ! Arguments:
  94    integer, intent(in)  :: numObsToProcess  ! Number of obs points to process
  95    logical, intent(in)  :: grossRej(:)      ! Gross rejection indicator
  96    real,    intent(in)  :: ztb(:)           ! Tbs from input BURP file
  97    real,    intent(out) :: zta(:)           ! Tas after conversion
  98
  99    ! Locals:
 100    integer :: hiIndex
 101    integer :: loIndex
 102    integer :: obsIndex
 103    real    :: spillCoeffs(ssbg_maxNumChan)  ! Spillover correction coefficients
 104
 105    !  Define spillover correction coefficients
 106    !!  Row1           ch1  ch2  ch3  ch4   ch5   ch6
 107    !!  Row2           ch7  ch8  ch9  ch10  ch11  ch12
 108    !!  Row3           ch13 ch14 ch15 ch16  ch17  ch18
 109    !!  Row4           ch19 ch20 ch21 ch22  ch23  ch24
 110
 111    !  Spillover coeff for all channels (from Steve Swadley/NRL 9 June 2010)
 112
 113    !  spillCoeffs = (/ 0.9850,  0.9850,  0.9850,  0.9850,  0.9850,  0.9815, &
 114    !                    0.9815,  0.9949,  0.9934,  0.9934,  0.9934,  0.9680, &
 115    !                    0.9720,  0.9820,  0.9810,  0.9850,  0.9820,  0.9780, &
 116    !                    0.9815,  0.9815,  0.9815,  0.9815,  0.9815,  0.9815 /)
 117    !
 118    !  Spillover coeff for 7 SSM/I-like channels (ch. 12-18) only
 119    !
 120    spillCoeffs = (/ 1.0000,  1.0000,  1.0000,  1.0000,  1.0000,  1.0000, &
 121                      1.0000,  1.0000,  1.0000,  1.0000,  1.0000,  0.9680, &
 122                      0.9720,  0.9820,  0.9810,  0.9850,  0.9820,  0.9780, &
 123                      1.0000,  1.0000,  1.0000,  1.0000,  1.0000,  1.0000 /)
 124
 125    !  Apply Tb -> Ta conversion
 126
 127    loIndex = 1
 128    do obsIndex = 1, numObsToProcess
 129
 130      hiIndex = obsIndex*ssbg_maxNumChan
 131      if ( .not. grossRej(obsIndex) ) then
 132        zta(loIndex:hiIndex) = spillCoeffs(:) * ztb(loIndex:hiIndex)
 133      else
 134        zta(loIndex:hiIndex) = ztb(loIndex:hiIndex)
 135      end if
 136
 137      loIndex = hiIndex + 1
 138
 139    end do
 140
 141  end subroutine ssmis_tb2ta
 142
 143  !--------------------------------------------------------------------------
 144  ! f16tdr_remapping
 145  !--------------------------------------------------------------------------
 146  subroutine f16tdr_remapping(satId, SSMIS_Ta, Remapped_SSMI_Ta)
 147    !:Purpose: Remap SSMIS imaging channel antenna temperature to SSMI Ta
 148
 149    !       SSMIS         C_Freq          SSMI
 150    !      -------   -----------------   -------
 151    !       Chan12    19.35h              Chan2
 152    !       Chan13    19.35v              Chan1
 153    !       Chan14    22.235v             Chan3
 154    !       Chan15    37.0h               Chan5
 155    !       Chan16    37.0v               Chan4
 156    !       Chan17    91.65v -> 85.5v     Chan6
 157    !       Chan18    91.65h -> 85.5h     Chan7
 158
 159    implicit none
 160
 161    ! Arguments:
 162    integer, intent(in)  :: satId                             ! Satellite ID
 163    real,    intent(in)  :: SSMIS_Ta(ssbg_maxNumChan)         ! SSMIS antenna temperature
 164    real,    intent(out) :: Remapped_SSMI_Ta(ssbg_maxNumChan) ! Remapped SSMI antenna temperature
 165
 166    ! Locals:
 167    integer, parameter :: f16_id = 1
 168    integer, parameter :: f17_id = 2
 169    integer, parameter :: f18_id = 3
 170    integer(2)         :: channelIndex
 171    real(8)            :: CP(ssbg_maxNumChan)
 172    real               :: tbx(ssbg_maxNumChan)
 173
 174    ! NESDIS Intercept/Slope for F16 SSMIS 7 IMG channels to SSMI linear remapping
 175                               ! ch1  ch2  ch3  ch4  ch5  ch6  ch7  ch8  ch9  ch10 ch11 (LAS/ENV)
 176    real(8), parameter :: AP(ssbg_maxNumChan)=(/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &
 177                                   7.44254,7.80472,6.76383,8.55426,7.34409,6.57813,6.45397, &
 178                                   0.0, 0.0, 0.0, 0.0, 0.0, 0.0/)
 179
 180    real(8), parameter :: BP(ssbg_maxNumChan)=(/1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,&
 181                                   0.969424,0.967519,0.959808,0.954316,0.958955,0.980339,0.978795, &
 182                                   1.0, 1.0, 1.0, 1.0, 1.0, 1.0/)
 183
 184    ! NESDIS F17 and F18 Tb biases with respect to F16
 185    real(8), parameter :: CP_F17(ssbg_maxNumChan)=(/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &
 186                                      -0.779,  -1.446,  -1.013,  -0.522,  -0.240,   0.735,   0.521,   &
 187                                       0.0, 0.0, 0.0, 0.0, 0.0, 0.0/)
 188
 189    real(8), parameter :: CP_F18(ssbg_maxNumChan)=(/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &
 190                                      -0.773,  -0.688,  -1.031,  -0.632,  -0.411,   0.171,   0.928,   &
 191                                       0.0, 0.0, 0.0, 0.0, 0.0, 0.0/)
 192
 193    ! Initialization
 194    tbx(1:ssbg_maxNumChan) = SSMIS_Ta(1:ssbg_maxNumChan)
 195
 196    if ( satId == f17_id ) then
 197      CP = CP_F17
 198    else if ( satId == f18_id ) then
 199      CP = CP_F18
 200    else
 201      CP = 0.0
 202    end if
 203
 204    do channelIndex=1, ssbg_maxNumChan
 205      Remapped_SSMI_Ta(channelIndex) = AP(channelIndex) + BP(channelIndex)*(tbx(channelIndex)+CP(channelIndex))
 206    end do
 207
 208  end subroutine f16tdr_remapping
 209
 210
 211  !--------------------------------------------------------------------------
 212  ! ssmi_ta2tb_fweng
 213  !--------------------------------------------------------------------------  
 214  subroutine ssmi_ta2tb_fweng(Ta, Tb)
 215    !:Purpose: To convert antenna temperature(Ta) to brightness temperature(Tb).
 216
 217    !  (1) All channel antenna gain spill-over correction
 218    !  (2) Imaging channel Cross-polarization correction
 219    !  (3) Doppler correction (to be developed)
 220
 221    implicit none
 222
 223    ! Arugments
 224    real, intent(in)  :: Ta(24) ! Antenna temperature
 225    real, intent(out) :: Tb(24) ! Brightness temperature
 226
 227    ! Locals:
 228    real(8), parameter :: AP(24)=(/0.9850,0.9850,0.9850,0.9850,0.9850,0.9790,0.9815,&
 229                                   0.9949,0.9934,0.9934,0.9934, &
 230                                   0.9690,0.9690,0.9740,0.9860,0.9860,0.9880,0.9880,&
 231                                   0.9815,0.9815,0.9815,0.9815,0.9815,0.9815/)
 232    real(8), parameter :: BP(24)=(/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &
 233                                   0.0, 0.0, 0.0, 0.0, &
 234                                   0.00415,0.00473,0.0107,0.02612,0.0217,0.01383,0.01947,&
 235                                   0.0, 0.0, 0.0, 0.0, 0.0, 0.0/)
 236
 237    integer(4)         :: channelIndex
 238    real(4)            :: CP(24), DP(24)
 239
 240    ! All channel antenna gain correction. Note the cross-polarization effects on antenna gain correction.
 241    do channelIndex=1, 24
 242      CP(channelIndex) = 1.0/( AP(channelIndex)*(1.0 - BP(channelIndex)) )
 243      DP(channelIndex) = CP(channelIndex) * BP(channelIndex)
 244      Tb(channelIndex) = CP(channelIndex)*Ta(channelIndex)
 245    end do
 246
 247    ! SSMI IMG channel cross polarization correction
 248    Tb(12) = Tb(12) - DP(12)*Ta(13)               ! 19H
 249    Tb(13) = Tb(13) - DP(13)*Ta(12)               ! 19V
 250    Tb(14) = Tb(14) - DP(13)*(0.65*Ta(13)+96.6)   ! 22V
 251    Tb(15) = Tb(15) - DP(15)*Ta(16)               ! 37H
 252    Tb(16) = Tb(16) - DP(16)*Ta(15)               ! 37V
 253    Tb(17) = Tb(17) - DP(17)*Ta(18)               ! 85V
 254    Tb(18) = Tb(18) - DP(18)*Ta(17)               ! 85H
 255
 256  end subroutine ssmi_ta2tb_fweng
 257
 258  !--------------------------------------------------------------------------
 259  ! ssmi_ta2tb_fwengtz
 260  !--------------------------------------------------------------------------  
 261  subroutine ssmi_ta2tb_fwentz(Ta, Tb)
 262    !:Purpose: Convert antenna temperatures to brightness temperatures.
 263
 264    implicit none
 265
 266    ! Arguments:
 267    real, intent(in)  :: Ta(:) ! Antenna temperature
 268    real, intent(out) :: Tb(:) ! Brightness temperature
 269
 270    ! Locals:
 271    real :: AV19V,AH19V,A019V,AH19H,AV19H,A019H,AV22V,A022V
 272    real :: AV37V,AH37V,A037V,AH37H,AV37H,A037H
 273    real :: AV85V,AH85V,A085V,AH85H,AV85H,A085H
 274    real :: TA19V,TA19H,TA22V,TA37V,TA37H,TA85V,TA85H
 275    real :: TB19V,TB19H,TB22V,TB37V,TB37H,TB85V,TB85H
 276
 277    !  Define APC coefficients
 278
 279    AV19V =  1.0369830
 280    AH19V = -0.0039359
 281    A019V = -0.0892273
 282
 283    AH19H =  1.0384912
 284    AV19H = -0.0054442
 285    A019H = -0.0892271
 286
 287    AV22V =  1.01993
 288    A022V =  1.994
 289
 290    AV37V =  1.0368094
 291    AH37V = -0.0222607
 292    A037V = -0.0392815
 293
 294    AH37H =  1.0421693
 295    AV37H = -0.0276206
 296    A037H = -0.0392816
 297
 298    AV85V =  1.0263188
 299    AH85V = -0.0143165
 300    A085V = -0.0324062
 301
 302    AH85H =  1.0321901
 303    AV85H = -0.0201877
 304    A085H = -0.0324065
 305
 306    !  Extract list of Tas from parent array.
 307
 308    TA19H = Ta(12)
 309    TA19V = Ta(13)
 310    TA22V = Ta(14)
 311    TA37H = Ta(15)
 312    TA37V = Ta(16)
 313    TA85V = Ta(17)
 314    TA85H = Ta(18)
 315
 316    !  Apply APC corrections: convert TDR's to SDR's.
 317
 318    TB19V= (AV19V * TA19V) + (AH19V * TA19H) + (A019V)
 319    TB19H= (AH19H * TA19H) + (AV19H * TA19V) + (A019H)
 320    TB22V= (AV22V * TA22V) + A022V
 321    TB37V= (AV37V * TA37V) + (AH37V * TA37H) + (A037V)
 322    TB37H= (AH37H * TA37H) + (AV37H * TA37V) + (A037H)
 323    TB85V= (AV85V * TA85V) + (AH85V * TA85H) + (A085V)
 324    TB85H= (AH85H * TA85H) + (AV85H * TA85V) + (A085H)
 325
 326    !  Insert list of new Tbs into parent array.
 327
 328    Tb(12) = TB19H
 329    Tb(13) = TB19V
 330    Tb(14) = TB22V
 331    Tb(15) = TB37H
 332    Tb(16) = TB37V
 333    Tb(17) = TB85V
 334    Tb(18) = TB85H
 335
 336  end subroutine ssmi_ta2tb_fwentz
 337
 338  !--------------------------------------------------------------------------
 339  ! compute_iwv_101 
 340  !--------------------------------------------------------------------------  
 341  subroutine compute_iwv_101(Tb, iwv)
 342    !:Purpose: Compute integrated water vapor from SSMI brightness temperatures.
 343
 344    implicit none
 345
 346    ! Arguments:
 347    real, intent(in) :: Tb(24) ! Brightness temperature
 348    real, intent(out) :: iwv   ! Integrated water vapor (kg/m**2)
 349
 350    ! Locals:
 351    integer :: precipScreen    ! = 1: possible presence of precipitation does
 352                               !      not allow retrieval of IWV and CLW.
 353                               ! = 0: retrieval is possible
 354    real    :: IWV_alishouse   ! estimated total precipitable water, with cubic polynomial correction (=iwv)
 355    real    :: IWV_alishouse0  ! estimated total precipitable water, without cubic polynomial correction
 356    real    :: precipThresh
 357    real    :: tb19v,tb22v,tb37v,tb37h
 358    real    :: ciwv(0:4)
 359
 360    !  Initializations.
 361
 362    ciwv = (/232.89393,-0.148596,-1.829125,-0.36954,0.006193/)
 363
 364    tb19v = Tb(13)
 365    tb22v = Tb(14)
 366    tb37h = Tb(15)
 367    tb37v = Tb(16)
 368
 369    !  Compute IWV_alishouse.
 370
 371    IWV_alishouse0 = ciwv(0) + ciwv(1)*tb19v + ciwv(2)*tb22v +   &
 372          &          ciwv(3)*tb37v + ciwv(4)*tb22v**2
 373
 374    !  Apply Cubic Polynomial Correction to the CAL/VAL water vapour
 375    !  algorithm (G.W. Petty).
 376
 377    IWV_alishouse = -3.75 + 1.507*IWV_alishouse0 - 0.01933*IWV_alishouse0**2 +  &
 378          &         (2.191e-04)*IWV_alishouse0**3
 379
 380    !  Compute Precipitation Screen.
 381
 382    precipThresh = -11.7939 - 0.02727*tb37v + 0.09920*tb37h
 383    if ( precipThresh > 0.0 ) then
 384      precipScreen = 1
 385    else
 386      precipScreen = 0
 387    end if
 388
 389    !  Apply precipitation screen to IWV_alishouse (units are kg/m**2).
 390    !  Bounds now applied in cld_filter_fweng.ftn90 routinge.
 391
 392    if( precipScreen == 1 ) then
 393       IWV_alishouse = ssbg_rmisg
 394    end if
 395
 396    !  Store the IWV value.
 397
 398    iwv = IWV_alishouse
 399
 400  end subroutine compute_iwv_101
 401
 402  !--------------------------------------------------------------------------
 403  ! determ_tpw
 404  !--------------------------------------------------------------------------  
 405  subroutine determ_tpw(Tb, sType, seaIce, TPW)
 406    !:Purpose: To calculate total precipitable water (in mm).
 407
 408    implicit none
 409
 410    ! Arguments:
 411    real,    intent(in)  :: Tb(24)   ! Brightness temperature
 412    integer, intent(in)  :: sType    ! Surface type
 413    real,    intent(in)  :: seaIce   ! Sea ice coverage
 414    real,    intent(out) :: TPW      ! Total precipitable water (mm)
 415
 416    ! Locals:
 417    integer, parameter :: ocean=0
 418    real :: SCT
 419    real :: Tb19v, Tb22v, Tb37v, Tb85v
 420
 421    ! Extract 4 IMG channels from Tb
 422    Tb19v = Tb(13)
 423    Tb22v = Tb(14)
 424    Tb37v = Tb(16)
 425    Tb85v = Tb(17)
 426
 427    TPW = ssbg_rmisg
 428
 429    ! No TWP over land and sea ice
 430    if ( sType == ocean ) then
 431
 432      SCT = -182.7 + 0.75*Tb19v + 2.543*Tb22v - 0.00543*Tb22v*Tb22v - Tb85v
 433
 434      if ( abs(seaIce) < 70.0 ) then
 435        TPW = 232.89393 - 0.148596*Tb19v - 1.829125*Tb22v + 0.006193*Tb22v**2 - 0.36954*Tb37v
 436        TPW = -3.753 + 1.507*TPW - 0.01933*TPW**2 + 0.0002191*TPW**3
 437
 438        if ( TPW < 0.0 ) TPW = 0.0
 439        if ( TPW > 80.0 ) TPW = 80.0
 440      end if
 441
 442    end if
 443
 444  end subroutine determ_tpw
 445
 446  !--------------------------------------------------------------------------
 447  !  determ_sea_ice
 448  !--------------------------------------------------------------------------  
 449  subroutine determ_sea_ice(ocean, Ta, sType, seaIce, latitude)
 450    !:Purpose: To calculate sea ice cover (in %).
 451
 452    implicit none
 453
 454    ! Arguments:
 455    integer, intent(in)  :: ocean    ! Ocean surface type index
 456    real,    intent(in)  :: Ta(24)   ! Antenna temperature
 457    integer, intent(in)  :: sType    ! Surface type
 458    real,    intent(out) :: seaIce   ! Sea ice coverage
 459    real,    intent(in)  :: latitude ! Latitude of observation
 460
 461    ! Locals:
 462    real :: Ta19v, Ta19h, Ta22v, Ta37v, Ta37h, Ta85v, Ta85h
 463
 464    ! Extract 7 IMG channels from Ta
 465    Ta19v = Ta(13)
 466    Ta19h = Ta(12)
 467    Ta22v = Ta(14)
 468    Ta37v = Ta(16)
 469    Ta37h = Ta(15)
 470    Ta85v = Ta(17)
 471    Ta85h = Ta(18)
 472
 473    ! Calculate Sea Ice Coverage
 474    if ( sType == ocean ) then   ! Over Ocean
 475      if (latitude > 44.4 .or. latitude < -52.0 ) then
 476        seaIce = 91.9 - 2.994*Ta22v + 2.846*Ta19v - 0.386*Ta37v + 0.495*Ta85v &
 477                  + 1.005*Ta19h - 0.904*Ta37h
 478        if ( seaIce >= 70.0 ) then
 479          seaIce = 100.0
 480        else
 481          seaIce = 0.0
 482        end if
 483      else
 484        seaIce = 0.0
 485      end if
 486    else         ! Over Land, there is no sea ice!
 487      seaIce = ssbg_rmisg
 488    end if
 489
 490  end subroutine determ_sea_ice
 491
 492  !--------------------------------------------------------------------------
 493  !  determ_clw
 494  !--------------------------------------------------------------------------  
 495  subroutine determ_clw(algOption, Ta, Tb, sType, CLW, IWV, latitude)
 496    !:Purpose: To calculate cloud liquid water for a single data point (in kg/m**2).
 497
 498    !    Normally called when sType = 0 (open water).
 499    !    Also retrieves sea-ice to see if there is sea-ice at water point.
 500
 501    implicit none
 502
 503    ! Arguments:
 504    character(len=6), intent(in)    :: algOption ! Algorithm option (fweng, fwentz or nsun)
 505    real,             intent(in)    :: Ta(24)    ! Antenna temperature
 506    real,             intent(in)    :: Tb(24)    ! Brightness temperature
 507    integer,          intent(inout) :: sType     ! Surface type
 508    real,             intent(out)   :: CLW       ! Cloud liquid water (in kg/m**2)
 509    real,             intent(inout) :: IWV       ! Integrated water vapor
 510    real,             intent(in)    :: latitude  ! Latitude of observation
 511
 512    ! Locals:
 513    integer,    parameter :: isSeaIce = 1
 514    integer,    parameter :: ocean  = 0
 515    integer(4), parameter :: RT = 285
 516    real,       parameter :: clwLimit = 6.0
 517    real :: ALG1, ALG2, ALG3
 518    real :: seaIce
 519    real :: Ta19v, Ta19h, Ta22v, Ta37v, Ta37h, Ta85v, Ta85h
 520    real :: Tb19v, Tb22v, Tb37v
 521    real :: TPW
 522
 523    CLW = ssbg_rmisg
 524
 525    ! Extract 7 IMG channels from Ta and Tb
 526    Ta19v = Ta(13)
 527    Ta19h = Ta(12)
 528    Ta22v = Ta(14)
 529    Ta37v = Ta(16)
 530    Ta37h = Ta(15)
 531    Ta85v = Ta(17)
 532    Ta85h = Ta(18)
 533
 534    Tb19v = Tb(13)
 535    Tb22v = Tb(14)
 536    Tb37v = Tb(16)
 537
 538    ! Call determ_sea_ice to find the seaIce
 539    !  -- seaIce = 100.0  when sea ice >= 70%
 540    !             = 0.0    when sea ice  < 70%
 541    call determ_sea_ice(ocean, Ta, sType, seaIce, latitude)
 542
 543    ! Calculate CLW Over Ocean
 544    if ( (sType == ocean) .and. (seaIce /= 100.0) ) then
 545      ALG1 = ssbg_rmisg
 546      ALG2 = ssbg_rmisg
 547      ALG3 = ssbg_rmisg
 548
 549      if ( trim(algOption) == 'fweng' ) then
 550        ! Compute IWV using F. Weng algorithm.
 551        IWV = 232.89 - 0.1486*Tb19v - 0.3695*Tb37v - (1.8291 - 0.006193*Tb22v)*Tb22v
 552        if ( IWV < 0.0 ) IWV = 0.0
 553      end if
 554      if ( trim(algOption) == 'nsun') then
 555        call determ_tpw(Tb,sType,seaIce,TPW)
 556        IWV = TPW
 557      end if
 558
 559      if ( (Ta19v < RT) .and. (Ta22v < RT) ) then
 560        ALG1 = -3.20 * ( ALOG(290.0-Ta19v) - 2.80 - 0.42*ALOG(290.0-Ta22v) )          !TA
 561        ! ALG1 = -3.20 * ( ALOG(290.0-Tb19v) - 2.84 - 0.40*ALOG(290.0-Tb22v) )      !TB
 562      end if
 563
 564      if ( (Ta37v < RT) .and. (Ta22v < RT) ) then
 565        ALG2 = -1.66 * ( ALOG(290.0-Ta37v) - 2.90 - 0.349*ALOG(290.0-Ta22v) )   !TA
 566        ! ALG2 = -1.66 * ( ALOG(290.0-Tb37v) - 2.99 - 0.32*ALOG(290.0-Tb22v) )    !TB
 567      end if
 568
 569      if ( (Ta85h < RT) .and. (Ta22v < RT) ) then
 570        ALG3 = -0.44 * ( ALOG(290.0-Ta85h) + 1.60 - 1.354*ALOG(290.0-Ta22v) )     !TA
 571        ! ALG3 = -0.44 * ( ALOG(290.0-Tb85h) + 1.11 - 1.26*ALOG(290.0-Tb22v) )      !TB
 572      end if
 573
 574      if ( ALG1 > 0.70 ) then
 575        CLW = ALG1
 576      else if ( ALG2 > 0.28 ) then
 577        CLW = ALG2
 578      else if ( IWV < 30.0 ) then
 579        CLW = ALG3
 580      else
 581        CLW = ALG2
 582      end if
 583
 584      ! Verify CLW is within acceptable upper limit.
 585      if ( CLW > clwLimit ) CLW = ssbg_rmisg
 586
 587      ! Force negative CLW values to zero.
 588      if ( CLW < 0.0 .and. CLW /= ssbg_rmisg ) CLW = 0.0
 589
 590    else
 591      ! Sea Ice (>70%) detected from s/r determ_sea_ice but sType was 0 = waterobs (on call)
 592      CLW = -500.0
 593      IWV = ssbg_rmisg
 594      sType = isSeaIce
 595
 596    end if
 597
 598  end subroutine determ_clw
 599
 600  !--------------------------------------------------------------------------
 601  !  cld_filter_fweng
 602  !--------------------------------------------------------------------------  
 603  subroutine cld_filter_fweng(numObsToProcess, obsTb, algOption, waterObs, grossRej,  &
 604            &                 cloudObs, iwvReject, precipObs, rclw, riwv, iSatId,   &
 605            &                 obsLatitude, numSeaIceObs)
 606    !:Purpose: Compute the cloud liquid water (CLW) from SSMIS channels using the
 607    !           regression algorithm of Fuzhong Weng and Ninghai Sun.
 608    !           Retrieve CLW path from F16 SSMIS TDR data
 609
 610    implicit none
 611
 612    ! Arguments:
 613    integer,          intent(in)    :: numObsToProcess  ! Number of obs points to process
 614    real,             intent(in)    :: obsTb(:)         ! Brightness temperature of observations
 615    character(len=6), intent(in)    :: algOption        ! Algorithm option (fweng, fwentz or nsun)
 616    logical,          intent(inout) :: waterObs(:)      ! Open water identifier for each obs
 617    logical,          intent(in)    :: grossRej(:)      ! Logical array of obs with gross error (obs to reject)
 618    logical,          intent(inout) :: cloudObs(:)      ! Logical array of obs for which CLW > 0.01 kg/m**2 or with precipitations
 619    logical,          intent(inout) :: iwvReject(:)     ! Logical array of obs for which IWV > 80 kg/m**2
 620    logical,          intent(inout) :: precipObs(:)     ! Logical array of obs with precipitations (CLW missing)
 621    real,             intent(inout) :: rclw(:)          ! Real array of CLW
 622    real,             intent(inout) :: riwv(:)          ! Real array of integrated water vapor (IWV)
 623    integer,          intent(in)    :: iSatId           ! Satellite identifier
 624    real,             intent(in)    :: obsLatitude(:)   ! Observation latitudes
 625    integer,          intent(inout) :: numSeaIceObs     ! Number of observations with sea ice
 626
 627    ! Locals:
 628    real,    parameter :: iwvThresh = 80.0    ! Upper bound for IWV in kg/m**2
 629    integer, parameter :: ocean  = 0
 630    integer, parameter :: seaIce = 1
 631    integer            :: hiIndex
 632    integer            :: loIndex
 633    integer            :: obsIndex
 634    integer            :: sType
 635    real               :: clw
 636    real               :: iwv
 637    real               :: latitude
 638    real               :: F16TDR(ssbg_maxNumChan)
 639    real               :: remappedTa(ssbg_maxNumChan)
 640    real               :: Tb(ssbg_maxNumChan)
 641    real               :: zta(ssbg_mxval*ssbg_maxObsNum)
 642
 643    !---------------------------------------------------
 644
 645    ! Convert Tbs received from UKMO to Tas, by reversing Ta to Tb
 646    ! spillover correction applied in B. Bell's pre-processing.
 647    ! Missing Tbs are also checked for here.
 648    ! Apply to all obs of current record.
 649
 650    call ssmis_tb2ta(numObsToProcess,grossRej,obsTb,zta)
 651
 652    rclw(:) = ssbg_rmisg
 653    riwv(:) = ssbg_rmisg
 654
 655    loIndex = 1
 656    HEADER: do obsIndex = 1, numObsToProcess
 657
 658      hiIndex = obsIndex*ssbg_maxNumChan
 659      F16TDR(:) = zta(loIndex:hiIndex)
 660      latitude = obsLatitude(obsIndex)
 661      loIndex = hiIndex + 1
 662
 663      ! Obtain CLW and IWV for obs points over open ocean, and where
 664      ! Tb values have passed gross filter check.
 665      ! Initialize IWV and CLW to missing for all other cases.
 666
 667      clw = ssbg_rmisg
 668      iwv = ssbg_rmisg
 669
 670      if ( waterObs(obsIndex) .and. (.not. grossRej(obsIndex)) ) then
 671
 672        sType = ocean     ! Surface-type=Ocean
 673
 674        ! Call SSMIS TDR to SSMI TDR remapping subroutine
 675
 676        call f16tdr_remapping(iSatId, F16TDR, remappedTa)
 677
 678        ! Call SSM/I Ta to Tb conversion subroutine
 679
 680        if ( trim(algOption) == 'fweng' ) then
 681          call ssmi_ta2tb_fweng(remappedTa, Tb)
 682          ! IWV computed in determ_clw subroutine below.
 683        else if ( trim(algOption) == 'fwentz' ) then
 684          call ssmi_ta2tb_fwentz(remappedTa, Tb)
 685          ! Compute IWV using Alishouse and Petty,
 686          ! because it won't be computed in determ_clw.
 687          ! Missing value for IWV means possible precipitation
 688          ! and so CLW will not be computed
 689          call compute_iwv_101(Tb,iwv)
 690          if ( iwv == ssbg_rmisg ) precipObs(obsIndex) = .true.
 691        else if ( trim(algOption) == 'nsun' ) then
 692          call ssmi_ta2tb_fweng(remappedTa, Tb)
 693          ! IWV computed in determ_clw subroutine below.
 694        else
 695          write(*,*) ' cld_filter_fweng: Invalid algorithm option !! '
 696          call abort()
 697        end if
 698
 699        ! Call CLW retrieval algorithm subroutine.
 700        !  -- also computes and returns (output) IWV if algOption=fweng or nsun
 701        !  -- sType is also changed to seaIce value if sea-ice is detected
 702
 703        if ( trim(algOption) /= 'fwentz' ) then
 704          call determ_clw(algOption, remappedTa, Tb, sType, clw, iwv, latitude)
 705        else
 706          if ( .not. precipObs(obsIndex) ) then
 707            call determ_clw(algOption, remappedTa, Tb, sType, clw, iwv, latitude)
 708          end if
 709        end if
 710
 711        ! Check for newly detected sea-ice (sType changed)
 712
 713        if ( sType == seaIce ) numSeaIceObs = numSeaIceObs + 1
 714
 715        ! Reject obs with precipitation or cloud amount more than threshold.
 716        ! Also, reject obs if CLW is missing (undetermined)
 717        ! Set waterObs flag to false if deter_clw returns "seaIce" value (-500)
 718        if ( (clw > ssbg_clwThresh) .or. precipObs(obsIndex) ) cloudObs(obsIndex) = .true.
 719        if ( clw == ssbg_rmisg )  cloudObs(obsIndex) = .true.
 720        if ( clw == -500.0 ) then
 721           waterObs(obsIndex) = .false.
 722           clw = ssbg_rmisg
 723        end if
 724        ! Reject obs with IWV value more than threshold and set IWV to missing.
 725        ! First, set IWV values below zero to 0.0.
 726        if ( iwv < 0.0 .and. iwv /= ssbg_rmisg ) iwv = 0.0
 727        if ( iwv > iwvThresh ) then
 728          if ( .not. cloudObs(obsIndex) ) iwvReject(obsIndex) = .true.
 729          iwv = ssbg_rmisg
 730        end if
 731
 732        ! Store CLW and IWV
 733        if (ssbg_debug) then
 734          write(*,*)'cld_filter_fweng: CLOUD BY DETERM_CLW = ', clw
 735          write(*,*)'cld_filter_fweng: IWV BY DETERM_CLW = ', iwv
 736        end if
 737        rclw(obsIndex) = clw
 738        riwv(obsIndex) = iwv
 739
 740      end if
 741
 742    end do HEADER
 743
 744    120  format(' obsIndex ',2x,i3,2x,' clw ',f4.2,2x,' CLW threshold = ',f4.2,2x,' cloudObs ',l2)
 745    130  format(' obsIndex ',2x,i3,2x,' iwv ',f4.2,2x,' IWV threshold = ',f4.2,2x,' iwvReject ',l2)
 746
 747  end subroutine cld_filter_fweng
 748
 749  !--------------------------------------------------------------------------
 750  ! copy1Dimto2DimRealArray
 751  !--------------------------------------------------------------------------
 752  subroutine copy1Dimto2DimRealArray(oneDimArray, firstDim, secondDim, twoDimArray)
 753    !
 754    !:Purpose: copy 1D real array into 2D real array given firstDim and secondDim
 755    !
 756    implicit none
 757
 758    ! Arguments:
 759    integer, intent(in)    :: firstDim                         ! First dimension
 760    integer, intent(in)    :: secondDim                        ! Second dimension
 761    real,    intent(in)    :: oneDimArray(firstDim*secondDim)  ! 1D real array
 762    real,    intent(inout) :: twoDimArray(firstDim,secondDim)  ! 2D real array
 763    
 764    ! Locals:
 765    integer                :: firstDimIndex
 766    integer                :: productDimIndex
 767    integer                :: secondDimIndex
 768
 769    ! copy the original input 1D array to 2D array. The 2D arrays are used in this s/r.
 770    do secondDimIndex=1,secondDim
 771      do firstDimIndex=1,firstDim
 772        productDimIndex = (secondDimIndex-1)*firstDim + firstDimIndex 
 773        twoDimArray(firstDimIndex,secondDimIndex) = oneDimArray(productDimIndex)
 774      end do
 775    end do
 776
 777  end subroutine copy1Dimto2DimRealArray
 778
 779  !--------------------------------------------------------------------------
 780  ! copy1Dimto2DimIntegerArray
 781  !--------------------------------------------------------------------------
 782  subroutine copy1Dimto2DimIntegerArray(oneDimArray, firstDim, secondDim, twoDimArray)
 783    !
 784    !:Purpose: copy 1D integer array into 2D Integer array given firstDim and secondDim
 785    !
 786    implicit none
 787
 788    ! Arguments:
 789    integer, intent(in)    :: firstDim                         ! First dimension
 790    integer, intent(in)    :: secondDim                        ! Second dimension
 791    integer, intent(in)    :: oneDimArray(firstDim*secondDim)  ! 1D integer array
 792    integer, intent(inout) :: twoDimArray(firstDim,secondDim)  ! 2D integer array
 793    
 794    ! Locals:
 795    integer :: firstDimIndex
 796    integer :: productDimIndex 
 797    integer :: secondDimIndex 
 798
 799    ! copy the original input 1D array to 2D array. The 2D arrays are used in this s/r.
 800    do secondDimIndex=1,secondDim
 801      do firstDimIndex=1,firstDim
 802        productDimIndex = (secondDimIndex-1)*firstDim + firstDimIndex 
 803        twoDimArray(firstDimIndex,secondDimIndex) = oneDimArray(productDimIndex)
 804      end do
 805    end do
 806
 807  end subroutine copy1Dimto2DimIntegerArray
 808
 809  !------------------------------------------------------------------------------------
 810  ! bennartz
 811  !------------------------------------------------------------------------------------
 812  subroutine bennartz (ier, numObsToProcess, tb89, tb150, satZenithAngle, landSeaQualifier, scatL, scatW)
 813    !:Purpose: Compute the following parameters using 2 AMSU-B channels:
 814    !           - scattering index (over land and ocean).
 815    !           The two channels used are: 89Ghz, 150Ghz.
 816    !
 817    !           REFERENCES: Bennartz, R., A. Thoss, A. Dybbroe and D. B. Michelson, 
 818    !                       1999: Precipitation Analysis from AMSU, Nowcasting SAF, 
 819    !                       Swedish Meteorologicali and Hydrological Institute, 
 820    !                       Visiting Scientist Report, November 1999.
 821    !
 822    implicit none
 823
 824    ! Arguments:
 825    integer, intent(in)  :: numObsToProcess      ! Number of obs points to process
 826    integer, intent(out) :: ier(numObsToProcess) ! Error return code
 827    real,    intent(in)  :: tb89(:)              ! 89Ghz AMSU-B brightness temperature (K)
 828    real,    intent(in)  :: tb150(:)             ! 150Ghz AMSU-B brightness temperature (K)
 829    real,    intent(in)  :: satZenithAngle(:)    ! Satellite zenith angle (deg.)
 830    integer, intent(in)  :: landSeaQualifier(:)  ! Land/sea indicator (0=land; 1=ocean)
 831    real,    intent(out) :: scatL(:)             ! Scattering index over land
 832    real,    intent(out) :: scatW(:)             ! Scattering index over water
 833
 834    ! Locals:
 835    ! Notes: In the case where an output parameter cannot be calculated, the
 836    ! value of this parameter is the missing value, i.e. -99.
 837    integer :: obsIndex
 838
 839    ! ____1) Initialise output parameters:**    -------------------------------*
 840
 841    do obsIndex = 1, numObsToProcess
 842      scatL(obsIndex) = ssbg_realMissing
 843      scatW(obsIndex) = ssbg_realMissing
 844    end do
 845
 846    !____2) Validate input parameters:**    -----------------------------*
 847    do obsIndex = 1, numObsToProcess
 848      if ( tb89(obsIndex)               < 120.  .or.     &
 849            tb89(obsIndex)              > 350.  .or.     &
 850            tb150(obsIndex)             < 120.  .or.     &
 851            tb150(obsIndex)             > 350.  .or.     &
 852            satZenithAngle(obsIndex)    < -90.  .or.     &
 853            satZenithAngle(obsIndex)    >  90.  .or.     &
 854            landSeaQualifier(obsIndex)  <   0   .or.     &
 855            landSeaQualifier(obsIndex)  >   1        ) then
 856          ier(obsIndex) = 1
 857      else
 858          ier(obsIndex) = 0
 859      end if 
 860    end do
 861
 862    !____3) Compute parameters:**    ----------------------*
 863    do obsIndex = 1, numObsToProcess
 864      if ( ier(obsIndex) == 0 ) then
 865        if (landSeaQualifier(obsIndex) == 1 ) then
 866          scatW(obsIndex) = (tb89(obsIndex)-tb150(obsIndex)) -    &
 867                     (-39.2010+0.1104*satZenithAngle(obsIndex))
 868        else
 869          scatL(obsIndex) = (tb89(obsIndex)-tb150(obsIndex)) -    &
 870                     (0.158+0.0163*satZenithAngle(obsIndex))
 871        end if
 872      else if ( (ier(obsIndex) /= 0  ) .and. (obsIndex <= 100 ) .and. (ssbg_debug)) then
 873        write(*,*), 'bennartz: Input Parameters are not all valid: '
 874        write(*,*), ' obsIndex,tb89(obsIndex),tb150(obsIndex),satZenithAngle(obsIndex),landSeaQualifier(obsIndex) = ',     &
 875                   obsIndex,tb89(obsIndex),tb150(obsIndex),satZenithAngle(obsIndex),landSeaQualifier(obsIndex)
 876        write(*,*), ' ier(obsIndex),scatL(obsIndex),scatW(obsIndex)=',     &
 877                   ier(obsIndex),scatL(obsIndex),scatW(obsIndex)
 878      end if
 879    end do
 880
 881  end subroutine bennartz
 882
 883  !--------------------------------------------------------------------------
 884  ! ssbg_readGeophysicFieldsAndInterpolate
 885  !--------------------------------------------------------------------------
 886  subroutine ssbg_readGeophysicFieldsAndInterpolate(obsLatitude, obsLongitude, modelInterpTer)
 887    !
 888    !:Purpose: Reads geophysical model variable (GZ) and saves for the first time.
 889    !           GZ is geopotential height (GZ at surface = surface height in dam).
 890    !           Then interpolates those variables to observation location.
 891    !
 892    implicit none
 893
 894    ! Arguments:
 895    real,              intent(in)  :: obsLatitude(:)    ! Observation latitudes
 896    real,              intent(in)  :: obsLongitude(:)   ! Observation longitudes
 897    real, allocatable, intent(out) :: modelInterpTer(:) ! Filtered and interpolated topography (in m)
 898
 899    ! Locals:
 900    integer, parameter      :: mxLat = 5
 901    integer, parameter      :: mxLon = 5
 902    real,    parameter      :: dLat = 0.4
 903    real,    parameter      :: dLon = 0.6
 904    character(len=12)       :: etikxx
 905    character(len=1)        :: grtyp
 906    character(len=4)        :: nomvxx
 907    character(len=2)        :: typxx
 908    integer                 :: boxPointIndex
 909    integer                 :: boxPointNum
 910    integer                 :: dataIndex
 911    integer                 :: dataNum
 912    integer                 :: ezQkDef
 913    integer                 :: ezSetOpt
 914    integer                 :: gdllsval
 915    integer,           save :: gdgz                   ! topo interpolation param
 916    integer                 :: idum1, idum2, idum3, idum4
 917    integer                 :: idum5, idum6, idum7, idum8
 918    integer                 :: idum9, idum10, idum11, idum12
 919    integer                 :: idum13, idum14, idum15
 920    integer                 :: idum16, idum17, idum18
 921    integer                 :: ier
 922    integer                 :: ig1, ig2, ig3, ig4
 923    integer                 :: irec
 924    integer                 :: iUnGeo
 925    integer                 :: latIndex
 926    integer                 :: lonIndex
 927    integer                 :: ni, nj, nk
 928    integer                 :: nLat
 929    integer                 :: nLon
 930    integer                 :: nObsLat
 931    integer                 :: nObsLon
 932    logical,           save :: ifFirstCall = .true.   ! If .True. we read GL, GZ and MG
 933    real, allocatable, save :: GZ(:)                  ! Modele Topographie (GZ)
 934    real, allocatable       :: GZIntBox(:,:)
 935    real, allocatable       :: obsLatBox (:,:)
 936    real, allocatable       :: obsLonBox (:,:)
 937    real                    :: topoFact               ! Facteur x topo pour avoir des unites en metre
 938    real                    :: xLat
 939    real                    :: xLon
 940    ! External functions
 941    integer, external       :: fclos
 942    integer, external       :: fnom
 943    integer, external       :: fstfrm
 944    integer, external       :: fstinf
 945    integer, external       :: fstlir
 946    integer, external       :: fstouv
 947    integer, external       :: fstprm
 948    integer, external       :: ip1_all
 949
 950    ! STEP 1: CHECK if obsLatitude AND obsLongitude ARE SAME DIMENSION
 951    nObsLat = size(obsLatitude)
 952    nObsLon = size(obsLongitude)
 953    if (nObsLat /= nObsLon) then
 954      call utl_abort ('ssbg_readGeophysicFieldsAndInterpolate: OBSERVATION obsLatitude and obsLongitude should have SAME LENGTH')
 955    else 
 956      dataNum = nObsLat
 957    end if
 958
 959    ! STEP 2: READ GZ from the FST FILE
 960    if(ifFirstCall) then
 961      iUnGeo = 0
 962      ier = fnom(iUnGeo,'trlm_01','STD+RND+R/O',0)
 963      ier = fstouv(iUnGeo,'RND')
 964
 965      ! Using hybrid coordinates
 966      irec = fstinf(iUnGeo,ni,nj,nk,-1,' ',ip1_all(1.0,5),-1,-1,' ','GZ')
 967      if (irec < 0) then
 968        call utl_abort('ssbg_readGeophysicFieldsAndInterpolate: LA TOPOGRAPHIE EST INEXISTANTE')
 969      end if
 970      topoFact = 10.0  ! dam --> m
 971
 972      if (allocated(GZ)) deallocate(GZ)
 973      allocate ( GZ(ni*nj), stat=ier)
 974      if ( ier /= 0 ) then
 975        call utl_abort('ssbg_readGeophysicFieldsAndInterpolate: Allocation of array GZ failed')
 976      end if
 977      ier = fstlir(GZ,iUnGeo,ni,nj,nk,-1,' ',ip1_all(1.0,5),-1,-1,' ','GZ')
 978
 979      GZ(:) = GZ(:)*topoFact
 980
 981      ier = fstprm ( irec, idum1, idum2, idum3, idum4, &
 982                     idum5, idum6, idum7, idum8, idum9, idum10,  &
 983                     idum11, typxx, nomvxx, etikxx, grtyp, ig1, &
 984                     ig2, ig3, ig4, idum12, idum13, idum14,  &
 985                     idum15, idum16, idum17, idum18 )
 986      write (*,*) ' GRILLE GZ : ',grtyp,ni,nj, &
 987                     ig1,ig2,ig3,ig4
 988      ier  = ezSetOpt('INTERP_DEGREE','LINEAR')
 989      gdgz = ezQkDef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iUnGeo)
 990
 991      ier = fstfrm(iUnGeo)
 992      ier = fclos(iUnGeo)
 993      ifFirstCall = .false.
 994    end if
 995
 996    ! STEP 3:  Interpolation de la glace et le champ terre/mer du modele aux pts TOVS.
 997    ! N.B.: on examine ces champs sur une boite centree sur chaque obs.
 998    boxPointNum = mxLat*mxLon
 999    if(allocated(obsLatBox)) deallocate(obsLatBox)
1000    allocate (obsLatBox(boxPointNum, dataNum) , stat=ier)
1001    if(allocated(obsLonBox)) deallocate(obsLonBox)
1002    allocate (obsLonBox(boxPointNum, dataNum) , stat=ier)
1003    if(allocated(GZIntBox)) deallocate(GZIntBox)
1004    allocate (GZIntBox(boxPointNum, dataNum) , stat=ier)
1005
1006    nLat = (mxLat-1)/2
1007    nLon = (mxLon-1)/2
1008    do dataIndex = 1, dataNum
1009      boxPointIndex = 0
1010      do latIndex = -nLat, nLat
1011        xLat = obsLatitude(dataIndex) +latIndex*dLat
1012        xLat = max(-90.0,min(90.0,xLat))
1013        do lonIndex = -nLon, nLon
1014          boxPointIndex = boxPointIndex + 1
1015          xLon = obsLongitude(dataIndex) +lonIndex*dLon
1016          if ( xLon < -180. ) xLon = xLon + 360.
1017          if ( xLon >  180. ) xLon = xLon - 360.
1018          if ( xLon <    0. ) xLon = xLon + 360.
1019          obsLatBox(boxPointIndex,dataIndex) = xLat
1020          obsLonBox(boxPointIndex,dataIndex) = xLon
1021        end do
1022      end do
1023    end do
1024
1025    ier = gdllsval(gdgz,GZIntBox,GZ,obsLatBox,obsLonBox,boxPointNum*dataNum)
1026    if (ier < 0) then
1027      call utl_abort ('ssbg_readGeophysicFieldsAndInterpolate: ERROR in the interpolation of GZ')
1028    end if
1029
1030    if(allocated(modelInterpTer)) deallocate(modelInterpTer)
1031    allocate (modelInterpTer(dataNum) , stat=ier)
1032
1033    modelInterpTer(:) = 0.0
1034    do dataIndex = 1, dataNum
1035      if (ssbg_debug) then
1036        write(*,*), 'ssbg_readGeophysicFieldsAndInterpolate: infos'
1037        write(*,*), '   '
1038        write(*,*), ' dataIndex = ', dataIndex
1039        write(*,*), '   '
1040        write(*,*), ' obsLatitude, obsLongitude = ', obsLatitude(dataIndex), obsLongitude(dataIndex)
1041        write(*,*), '   '
1042        write(*,*), ' obsLatBox = '
1043        write(*,*),  (obsLatBox(boxPointIndex,dataIndex),boxPointIndex=1,boxPointNum)
1044        write(*,*), ' obsLonBox = '
1045        write(*,*),  (obsLonBox(boxPointIndex,dataIndex),boxPointIndex=1,boxPointNum)
1046        write(*,*), ' GZIntBox = '
1047        write(*,*),  (GZIntBox(boxPointIndex,dataIndex),boxPointIndex=1,boxPointNum)
1048      end if
1049      do boxPointIndex=1, boxPointNum
1050        modelInterpTer(dataIndex) = max(modelInterpTer(dataIndex),GZIntBox(boxPointIndex,dataIndex))
1051      end do
1052      if (ssbg_debug) then
1053        write(*,*), 'ssbg_readGeophysicFieldsAndInterpolate: modelInterpTer = ', modelInterpTer(dataIndex)
1054      end if
1055    end do
1056
1057  end subroutine ssbg_readGeophysicFieldsAndInterpolate
1058
1059  !--------------------------------------------------------------------
1060  !  land_ice_mask_ssmis
1061  !--------------------------------------------------------------------
1062  subroutine land_ice_mask_ssmis(numObsToProcess, obsLatitude, obsLongitude, landSeaQualifier, &
1063       &                         terrainType, waterObs)
1064    !:Purpose: Determine for each observation point the ice mask value from
1065    !           the binary file copied to the local work directory.
1066
1067    !           This may be a user-specified file or it is copied from
1068    !              /users/dor/afsi/sio/datafiles/data2/ade.maskice10
1069    !           The ice mask is updated every day at 00 UTC. The binary file has
1070    !           a resolution of 0.1 deg. Observations with an ice mask value of 0
1071    !           (=ice; land or sea=-1) are removed. The ice mask value also
1072    !           determines the terrain-type qualifier (element 13039) for each obs
1073    !           pt, which is required when writing output to boxed format.
1074
1075    !           Finally, this routine performs a land/ice consistency check using
1076    !           the MG and LG fields used by the model which produces the trial field.
1077    !           The purpose of this check is to remove obs that reside close to coasts
1078    !           or ice, and so whose TBs may be contaminated.
1079    !           For the GEM global model, any of the analysis files provides the MG
1080    !           field, while for the meso-global model a user-specified file is required
1081    !           to define MG. In either case, the GEM Global analysis file provides the
1082    !           LG field, and it is interpolated to the resolution of the model.
1083    !             - MG from climate file :     /data/gridpt/dbase/anal/glbeta2/DATE_000
1084    !             - LG from analysis file: ex. /data/gridpt/dbase/anal/glbpres2/DATE_000
1085
1086    !           In the application of this check, a 5x5 mesh, with spacing defined by rLatKm and
1087    !           rLonKm, is positioned with its center over an obs pt (2 grid pts on either side
1088    !           of the obs pt; size of mesh is equal to 4*rLatKm x 4*rLonKm). The values of MG
1089    !           and LG are evaluated at the grid points of this mesh. The maximum value of each
1090    !           determines whether the obs pt is too close to ice or land to be retained.
1091    !           **NOTE: the threshold value for MG has a very strong effect on the distance
1092    !                   from land that is permitted for an obs to be retained
1093
1094
1095    !      Maximum FOV             x---x---x---x---x     ^
1096    !         = 75km x 75km        |   |   |   |   |     |
1097    !         for Meso-sphere CHs  x---x---x---x---x     |
1098    !         = 74km x 47km        |   |   |   |   |     |
1099    !         for 19 GHz           x---x---o---x---x     | = 4*rLatKm
1100    !                              |   |   |   |   |     | = 4*40 km
1101    !                           ^  x---x---x---x---x     | = 160 km = 80 km north & south
1102    !                   rLatKm  |  |   |   |   |   |     |
1103    !                           v  x---x---x---x---x     v
1104    !                                          <--->
1105    !                                         rLonKm
1106    !
1107    !                              <--------------->
1108    !                                 = 4*rLonKm
1109    !                                 = 4*40 km
1110    !                                 = 160 km = 80 km east & west
1111
1112
1113    !               MG value = 1.0  ==>  LAND       MG value = 0.0  ==>  OCEAN
1114    !               LG value = 1.0  ==>  ICE        LG value = 0.0  ==>  NO ICE
1115
1116    !--------------------------------------------------------------------
1117    ! Variable Definitions
1118    ! --------------------
1119    ! fileMgLg         - input  -  name of file holding model MG and LG fields (external)
1120    ! numObsToProcess  - input  -  number of input obs pts in record
1121    ! obsLatitude      - input  -  array holding lat values for all obs pts in record
1122    ! obsLongitude     - input  -  array holding lon values for all obs pts in record
1123    ! landSeaQualifier - in/out -  array holding land/sea qualifier values for all obs
1124    !                              pts of record (0 = land, 1 = sea)
1125    ! terrainType      - output -  array holding terrain-type values for all obs pts
1126    !                              of current record
1127    ! waterObs         - output -  logical array identifying for each obs in current record
1128    !                              whether it is over open water, far from coast/ice
1129    ! iMask            -internal-  value determined by interpolating obs pt to
1130    !                              binary ice mask field
1131    !                                for land/sea: iMask = -1
1132    !                                for ice:      iMask = 0
1133    ! mxLat            -internal-  number of grid pts in lat. direction for mesh
1134    ! mxLon            -internal-  number of grid pts in lon. direction for mesh
1135    ! rLatKm           -internal-  spacing desired between mesh grid points in km
1136    !                              along lat. direction
1137    ! rLonKm           -internal-  spacing desired between mesh grid points in km
1138    !                              along lon. direction
1139    ! dLat             -internal-  spacing between mesh grid points along lon. direction
1140    !                              in degrees computed from rLatKm
1141    ! dLon             -internal-  spacing between mesh grid points along lon. direction
1142    !                              in degrees computed from rLonKm
1143    ! rKmPerDeg        -internal- distance in km per degree
1144    !                               = Earth radius * PI/180.0
1145    !                               = 6371.01 km * PI/180.0
1146    !                               = 111.195 km
1147    ! nLat,nLon        -internal-  used to define the lat/lon of the grid pts of mesh
1148    ! obsLatBox        -internal-  lat values at all grid pts of mesh for all obs pts
1149    ! obsLonBox        -internal-  lon values at all grid pts of mesh for all obs pts
1150    ! latMesh          -internal-  lat values at all grid pts of mesh for 1 obs pt
1151    ! lonMesh          -internal-  lon values at all grid pts of mesh for 1 obs pt
1152    ! mgIntOb          -internal-  interpolated MG values at all grid pts of mesh for 1 obs pt
1153    ! lgIntOb          -internal-  interpolated LG values at all grid pts of mesh for 1 obs pt
1154    ! mgIntrp          -internal-  max. interpolated MG value on mesh for all obs pts
1155    ! lgIntrp          -internal-  max. interpolated LG value on mesh for all obs pts
1156    ! MGthresh         -internal-  maximum allowable land fraction for obs to be kept
1157    ! LGthresh         -internal-  maximum allowable ice  fraction for obs to be kept
1158    !--------------------------------------------------------------------
1159    !
1160    implicit none
1161
1162    ! Arguments:
1163    integer,              intent(in)    :: numObsToProcess     ! Number of obs points to process
1164    real,                 intent(in)    :: obsLatitude(:)      ! Observation latitudes
1165    real,                 intent(in)    :: obsLongitude(:)     ! Observation longitudes
1166    integer,              intent(inout) :: landSeaQualifier(:) ! Land/sea indicator (0=land; 1=ocean)
1167    integer, allocatable, intent(out)   :: terrainType(:)      ! Terrain type qualifier
1168    logical, allocatable, intent(out)   :: waterObs(:)         ! Open water identifier for each obs
1169
1170    ! Locals:
1171    integer, parameter      :: mxLat=5
1172    integer, parameter      :: mxLon=5
1173    real,    parameter      :: LGthresh=0.01
1174    real,    parameter      :: MGthresh=0.01
1175    real,    parameter      :: pi=3.141592654
1176    real,    parameter      :: rKmPerDeg=111.195
1177    real,    parameter      :: rLatKm=40.0
1178    real,    parameter      :: rLonKm=40.0
1179    character(len=12)       :: etikxx
1180    character(len=1)        :: grtyp
1181    character(len=1)        :: grtyplg
1182    character(len=4)        :: nomvxx
1183    character(len=2)        :: typxx
1184    integer                 :: boxPointIndex
1185    integer                 :: ezQkDef
1186    integer                 :: ezSetOpt
1187    integer,           save :: gdId
1188    integer,           save :: gdIdlg
1189    integer                 :: gdllsval
1190    integer                 :: idum1, idum2, idum3, idum4
1191    integer                 :: idum5, idum6, idum7, idum8
1192    integer                 :: idum9, idum10, idum11, idum12
1193    integer                 :: idum13, idum14, idum15
1194    integer                 :: idum16, idum17, idum18
1195    integer                 :: ier
1196    integer                 :: ig1, ig2, ig3, ig4
1197    integer                 :: ig1lg, ig2lg, ig3lg, ig4lg
1198    integer                 :: iMask
1199    integer                 :: irec
1200    integer                 :: iUnGeo
1201    integer                 :: latIndex
1202    integer                 :: lonIndex
1203    integer                 :: ni, nj, nk
1204    integer                 :: nilg, njlg
1205    integer                 :: nLat
1206    integer                 :: nLon
1207    integer                 :: obsIndex
1208    logical,           save :: firstCall=.true.
1209    real, allocatable, save :: lg(:)
1210    real, allocatable, save :: mg(:)
1211    real, allocatable       :: latMesh(:)
1212    real, allocatable       :: lgIntOb(:)
1213    real, allocatable       :: lgIntrp(:)
1214    real, allocatable, save :: lonMesh(:)
1215    real, allocatable       :: mgIntOb(:)
1216    real, allocatable       :: mgIntrp(:)
1217    real, allocatable       :: obsLatBox(:,:)
1218    real, allocatable       :: obsLonBox(:,:)
1219    real                    :: dLat
1220    real                    :: dLon
1221    real                    :: rLatIndex
1222    real                    :: rLonIndex
1223    real                    :: xLat
1224    real                    :: xLatRad
1225    real                    :: xLon
1226    ! External functions
1227    integer, external       :: fclos
1228    integer, external       :: fnom
1229    integer, external       :: fstfrm
1230    integer, external       :: fstinf
1231    integer, external       :: fstlir
1232    integer, external       :: fstouv
1233    integer, external       :: fstprm
1234
1235    ! Allocate space for arrays holding values on mesh grid pts.
1236    call utl_reAllocate(latMesh, mxLat*mxLon)
1237    call utl_reAllocate(lonMesh, mxLat*mxLon)
1238    call utl_reAllocate(mgIntOb, mxLat*mxLon)
1239    call utl_reAllocate(lgIntOb, mxLat*mxLon)
1240    call utl_reAllocate(obsLatBox, mxLat*mxLon, numObsToProcess)
1241    call utl_reAllocate(obsLonBox, mxLat*mxLon, numObsToProcess)
1242    call utl_reAllocate(terrainType, numObsToProcess)
1243    call utl_reAllocate(waterObs, numObsToProcess)
1244
1245    if (firstCall) then
1246
1247      firstCall = .false.
1248
1249      ! Open FST file.
1250      iUnGeo = 0
1251      ier = fnom( iUnGeo,fileMgLg,'STD+RND+R/O',0 )
1252      ier = fstouv( iUnGeo,'RND' )
1253
1254      ! Read MG field.
1255      irec = fstinf(iUnGeo,ni,nj,nk,-1,' ',-1,-1,-1,' ' ,'MG')
1256      if ( irec <  0 ) then
1257        call utl_abort('land_ice_mask_ssmis: The MG field is MISSING')
1258      end if
1259
1260      call utl_reAllocate(mg, ni*nj)
1261
1262      ier = fstlir(mg,iUnGeo,ni,nj,nk,-1,' ',-1,-1,-1,' ','MG')
1263
1264      ier = fstprm(irec,idum1,idum2,idum3,idum4,idum5,idum6,idum7,idum8,    &
1265                   idum9,idum10,idum11,typxx,nomvxx,etikxx,grtyp,ig1,ig2,  &
1266                   ig3,ig4,idum12,idum13,idum14,idum15,idum16,idum17,      &
1267                   idum18)
1268
1269      ! Read LG field.
1270
1271      irec = fstinf(iUnGeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ' ,'LG')
1272      if ( irec <  0 ) then
1273        call utl_abort('land_ice_mask_ssmis: The LG field is MISSING ')
1274      end if
1275      call utl_reAllocate(lg, nilg*njlg)
1276      ier = fstlir(lg,iUnGeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ','LG')
1277
1278      ier = fstprm(irec,idum1,idum2,idum3,idum4,idum5,idum6,idum7,idum8,          &
1279          &        idum9,idum10,idum11,typxx,nomvxx,etikxx,grtyplg,ig1lg,ig2lg,  &
1280          &        ig3lg,ig4lg,idum12,idum13,idum14,idum15,idum16,idum17,        &
1281          &        idum18)
1282
1283      gdId = ezQkDef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iUnGeo)
1284      gdIdlg = ezQkDef(nilg,njlg,grtyplg,ig1lg,ig2lg,ig3lg,ig4lg,iUnGeo)
1285
1286      ier = fstfrm(iUnGeo)
1287      ier = fclos(iUnGeo)
1288
1289    end if ! firstCall
1290
1291
1292    ! For each obs pt, define a grid of artificial pts surrounding it.
1293
1294    nLat = (mxLat-1)/2
1295    nLon = (mxLon-1)/2
1296
1297    dLat = rLatKm / rKmPerDeg
1298    do obsIndex = 1, numObsToProcess
1299      boxPointIndex = 0
1300
1301      do latIndex = -nLat, nLat
1302        rlatIndex = float(latIndex)
1303        xLat = obsLatitude(obsIndex) + rLatIndex*dLat
1304        xLat = max( -90.0, min(90.0,xLat) )
1305        xLatRad = xLat*pi/180.0
1306
1307        do lonIndex = -nLon, nLon
1308          dLon = rLonKm / ( rKmPerDeg*cos(xLatRad) )
1309          rLonIndex = float(lonIndex)
1310          boxPointIndex = boxPointIndex + 1
1311          xLon = obsLongitude(obsIndex) + rLonIndex*dLon
1312          if ( xLon < -180. ) xLon = xLon + 360.
1313          if ( xLon >  180. ) xLon = xLon - 360.
1314          if ( xLon <    0. ) xLon = xLon + 360.
1315          obsLatBox(boxPointIndex,obsIndex) = xLat
1316          obsLonBox(boxPointIndex,obsIndex) = xLon
1317        end do
1318
1319      end do
1320    end do
1321
1322
1323    ! Interpolate values from MG and LG field to grid pts of mesh centred over each obs pt.
1324    ! Determine for each obs pt, the max interpolated MG and LG value within the box
1325    ! surrounding it.
1326
1327    ier  = ezSetOpt('INTERP_DEGREE','LINEAR')
1328 
1329    call utl_reAllocate(mgIntrp, numObsToProcess)
1330    call utl_reAllocate(lgIntrp, numObsToProcess)
1331
1332    mgIntrp(:) = 0.0
1333    lgIntrp(:) = 0.0
1334    do obsIndex = 1, numObsToProcess
1335 
1336      latMesh = obsLatBox(:,obsIndex)
1337      lonMesh = obsLonBox(:,obsIndex)
1338
1339      ier  = gdllsval(gdId,mgIntOb,mg,latMesh,lonMesh,mxLat*mxLon)
1340      ier  = gdllsval(gdIdlg,lgIntOb,lg,latMesh,lonMesh,mxLat*mxLon)
1341
1342      mgIntrp(obsIndex) = maxval(mgIntOb(:))
1343      lgIntrp(obsIndex) = maxval(lgIntOb(:))
1344
1345    end do
1346
1347    !  Initialize all obs as being over land and free of ice or snow.
1348    !  Determine which obs are over open water.
1349
1350    waterObs(:) = .false.   ! not over open water
1351    terrainType(:) = -1             ! no ice or snow
1352    HEADER: do obsIndex = 1, numObsToProcess
1353
1354      !    Determine for each obs pt, the value of iMask from the 0.1 deg binary file.
1355      !      - open land/sea: iMask = -1 (missing)
1356      !      - ice or snow:   iMask = 0  (from LG field --> binary ice mask)
1357      !    Define the terrain-type qualifier terrainType for each point based on the ice mask values.
1358      !       terrainType = -1  not defined (open water or snow free land)
1359      !                      0  sea-ice           (landSeaQualifier = 1)
1360      !                      1  snow-covered land (landSeaQualifier = 0)
1361
1362      if (lgintrp(obsIndex) < LGthresh ) then
1363        iMask = -1
1364      else 
1365        iMask = 0
1366      end if 
1367
1368      if ( iMask == 0 ) then  ! if ice or snow
1369        terrainType(obsIndex) = 1 - landSeaQualifier(obsIndex)
1370      end if
1371
1372      !    If iMask is -1 (no ice/snow), and this is consistent with the model ice
1373      !    LG value (ie. < LGthresh), and the max MG value indicates ocean (ie.
1374      !    < MGthresh), then this is a WATEROBS point.
1375
1376      if ( iMask == -1 .and. lgintrp(obsIndex) < LGthresh .and. mgintrp(obsIndex) < MGthresh ) then
1377      !!!! TO RUN WITHOUT BINARY ICE MASK CHECK (I.E. RELY ON LG ONLY TO REMOVE ICE PTS):
1378      !if ( lgintrp(obsIndex) < LGthresh .and. mgintrp(obsIndex) < MGthresh ) then
1379        waterObs(obsIndex) = .true.
1380      end if
1381
1382      !   Modify land/sea quailifier if not consistent with waterObs (applies to remote small
1383      !   islands that should be treated as sea points (for RTTOV)):
1384      !     -- if waterObs=true, land/sea qualifier should be "sea" value (1)
1385
1386      if ( waterObs(obsIndex) .and. (landSeaQualifier(obsIndex) == 0) ) landSeaQualifier(obsIndex) = 1
1387
1388    end do HEADER
1389
1390  end subroutine land_ice_mask_ssmis
1391
1392  !--------------------------------------------------------------------------
1393  ! wentz_sfctype_ssmis  
1394  !--------------------------------------------------------------------------
1395  subroutine wentz_sfctype_ssmis(numObsToProcess, obsLatitude, obsLongitude, landSeaQualifier)
1396    !:Purpose: Determine for each observation point the wentz surface value
1397    !           from the FST file wentz_surf.std.
1398
1399    !           This file has a resolution of 0.25 deg, and it discriminates
1400    !           between the following surface types:
1401    !                      a) land  (wentz value = 0)
1402    !                      b) ice   (wentz value = 4)
1403    !                      c) sea   (wentz value = 5)
1404    !                      d) coast (wentz value = 6)
1405    !           This information is used to define the land/sea qualifier
1406    !           (element 008012) for each obs pt, which is needed by 3D-Var.
1407    !           Also, the land/sea qualifier is used later to flag obs pts
1408    !           over land and coast.
1409    !
1410    !--------------------------------------------------------------------
1411    !  Variable Definitions
1412    !  --------------------
1413    ! numObsToProcess  - input  -  number of input obs pts in record
1414    ! obsLatitude      - input  -  array of size ssbg_maxObsNum holding lat values for obs pts
1415    !                              in record plus undefined pts
1416    ! obsLongitude     - input  -  array of size ssbg_maxObsNum holding lon values for obs pts
1417    !                              in record plus undefined pts
1418    ! landSeaQualifier - output -  array holding land/sea qualifier values for all obs pts of record
1419    ! xLat             -internal-  array of size numObsToProcess holding lat values for obs pts in record
1420    ! xLon             -internal-  array of size numObsToProcess holding lon values for obs pts in record
1421    ! lm               -internal-  array of size 1440x720 holding gridded wentz surface values
1422    ! wenTyp           -internal-  array of size numObsToProcess holding wentz surface values interpolated to obs pts
1423    !--------------------------------------------------------------------
1424    !
1425    implicit none
1426
1427    ! Arguments:
1428    integer,              intent(in)  :: numObsToProcess      ! Number of obs points to process
1429    real,                 intent(in)  :: obsLatitude(:)       ! Observation latitudes
1430    real,                 intent(in)  :: obsLongitude(:)      ! Observation longitudes
1431    integer, allocatable, intent(out) :: landSeaQualifier(:)  ! Land/sea indicator (0=land; 1=ocean)
1432
1433    ! Locals:
1434    character(len=12) :: etikxx
1435    character(len=1)  :: grtyp
1436    character(len=4)  :: nomvxx
1437    character(len=2)  :: typxx
1438    integer           :: ezQkDef
1439    integer           :: ezSetOpt
1440    integer           :: gdId
1441    integer           :: gdllsval
1442    integer           :: idum1, idum2, idum3, idum4
1443    integer           :: idum5, idum6, idum7, idum8
1444    integer           :: idum9, idum10, idum11, idum12
1445    integer           :: idum13, idum14, idum15
1446    integer           :: idum16, idum17, idum18
1447    integer           :: ier
1448    integer           :: ig1, ig2, ig3, ig4
1449    integer           :: irec
1450    integer           :: iUnIn
1451    integer           :: ni, nj, nk
1452    integer           :: obsIndex
1453    real, allocatable :: xLat(:)
1454    real, allocatable :: xLon(:)
1455    real, allocatable :: wenTyp(:)
1456    real, allocatable :: lm(:)
1457    ! External functions
1458    integer, external :: fclos
1459    integer, external :: fnom
1460    integer, external :: fstfrm
1461    integer, external :: fstinf
1462    integer, external :: fstlir
1463    integer, external :: fstouv
1464    integer, external :: fstprm
1465
1466    ! Open Wentz surface field if first call
1467    iUnIn = 0
1468    ier = fnom( iUnIn,fileWentz,'STD+RND+R/O',0 )
1469    ier = fstouv( iUnIn,'RND' )
1470
1471    irec = fstinf(iUnIn,ni,nj,nk,-1,' ',0,0,0,' ','LM')
1472    if ( irec <  0 ) then
1473      call utl_abort('wentz_sfctype_ssmis: The LM field is MISSING ')
1474    else
1475      call utl_reAllocate( lm, ni*nj )
1476      ier = fstlir(lm,iUnIn,ni,nj,nk,-1,' ',-1,-1,-1,' ','LM')
1477    end if
1478
1479    ier = fstprm(irec,idum1,idum2,idum3,idum4,idum5,idum6,idum7,idum8,    &
1480        &        idum9,idum10,idum11,typxx,nomvxx,etikxx,grtyp,ig1,ig2,  &
1481        &        ig3,ig4,idum12,idum13,idum14,idum15,idum16,idum17,      &
1482        &        idum18)
1483
1484    ! Re-define the grid type.
1485    !    - L-grid: IG's SHOULD define grid spacing over a REGIONAL
1486    !              domain. Here, IG's are =0 though.
1487
1488    !    - A-grid: is for global grid. See RPN documentation on
1489    !              grid-types for proper IG values.
1490    grtyp ='A'
1491
1492    ! Transfer data from obsLatitude,obsLongitude to xLat,xLon.
1493    ! Ensure proper range for lon values.
1494
1495    call utl_reAllocate( xLat, numObsToProcess)
1496    call utl_reAllocate( xLon, numObsToProcess)
1497    call utl_reAllocate( wenTyp, numObsToProcess)
1498    call utl_reAllocate( landSeaQualifier, numObsToProcess)
1499
1500    landSeaQualifier(:) = 0
1501    xLat(:) = obsLatitude(1:numObsToProcess)
1502    xLon(:) = obsLongitude(1:numObsToProcess)
1503    do obsIndex = 1, numObsToProcess
1504      if ( xLon(obsIndex) < 0. ) xLon(obsIndex) = xLon(obsIndex) + 360.
1505    end do
1506
1507    ! Interpolate values from LM field to all obs pts of record.
1508    ier  = ezSetOpt('INTERP_DEGREE','VOISIN')
1509    gdId = ezQkDef(ni,nj,grTyp,ig1,ig2,ig3,ig4,iUnIn)
1510    ier  = gdllsval(gdId,wenTyp,lm,xLat,xLon,numObsToProcess)
1511
1512    ! Define the land/sea qualifier for each point based on wentz surface values.
1513    do obsIndex = 1, numObsToProcess
1514      if ( wenTyp(obsIndex) == 0. .or. wenTyp(obsIndex) == 6. ) then
1515        ! wentz = land/coast --> land
1516        landSeaQualifier(obsIndex) = 0
1517      else if ( wenTyp(obsIndex) == 4. .or. wenTyp(obsIndex) == 5. ) then
1518        ! wentz = sea/sea-ice --> sea
1519        landSeaQualifier(obsIndex) = 1
1520      else
1521        call utl_abort('wentz_sfctype_ssmis: Unexpected Wentz value ')
1522      end if
1523    end do
1524    ier = fstfrm(iUnIn)
1525    ier = fclos(iUnIn)
1526
1527  end subroutine wentz_sfctype_ssmis
1528
1529  !--------------------------------------------------------------------------
1530  ! ssbg_computeSsmisSurfaceType
1531  !--------------------------------------------------------------------------
1532  subroutine ssbg_computeSsmisSurfaceType(obsSpaceData)
1533    !
1534    !:Purpose: Compute surface type element and update obsSpaceData.
1535    !
1536    implicit none
1537
1538    ! Arguments:
1539    type(struct_obs), intent(inout) :: obsSpaceData           ! ObsSpaceData object
1540
1541    ! Locals:
1542    real, parameter      :: satAzimuthAngle = 210.34
1543    real, parameter      :: satZenithAngle = 53.1
1544    integer, allocatable :: landSeaQualifier(:)
1545    integer              :: codtyp
1546    integer              :: headerIndex
1547    logical              :: ssmisDataPresent
1548    real                 :: obsLatitude(1)
1549    real                 :: obsLongitude(1)
1550
1551    write(*,*) 'ssbg_computeSsmisSurfaceType: Starting'
1552
1553    ssmisDataPresent=.false.
1554    call obs_set_current_header_list(obsSpaceData,'TO')
1555
1556    HEADER0: do
1557      headerIndex = obs_getHeaderIndex(obsSpaceData)
1558      if (headerIndex < 0) exit HEADER0
1559      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
1560      if ( tvs_isIdBurpInst(codtyp,'ssmis') ) then
1561        ssmisDataPresent = .true.
1562        exit HEADER0
1563      end if
1564    end do HEADER0
1565    
1566    if ( .not. ssmisDataPresent ) then
1567      write(*,*) 'WARNING: WILL NOT RUN ssbg_computeSsmisSurfaceType since no SSMIS DATA is found'
1568      return
1569    end if
1570
1571    call obs_set_current_header_list(obsSpaceData,'TO')
1572    HEADER1: do
1573      headerIndex = obs_getHeaderIndex(obsSpaceData)
1574      if (headerIndex < 0) exit HEADER1
1575      obsLatitude(1)  = obs_headElem_r( obsSpaceData, OBS_LAT, headerIndex )
1576      obsLongitude(1) = obs_headElem_r( obsSpaceData, OBS_LON, headerIndex )
1577      obsLongitude(1) = obsLongitude(1)*MPC_DEGREES_PER_RADIAN_R8
1578      if( obsLongitude(1) > 180. ) obsLongitude(1) = obsLongitude(1) - 360.
1579      obsLatitude(1)  = obsLatitude(1) *MPC_DEGREES_PER_RADIAN_R8
1580      call wentz_sfctype_ssmis(1, obsLatitude, obsLongitude, landSeaQualifier)
1581      call obs_headSet_i(obsSpaceData, OBS_STYP, headerIndex, landSeaQualifier(1))
1582      call obs_headSet_r(obsSpaceData, OBS_SZA, headerIndex,satZenithAngle)
1583      call obs_headSet_r(obsSpaceData, OBS_AZA, headerIndex, satAzimuthAngle)
1584    end do HEADER1
1585
1586    write(*,*) 'ssbg_computeSsmisSurfaceType: Finished'
1587
1588  end subroutine ssbg_computeSsmisSurfaceType
1589  
1590  !--------------------------------------------------------------------------
1591  ! ssbg_grossValueCheck
1592  !--------------------------------------------------------------------------
1593  subroutine ssbg_grossValueCheck(numObsToProcess, obsTb, obsTbMin, obsTbMax, grossRej)
1594    !
1595    !:Purpose: Check obsTb for values that are missing or outside physical limits.
1596    !
1597    implicit none
1598
1599    ! Arguments:
1600    integer,              intent(in)  :: numObsToProcess  ! Number of obs points to process
1601    real,                 intent(in)  :: obsTb(:)         ! Brightness temperature of observations
1602    real,                 intent(in)  :: obsTbMin         ! Min(obsTb) threshold for rejection
1603    real,                 intent(in)  :: obsTbMax         ! Max(obsTb) threshold for rejection
1604    logical, allocatable, intent(out) :: grossRej(:)      ! Logical array of obs with gross error (obs to reject)
1605
1606    ! Locals:
1607    integer :: hiIndex
1608    integer :: loIndex
1609    integer :: obsIndex
1610
1611    call utl_reAllocate(grossRej, numObsToProcess)
1612    
1613    grossRej(1:numObsToProcess) = .true.
1614    loIndex = 1
1615    do obsIndex = 1, numObsToProcess
1616
1617      hiIndex = obsIndex*ssbg_maxNumChan
1618      if ( all( obsTb(loIndex:hiIndex) > obsTbMin ) .and. all( obsTb(loIndex:hiIndex) < obsTbMax ) ) then
1619        grossRej(obsIndex) = .false.
1620      end if
1621      loIndex = hiIndex + 1
1622
1623    end do
1624
1625  end subroutine ssbg_grossValueCheck
1626
1627  !--------------------------------------------------------------------------
1628  ! ssbg_satqcSsmis
1629  !--------------------------------------------------------------------------
1630  subroutine ssbg_satqcSsmis(obsSpaceData, headerIndex, obsToReject)
1631    !
1632    !:Purpose: This program is applied as a first stage of processing to
1633    !           SSMIS data after it is received from UK MetOffice and
1634    !           organized into boxes by a program of Jose Garcia. The
1635    !           processing applied in this program includes:
1636    !             --  interpolate Wentz surface land mask to each obs pt
1637    !                 (nearest neighbour) to define land/sea qualifier (008012)
1638    !             --  interpolate binary ice mask to each obs pt (nearest
1639    !                 neighbour) to define terrain-type element (013039) where
1640    !                 0 = sea ice and 1 = snow-covered land
1641    !             --  interpolate model MG and LG fields to a grid surrounding each obs
1642    !                 pt to identify obs that are over open water, far from coast/ice
1643    !             --  identify those obs for which the UKMO rain marker
1644    !                 is ON (ie. 020029 = 1) indicating poor quality
1645    !             --  apply a cloud filter to identify those obs in cloudy regions;
1646    !                 write CLW and IWV (over ocean) to output BURP file
1647    !             --  re-write data to output BURP file while modifying flags
1648    !                 for those obs which are not over open water, or have been
1649    !                 identified in rain/cloud areas, or are of poor quality
1650    !             --  define satellite zenith angle element (007024) and add
1651    !                 this and land/sea qualifier and terrain-type elements
1652    !                 to the output file
1653    !
1654    implicit none
1655
1656    ! Arguments:
1657    type(struct_obs),     intent(inout) :: obsSpaceData           ! ObsSpaceData object
1658    integer,              intent(in)    :: headerIndex            ! Current header index
1659    logical, allocatable, intent(out)   :: obsToReject(:)         ! Observations that will be rejected
1660    
1661    ! Locals:
1662    ! arrays to get from obsspacedata
1663    character(len=9)           :: burpFileSatId
1664    integer, allocatable       :: landSeaQualifier(:)
1665    integer, allocatable       :: terrainType(:)
1666    integer, allocatable       :: ukRainObs(:)
1667    real,    allocatable       :: obsLatitude(:)
1668    real,    allocatable       :: obsLongitude(:)
1669    real,    allocatable       :: obsTb(:)
1670    real,    allocatable       :: rclw(:)
1671    real,    allocatable       :: riwv(:)
1672    real,    allocatable       :: satZenithAngle(:)
1673    real,    allocatable       :: scatW(:)
1674    integer, allocatable       :: ier(:)
1675    integer                    :: actualNumChannel
1676    integer                    :: bodyIndex
1677    integer                    :: bodyIndexBeg
1678    integer                    :: bodyIndexEnd
1679    integer                    :: codtyp
1680    integer                    :: currentChannelNumber
1681    integer                    :: headerCompt
1682    integer                    :: instr
1683    integer                    :: instrId
1684    integer                    :: obsIndex
1685    integer                    :: iSat
1686    integer                    :: iSatId
1687    integer,              save :: numLandObs
1688    integer,              save :: numUkBadObs
1689    integer,              save :: numGrossObs
1690    integer,              save :: numCloudyObs
1691    integer,              save :: numBadIWVObs
1692    integer,              save :: numPrecipObs
1693    integer,              save :: numTotFilteredObs
1694    integer,              save :: numLandScatObs
1695    integer,              save :: numSeaScatObs
1696    integer,              save :: numDryIndexObs
1697    integer,              save :: numSeaIceObs
1698    integer,              save :: numScatPrecipObs
1699    integer,              save :: numCloudsinObs
1700    integer,              save :: numWaterObs
1701    integer,              save :: numObsF16
1702    integer,              save :: numObsF17
1703    integer,              save :: numObsF18
1704    integer                    :: numObsToProcess
1705    integer                    :: platf
1706    integer                    :: platfId
1707    integer                    :: refPosObs
1708    integer                    :: sensorIndex
1709    logical, allocatable       :: cloudObs(:)
1710    logical, allocatable       :: grossRej(:)
1711    logical, allocatable       :: iwvReject(:)
1712    logical, allocatable       :: precipObs(:)
1713    logical, allocatable       :: rainDetectionUKMethod(:)
1714    logical, allocatable       :: waterObs(:)
1715    logical,              save :: ifFirstCall = .true.
1716    logical                    :: sensorIndexFound
1717    logical                    :: ssmisDataPresent
1718    real,    allocatable       :: amsubDrynessIndex(:)
1719    real,    allocatable       :: scatL(:)
1720    real,    allocatable       :: ztb91(:)
1721    real,    allocatable       :: ztb150(:)
1722    real,    allocatable       :: ztb_amsub3(:)
1723    real,    allocatable       :: ztb_amsub5(:)
1724
1725    ! Check if its ssmis data:
1726    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
1727    ssmisDataPresent = tvs_isIdBurpInst(codtyp,'ssmis')
1728
1729    if ( .not. ssmisDataPresent ) then
1730      write(*,*) 'WARNING: WILL NOT RUN ssbg_satqcSsmis since no SSMIS DATA is found'
1731      return
1732    end if
1733
1734    ! find tvs_sensor index corresponding to current obs
1735
1736    platf = obs_headElem_i( obsSpaceData, OBS_SAT, headerIndex )
1737    instr = obs_headElem_i( obsSpaceData, OBS_INS, headerIndex )
1738
1739    call tvs_mapSat( platf, platfId, iSat )
1740    call tvs_mapInstrum( instr, instrId )
1741
1742    sensorIndexFound = .false.
1743    HEADER0: do sensorIndex = 1, tvs_nsensors
1744      if ( platfId == tvs_platforms(sensorIndex)  .and. &
1745           iSat    == tvs_satellites(sensorIndex) .and. &
1746           instrId == tvs_instruments(sensorIndex) ) then
1747          sensorIndexFound = .true.
1748        exit HEADER0
1749      end if
1750    end do HEADER0
1751    if ( .not. sensorIndexFound ) call utl_abort('ssbg_satqcSsmis: sensor Index not found')
1752
1753    ! find actual Number of channels
1754    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
1755
1756    headerCompt = 1
1757    numObsToProcess = 1
1758
1759    ! Allocate intent out arrays 
1760
1761    call utl_reAllocate(obsToReject, numObsToProcess*actualNumChannel)
1762
1763    ! Allocate Fortran working arrays 
1764
1765    call utl_reAllocate(amsubDrynessIndex, numObsToProcess)
1766    call utl_reAllocate(cloudObs, numObsToProcess)
1767    call utl_reAllocate(grossRej, numObsToProcess)
1768    call utl_reAllocate(ier, numObsToProcess)
1769    call utl_reAllocate(iwvReject, numObsToProcess)
1770    call utl_reAllocate(precipObs, numObsToProcess)
1771    call utl_reAllocate(rainDetectionUKMethod, numObsToProcess)
1772    call utl_reAllocate(scatL, numObsToProcess)
1773    call utl_reAllocate(waterObs, numObsToProcess)
1774    call utl_reAllocate(ztb91, numObsToProcess)
1775    call utl_reAllocate(ztb150, numObsToProcess)
1776    call utl_reAllocate(ztb_amsub3, numObsToProcess)
1777    call utl_reAllocate(ztb_amsub5, numObsToProcess)
1778
1779    ! ELEMENTS FROM OBSSPACEDATA
1780    ! Allocate Header elements
1781    call utl_reAllocate(landSeaQualifier, numObsToProcess)
1782    call utl_reAllocate(obsLatitude, numObsToProcess)
1783    call utl_reAllocate(obsLongitude, numObsToProcess)
1784    call utl_reAllocate(rclw, numObsToProcess)
1785    call utl_reAllocate(riwv, numObsToProcess)
1786    call utl_reAllocate(satZenithAngle, numObsToProcess)
1787    call utl_reAllocate(scatW, numObsToProcess)
1788    call utl_reAllocate(terrainType, numObsToProcess)
1789    call utl_reAllocate(ukRainObs, numObsToProcess)
1790    ! Allocate Body elements
1791    call utl_reAllocate(obsTb, numObsToProcess*actualNumChannel)
1792    !initialization
1793    obsTb(:) = ssbg_realMissing
1794    riwv(:) = ssbg_realMissing
1795    
1796    ! Lecture dans obsspacedata
1797
1798    burpFileSatId                 = obs_elem_c    ( obsSpaceData, 'STID'  , headerIndex )
1799    rclw(headerCompt)             = obs_headElem_r( obsSpaceData, OBS_CLWO, headerIndex )
1800    ukRainObs(headerCompt)        = obs_headElem_i( obsSpaceData, OBS_RAIN, headerIndex )
1801    landSeaQualifier(headerCompt) = obs_headElem_i( obsSpaceData, OBS_STYP, headerIndex )
1802    terrainType(headerCompt)      = obs_headElem_i( obsSpaceData, OBS_TTYP, headerIndex )
1803    ! If terrain type is missing, set it to -1 for the QC programs
1804    if (terrainType(headerCompt) ==  99) terrainType(headerCompt) = -1
1805    obsLatitude(headerCompt)      = obs_headElem_r( obsSpaceData, OBS_LAT, headerIndex )
1806    obsLongitude(headerCompt)     = obs_headElem_r( obsSpaceData, OBS_LON, headerIndex )
1807    satZenithAngle(headerCompt)   = obs_headElem_r( obsSpaceData, OBS_SZA, headerIndex )
1808    ! Convert lat/lon to degrees
1809    obsLatitude(headerCompt)  = obsLatitude(headerCompt) *MPC_DEGREES_PER_RADIAN_R8
1810    obsLongitude(headerCompt) = obsLongitude(headerCompt)*MPC_DEGREES_PER_RADIAN_R8
1811    if( obsLongitude(headerCompt) > 180. ) obsLongitude(headerCompt) = obsLongitude(headerCompt) - 360.
1812
1813    ! To read body elements
1814    bodyIndexBeg = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
1815    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
1816
1817    BODY: do bodyIndex = bodyIndexbeg, bodyIndexEnd
1818      currentChannelNumber = nint(obs_bodyElem_r( obsSpaceData, OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
1819      if (obs_bodyElem_r( obsSpaceData,  OBS_BCOR, bodyIndex ) /= ssbg_rmisg) then
1820        obsTb(currentChannelNumber) = obs_bodyElem_r( obsSpaceData,  OBS_VAR, bodyIndex ) - obs_bodyElem_r( obsSpaceData,  OBS_BCOR, bodyIndex )
1821      else
1822        obsTb(currentChannelNumber) = obs_bodyElem_r( obsSpaceData,  OBS_VAR, bodyIndex )
1823      end if
1824    end do BODY
1825
1826    ! initialization
1827    if (ifFirstCall) then
1828      ifFirstCall = .false.
1829      numBadIWVObs = 0
1830      numCloudyObs = 0
1831      numDryIndexObs = 0
1832      numGrossObs = 0
1833      numLandObs = 0
1834      numLandScatObs = 0
1835      numObsF16 = 0
1836      numObsF17 = 0
1837      numObsF18 = 0
1838      numPrecipObs = 0
1839      numScatPrecipObs = 0
1840      numSeaIceObs = 0
1841      numSeaScatObs = 0
1842      numTotFilteredObs = 0
1843      numUkBadObs = 0
1844      numCloudsinObs = 0
1845      numWaterObs = 0
1846    end if 
1847    waterObs(:) = .false.
1848
1849    ! Record the total number of obs pts read for each satellite.
1850    ! Set the satellite ID number.
1851    select case (burpFileSatId(2:7) )
1852    case ( 'DMSP16' )
1853      numObsF16 = numObsF16 + numObsToProcess
1854      iSatId = 1
1855    case ( 'DMSP17' )
1856      numObsF17 = numObsF17 + numObsToProcess
1857      iSatId = 2
1858    case ( 'DMSP18' )
1859      numObsF18 = numObsF18 + numObsToProcess
1860      iSatId = 3
1861    end select
1862
1863    !--------------------------------------------------------------------
1864    ! Determine which obs pts are over open water (i.e NOT near coasts or
1865    ! over/near land/ice.
1866    !    - using model MG field, plus 0.1 deg ice mask and model LG field
1867    ! Also, define the terrain-type qualifier values (itt).
1868    ! Also, ensure land/sea qualifier (ilq) = 1 (sea) for points identified
1869    !   as open water by waterobs array.
1870    !--------------------------------------------------------------------
1871
1872    call land_ice_mask_ssmis(numObsToProcess, obsLatitude, obsLongitude, landSeaQualifier, &
1873                              terrainType, waterObs)
1874
1875    !--------------------------------------------------------------------
1876    ! Determine which obs pts have been flagged for rain (using UKMO method of identifying
1877    ! bad quality SSMIS data for DMSP16). First, initialize all obs as good.
1878    !--------------------------------------------------------------------
1879
1880    rainDetectionUKMethod(:) = .false.
1881    where ( ukRainObs == 1 ) rainDetectionUKMethod = .true.
1882
1883    !--------------------------------------------------------------------
1884    ! Check for values of TB that are missing or outside physical limits.
1885    ! **NOTE: REJECT ALL CHANNELS IF ONE IS FOUND TO BE BAD.
1886    !--------------------------------------------------------------------
1887
1888    grossRej(:)  = .false.
1889    call ssbg_grossValueCheck(numObsToProcess, obsTb, 50., 400., grossRej)
1890
1891    !--------------------------------------------------------------------
1892    ! Apply a CLW regression technique to determine cloudy obs pts.
1893    ! Apply a IWV regression technique to determine IWV obs out of bounds.
1894    ! Detect Sea_Ice percent from Tb(Ta) and set waterobs() flag to .FALSE. if
1895    !  amount > 70%
1896    ! To begin, assume that all obs are good.
1897    !--------------------------------------------------------------------
1898
1899    cloudobs(:)  = .false.
1900    iwvReject(:) = .false.
1901    precipobs(:) = .false.
1902
1903    ! --------------------------------------------------------------------
1904
1905    call cld_filter_fweng(numObsToProcess, obsTb, algOption, waterObs, grossRej, &
1906                          cloudObs, iwvReject, precipObs, rclw, riwv, iSatId,    &
1907                          obsLatitude, numSeaIceObs)
1908    !
1909    !  NOTE: rclw, riwv missing value is zmisg=9.9e09
1910    !    --> if ( (clw > clw_thresh) .or. precipobs(obsIndex) ) cloudobs(obsIndex) = .true.
1911    !    --> if ( clw == zmisg )  cloudobs(obsIndex) = .true.
1912    !
1913    !   Extract Tb for channels 18 (91H GHz)  and 8 (150H GHz) for Bennartz SI
1914    !   Extract Tb for channels 11 (AMSU-B 3) and 9 (AMSU-B 5) for Dryness Index (DI)
1915
1916    refPosObs = 1
1917    do obsIndex = 1, numObsToProcess
1918      ztb91(obsIndex)      = obsTb(refPosObs+17)
1919      ztb150(obsIndex)     = obsTb(refPosObs+7)
1920      ztb_amsub3(obsIndex) = obsTb(refPosObs+10)
1921      ztb_amsub5(obsIndex) = obsTb(refPosObs+8)
1922      refPosObs = obsIndex*ssbg_maxNumChan + 1
1923    end do
1924
1925    !---------------------------------------------------------------------------------
1926    ! Compute the Bennartz scattering index (SI) for each point from channels 8 and 18
1927    ! (detects precipitation affected AMSU-B-like channel radiances)
1928    ! -- using constant satellite zenith angle = 53.1
1929    ! -- SSMIS channel 18 (91.655 GHz) is used for AMSU-B like channel 1 (89 GHz)
1930    ! -- SSMIS channel  8 is used for AMSU-B like channel 2 (both at 150 GHz)
1931    !---------------------------------------------------------------------------------
1932
1933    call bennartz(ier, numObsToProcess, ztb91, ztb150, satZenithAngle, landSeaQualifier, & 
1934                 scatL, scatW)
1935
1936    !--------------------------------------------------------------------
1937    ! Compute the AMSU-B Dryness Index for all points
1938    !--------------------------------------------------------------------
1939
1940    where ( (ztb_amsub3 /= ssbg_realMissing) .and. (ztb_amsub5 /= ssbg_realMissing) )
1941      amsubDrynessIndex = ztb_amsub3 - ztb_amsub5
1942    elsewhere
1943      amsubDrynessIndex = ssbg_realMissing 
1944    end where
1945
1946    !--------------------------------------------------------------------
1947    ! Review all the checks previously made to determine which obs are to be accepted
1948    ! for assimilation and which are to be flagged for exclusion. First, initialize
1949    ! all obs for inclusion.
1950    !   ukBadObs()  = .true. if UKMetO QC sets "rain" flag: solar-intrusion or other anomaly
1951    !   grossRej()  = .true. if any channel had a gross error at the point
1952    !   cloudObs()  = .true. if CLW > 0.01 kg/m**2 or precip (over water)
1953    !   precipObs() = .true. if precip. detected (CLW=missing)
1954    !   waterObs()  = .true. if open water point (far from land or sea ice)
1955    !   iwvReject() = .true. if IWV > 80 kg/m**2
1956    !--------------------------------------------------------------------
1957
1958    obsToReject(:) = .false.
1959    HEADER1: do obsIndex = 1, numObsToProcess
1960      
1961      !      Reject all channels if UKMet rain flag or gross Tb error detected   
1962      if ( rainDetectionUKMethod(obsIndex) .or. grossRej(obsIndex) ) then
1963        !      "BAD" Observations       
1964        obsToReject(:) = .true.
1965        numTotFilteredObs = numTotFilteredObs + 1
1966
1967      else     ! if ( ukBadObs(obsIndex) .or. grossRej(obsIndex) )
1968        !      "GOOD" Observations       
1969        !-----------------------------------------------------------------------------------       
1970        !      OVER LAND OR SEA-ICE, 
1971        !         -- reject lower tropospheric channels and imager channels
1972        !         -- check Bennartz SI and DI for AMSU-B like channels
1973        !----------------------------------------------------------------------------------- 
1974        !        -- CLW/PRECIP not determined over land
1975        !        -- surface emissivity effects lower tropospheric channels     
1976        if  ( .not. waterObs(obsIndex) ) then
1977          obsToReject(1:ipc) = .true.    ! AMSU-A 3-5(6)
1978          obsToReject(12:18) = .true.    ! SSMI-like imager 1-7
1979          obsToReject(8:9) = .true.      ! AMSU-B 2,5
1980          !        Check BSI and DI for AMSU-B 3-4
1981          !         Bennartz Scattering Index
1982          !         Land point           
1983          if ( scatL(obsIndex) > 0.0  .and. scatL(obsIndex) /= ssbg_realMissing )   obsToReject(10:11) = .true.
1984          !         Sea-ice point 
1985          if ( scatW(obsIndex) > 40.0 .and. scatW(obsIndex) /= ssbg_realMissing )   obsToReject(10:11) = .true.
1986          !         Missing scattering index
1987          if ( scatW(obsIndex) == ssbg_realMissing .and. scatL(obsIndex) == ssbg_realMissing ) obsToReject(10:11) = .true.
1988          if ( any(obsToReject(10:11))) then
1989            numLandScatObs = numLandScatObs + 1
1990          end if
1991          !         Dryness index           
1992          if ( amsubDrynessIndex(obsIndex) > 0.0 ) then
1993            obsToReject(11) = .true.
1994          end if
1995          if ( amsubDrynessIndex(obsIndex) > -10.0 ) then
1996            obsToReject(10) = .true.
1997          end if
1998          if ( amsubDrynessIndex(obsIndex) > -20.0 ) then
1999            obsToReject(9) = .true.
2000          end if
2001          if ( amsubDrynessIndex(obsIndex) > -10.0 ) numDryIndexObs = numDryIndexObs + 1
2002          numTotFilteredObs = numTotFilteredObs + 1
2003        end if
2004        !-----------------------------------------------------------------------------------         
2005        !      OVER WATER, 
2006        !        -- reject tropospheric channels and imager channels if cloudy, 
2007        !           precip, or excessive IWV
2008        !        -- check Bennartz SI for AMSU-B like channels
2009        !-----------------------------------------------------------------------------------    
2010        if  ( waterObs(obsIndex) ) then
2011          if (cloudObs(obsIndex) .or. iwvReject(obsIndex))  then
2012            if ( iwvReject(obsIndex) ) then
2013              !              SSMI-like imager channels
2014              obsToReject(12:18) = .true.
2015              !             AMSU-A like channels 3-5(6)
2016              obsToReject(1:ipc) = .true.
2017            else  !  ----- CLOUDY OBSERVATION (OR MISSING CLOUD) ------
2018              !              SSMI-like imager channels
2019              obsToReject(12:18) = .true.
2020              !              AMSU-A like channels 3-5(6)
2021              if ( rclw(obsIndex) > clw_amsu_rej ) then
2022                obsToReject(2:ipc) = .true.
2023              end if
2024              if ( rclw(obsIndex) > clw_amsu_rej_ch3  ) then
2025                obsToReject(1) = .true.
2026              end if
2027            end if
2028            numTotFilteredObs = numTotFilteredObs + 1
2029          end if
2030          !        Check BSI for AMSU B channels 2-5 for all water obs
2031          !        Bennartz Scattering Index ( AMSU-B 2-5)
2032          !        Open water point
2033          if ( scatW(obsIndex) > 15.0 .and. scatW(obsIndex) /= ssbg_realMissing ) then
2034            obsToReject(8:11) = .true.
2035            numSeaScatObs = numSeaScatObs + 1
2036            if ( precipObs(obsIndex) ) numScatPrecipObs = numScatPrecipObs + 1
2037          end if
2038
2039        end if   ! if waterObs
2040
2041      end if   ! if ( ukBadObs(obsIndex) .or. grossRej(obsIndex) [end else] )
2042
2043      if ( .not. waterObs(obsIndex) ) numLandObs  = numLandObs  + 1
2044      if ( rainDetectionUKMethod(obsIndex) )  numUkBadObs = numUkBadObs + 1
2045      if ( grossRej(obsIndex) )  numGrossObs = numGrossObs + 1
2046      if ( cloudObs(obsIndex) )  numCloudsinObs = numCloudsinObs + 1
2047      if ( waterObs(obsIndex) )  numWaterObs = numWaterObs + 1
2048      if ( cloudObs(obsIndex)  .and. waterObs(obsIndex) ) numCloudyObs  = numCloudyObs  + 1
2049      if ( iwvReject(obsIndex) .and. waterObs(obsIndex) .and.   &
2050      &         (.not. cloudObs(obsIndex)) ) numBadIWVObs  = numBadIWVObs  + 1
2051      if ( precipObs(obsIndex) .and. waterObs(obsIndex) ) then
2052        numPrecipObs = numPrecipObs + 1
2053      end if
2054    end do HEADER1
2055      
2056    !-------------------------------------------------------------------------------
2057    ! Update ObsspaceData with the computed values of: ZLQ, ZTT, IWV and scatW
2058    !-------------------------------------------------------------------------------
2059    call obs_headSet_i(obsSpaceData, OBS_STYP, headerIndex, landSeaQualifier(1))
2060    call obs_headSet_i(obsSpaceData, OBS_TTYP, headerIndex, terrainType(1))
2061    if (scatW(1) /= ssbg_realMissing) then
2062      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, scatW(1))
2063    else
2064      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, MPC_missingValue_R4)
2065    end if
2066    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, rclw(1))
2067    call obs_headSet_r(obsSpaceData, OBS_IWV,  headerIndex, riwv(1))
2068
2069    if (headerIndex == obs_numHeader(obsSpaceData)) then
2070      write(*,*) '*******************************************************************'
2071      write(*,*) '******************* SATQC PROGRAM STATS****************************'
2072      write(*,*) '*******************************************************************'
2073      write(*,*)
2074      write(*,*) 'F16 obs =            ', numObsF16
2075      write(*,*) 'F17 obs =            ', numObsF17
2076      write(*,*) 'F18 obs =            ', numObsF18
2077      write(*,*)
2078      write(*,*) 'Land obs =           ', numLandObs
2079      write(*,*) 'UKMO bad obs =       ', numUkBadObs
2080      write(*,*) 'Gross rej obs =      ', numGrossObs
2081      write(*,*) 'Cloudy obs =         ', numCloudyObs
2082      write(*,*) 'Bad IWV obs =        ', numBadIWVObs
2083      write(*,*) 'Precip obs =         ', numPrecipObs
2084      write(*,*) 'Clouds in obs =      ', numCloudsinObs
2085      write(*,*) 'Obs with water =     ', numWaterObs
2086      write(*,*) '-------------------------------------------------------------------'
2087      write(*,*) 'Total filtered obs = ', numTotFilteredObs
2088      write(*,*) '-------------------------------------------------------------------'
2089      write(*,*) '             AMSU-B-Like Channel Filtering Report'
2090      write(*,*) 'Land/ice points with data flagged for Bennartz SI = ', numLandScatObs
2091      write(*,*) 'Water    points with data flagged for Bennartz SI = ', numSeaScatObs
2092      write(*,*) 'Dry obs =            ', numDryIndexObs
2093      write(*,*) 'Sea Ice obs =        ', numSeaIceObs
2094      write(*,*) '*******************************************************************'
2095    end if
2096
2097  end subroutine ssbg_satqcSsmis 
2098
2099  !--------------------------------------------------------------------------
2100  ! ssbg_updateObsSpaceAfterSatQc
2101  !--------------------------------------------------------------------------
2102  subroutine ssbg_updateObsSpaceAfterSatQc(obsSpaceData, headerIndex, obsToReject) 
2103    !
2104    !:Purpose:      Update obspacedata variables (obstTB and obs flags) after QC
2105    !
2106    implicit none
2107
2108    ! Arguments:
2109    type(struct_obs), intent(inout) :: obsSpaceData           ! ObsSpaceData object
2110    integer,          intent(in)    :: headerIndex            ! Current header index
2111    logical,          intent(in)    :: obsToReject(:)         ! Observations that will be rejected
2112
2113    ! Locals:
2114    integer, allocatable                :: obsFlags(:)
2115    integer, allocatable                :: obsGlobalFlag(:)
2116    integer, allocatable                :: satScanPosition(:)
2117    integer                             :: actualNumChannel
2118    integer                             :: bodyIndex
2119    integer                             :: bodyIndexBeg
2120    integer                             :: bodyIndexEnd
2121    integer                             :: channelIndex
2122    integer                             :: currentChannelNumber
2123    integer                             :: dataIndex
2124    integer                             :: headerCompt
2125    integer                             :: instr
2126    integer                             :: instrId
2127    integer                             :: iSat
2128    integer                             :: numObsToProcess
2129    integer                             :: obsIndex
2130    integer                             :: platf
2131    integer                             :: platfId
2132    integer                             :: sensorIndex
2133    logical                             :: sensorIndexFound
2134
2135    ! find tvs_sensor index corresponding to current obs
2136
2137    platf = obs_headElem_i( obsSpaceData, OBS_SAT, headerIndex )
2138    instr = obs_headElem_i( obsSpaceData, OBS_INS, headerIndex )
2139
2140    call tvs_mapSat( platf, platfId, iSat )
2141    call tvs_mapInstrum( instr, instrId )
2142
2143    sensorIndexFound = .false.
2144    HEADER0: do sensorIndex = 1, tvs_nsensors
2145      if ( platfId == tvs_platforms(sensorIndex)  .and. &
2146           iSat    == tvs_satellites(sensorIndex) .and. &
2147           instrId == tvs_instruments(sensorIndex) ) then
2148          sensorIndexFound = .true.
2149        exit HEADER0
2150      end if
2151    end do HEADER0
2152    if ( .not. sensorIndexFound ) call utl_abort('ssbg_updateObsSpaceAfterSatQc: sensor Index not found')
2153
2154    ! find actual Number of channels
2155    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
2156
2157    headerCompt = 1
2158    numObsToProcess = 1
2159    ! Allocate Header elements
2160    call utl_reAllocate(obsGlobalFlag, numObsToProcess)
2161    call utl_reAllocate(satScanPosition, numObsToProcess)
2162    
2163    ! Allocation
2164    call utl_reAllocate(obsFlags, numObsToProcess*actualNumChannel)
2165
2166    ! Read elements in obsspace
2167
2168    obsGlobalFlag(headerCompt)   = obs_headElem_i( obsSpaceData, OBS_ST1, headerIndex )
2169    satScanPosition(headerCompt) = obs_headElem_i( obsSpaceData, OBS_FOV, headerIndex )
2170
2171    bodyIndexBeg = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
2172    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
2173
2174    BODY2: do bodyIndex = bodyIndexbeg, bodyIndexEnd
2175      currentChannelNumber = nint(obs_bodyElem_r( obsSpaceData, OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
2176      obsFlags(currentChannelNumber) = obs_bodyElem_i( obsSpaceData, OBS_FLG, bodyIndex )
2177    end do BODY2
2178    
2179    !------------------------------------------------------------------ 
2180    ! 1 - Mark global flags if any obsToReject
2181    !------------------------------------------------------------------ 
2182    do obsIndex = 1, numObsToProcess
2183      if ( any(obsToReject) ) obsGlobalFlag(obsIndex) = ibset(obsGlobalFlag(obsIndex), 6)
2184    end do 
2185
2186    !------------------------------------------------------------------ 
2187    ! 2 - Mark obs flags for each value of obsToReject
2188    !------------------------------------------------------------------ 
2189    dataIndex = 0
2190    do obsIndex = 1, numObsToProcess
2191      do channelIndex = 1, actualNumChannel
2192        dataIndex = dataIndex+1 
2193        if (resetQc) obsFlags(dataIndex) = 0
2194        if (obsToReject(dataIndex)) obsFlags(dataIndex) = ibset(obsFlags(dataIndex),7)
2195      end do 
2196    end do
2197
2198    !-----------------------------------------------------------------
2199    !    Subtract 270 from FOV values (element 005043).
2200    !-----------------------------------------------------------------
2201    
2202    do obsIndex = 1, numObsToProcess
2203      if (satScanPosition(obsIndex) > 270) satScanPosition(obsIndex) = satScanPosition(obsIndex) - 270
2204    end do
2205 
2206    ! write elements in obsspace
2207    call obs_headSet_i(obsSpaceData, OBS_FOV, headerIndex, satScanPosition(1))
2208    call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex, obsGlobalFlag(1))
2209
2210    bodyIndexBeg = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
2211    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
2212
2213    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
2214      currentChannelNumber=nint(obs_bodyElem_r( obsSpaceData, OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
2215      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags(currentChannelNumber))
2216    end do BODY
2217
2218  end subroutine ssbg_updateObsSpaceAfterSatQc 
2219
2220  !--------------------------------------------------------------------------
2221  ! ssbg_inovqcSsmis
2222  !--------------------------------------------------------------------------
2223  subroutine ssbg_inovqcSsmis(obsSpaceData, headerIndex, flagsInovQc)
2224    !
2225    !:Purpose: Identify those observations in SSMIS data that have O-P
2226    !          values greater than a threshold proportional to known
2227    !          standard deviations (computed when the bias correction
2228    !          coefficients were derived). The flags of these observations
2229    !          are adjusted accordingly (ie bit 9 switched ON).
2230    !          Also,
2231    !              -- flag channels for systematic rejection based on UTIL
2232    !                 value in stats_*_errtot file or because flag bit 6 OFF
2233    !                 (uncorrected data)
2234    !              -- reject sets of AMSU-like channels based on O-P for
2235    !                 a single channel
2236    !              -- reject selected AMSU-like channels over land when
2237    !                 model surface height exceeds a specified limit
2238    !                 (topography check)
2239    !
2240    implicit none
2241
2242    ! Arguments:
2243    type(struct_obs),     intent(inout) :: obsSpaceData      ! ObsSpaceData object
2244    integer,              intent(in)    :: headerIndex       ! Current header index
2245    integer, allocatable, intent(out)   :: flagsInovQc(:)    ! Flags for assimilation/rejection of obs
2246
2247    ! Locals:
2248    character(len=9)     :: burpFileSatId            ! Satellite ID
2249    integer, allocatable :: obsChannels(:)           ! channel numbers
2250    integer, allocatable :: obsFlags(:)              ! data flags
2251    integer              :: actualNumChannel         ! actual Num channel
2252    integer              :: bodyIndex
2253    integer              :: bodyIndexBeg
2254    integer              :: bodyIndexEnd 
2255    integer              :: codtyp                   ! code type
2256    integer              :: channelIndex
2257    integer              :: currentChannelNumber
2258    integer              :: headerCompt
2259    integer              :: instr
2260    integer              :: instrId
2261    integer              :: iSat
2262    integer              :: numObsToProcess          ! Number of obs points to process
2263    integer              :: platf
2264    integer              :: platfId
2265    integer              :: sensorIndex              ! find tvs_sensor index corresponding to current obs
2266    logical              :: sensorIndexFound
2267    logical              :: ssmisDataPresent
2268    real   , allocatable :: modelInterpTer(:)        ! topo in standard file interpolated to obs point
2269    real   , allocatable :: obsLatitude(:)           ! obs. point latitude
2270    real   , allocatable :: obsLongitude(:)          ! obs. point longitude
2271    real   , allocatable :: ompTb(:)                 ! OMP values
2272
2273    !-------------------------------------------------------------------------
2274    ! 1) INOVQC begins
2275    !-------------------------------------------------------------------------
2276
2277    ! Check if its ssmis data:
2278    ssmisDataPresent = .false.
2279    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
2280    if ( tvs_isIdBurpInst(codtyp,'ssmis') ) then
2281      ssmisDataPresent = .true.
2282    end if
2283
2284    if ( .not. ssmisDataPresent ) then
2285      write(*,*) 'WARNING: WILL NOT RUN ssbg_inovqcSsmis since no SSMIS DATA were found'
2286      return
2287    end if
2288
2289    ! find tvs_sensor index corresponding to current obs
2290
2291    platf = obs_headElem_i( obsSpaceData, OBS_SAT, headerIndex )
2292    instr = obs_headElem_i( obsSpaceData, OBS_INS, headerIndex )
2293
2294    call tvs_mapSat( platf, platfId, iSat )
2295    call tvs_mapInstrum( instr, instrId )
2296
2297    sensorIndexFound = .false.
2298    HEADER1: do sensorIndex = 1, tvs_nsensors
2299      if ( platfId == tvs_platforms(sensorIndex)  .and. &
2300           iSat    == tvs_satellites(sensorIndex) .and. &
2301           instrId == tvs_instruments(sensorIndex) ) then
2302        sensorIndexFound = .true.
2303        exit HEADER1
2304      end if
2305    end do HEADER1
2306    if ( .not. sensorIndexFound ) call utl_abort('ssbg_inovqcSsmis: sensor Index not found')
2307
2308    !--------------------------------------------------------------------
2309    ! 2) Allocating arrays
2310    !--------------------------------------------------------------------
2311
2312    ! find actual Number of channels
2313    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
2314
2315    headerCompt = 1
2316    numObsToProcess = 1
2317
2318    ! ELEMENTS FROM OBSSPACEDATA
2319    ! Allocate header elements
2320    call utl_reAllocate(obsLatitude, numObsToProcess)
2321    call utl_reAllocate(obsLongitude, numObsToProcess)
2322    ! Allocate Body elements
2323    call utl_reAllocate(obsChannels, numObsToProcess*actualNumChannel)
2324    call utl_reAllocate(obsFlags, numObsToProcess*actualNumChannel)
2325    call utl_reAllocate(ompTb, numObsToProcess*actualNumChannel)
2326    ! Allocate intent out array
2327    call utl_reAllocate(flagsInovQc, numObsToProcess*actualNumChannel)
2328
2329    ! Lecture dans obsspacedata
2330    burpFileSatId             = obs_elem_c    ( obsSpaceData, 'STID' , headerIndex )
2331    obsLatitude(headerCompt)  = obs_headElem_r( obsSpaceData, OBS_LAT, headerIndex )
2332    obsLongitude(headerCompt) = obs_headElem_r( obsSpaceData, OBS_LON, headerIndex )
2333
2334    bodyIndexBeg = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
2335    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
2336
2337    BODY: do bodyIndex = bodyIndexbeg, bodyIndexEnd
2338      currentChannelNumber = nint(obs_bodyElem_r( obsSpaceData, OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
2339      ompTb(currentChannelNumber)    = obs_bodyElem_r( obsSpaceData, OBS_OMP, bodyIndex )
2340      obsFlags(currentChannelNumber) = obs_bodyElem_i( obsSpaceData, OBS_FLG, bodyIndex )
2341    end do BODY
2342
2343    do channelIndex=1, actualNumChannel
2344      obsChannels(channelIndex) = channelIndex+tvs_channelOffset(sensorIndex)
2345    end do
2346
2347    ! Convert lat/lon to degrees
2348    obsLatitude(headerCompt)  = obsLatitude(headerCompt) *MPC_DEGREES_PER_RADIAN_R8
2349    obsLongitude(headerCompt) = obsLongitude(headerCompt)*MPC_DEGREES_PER_RADIAN_R8
2350    if( obsLongitude(headerCompt) > 180. ) obsLongitude(headerCompt) = obsLongitude(headerCompt) - 360.
2351
2352    !--------------------------------------------------------------------
2353    ! 3) Extract model topography from the input GEO file and interpolate
2354    !    to all observation points in the box
2355    !--------------------------------------------------------------------
2356
2357    call ssbg_readGeophysicFieldsAndInterpolate(obsLatitude, obsLongitude, modelInterpTer)
2358
2359    !--------------------------------------------------------------------
2360    ! 4) Perform quality control on the data, checking O-P and topography
2361    !--------------------------------------------------------------------
2362
2363    call check_stddev(obsChannels, ompTb, flagsInovQc, actualNumChannel, numObsToProcess, &
2364        &             sensorIndex, burpFileSatId, obsFlags)
2365
2366    call check_topo(modelInterpTer, flagsInovQc, actualNumChannel, numObsToProcess)
2367
2368  end subroutine ssbg_inovqcSsmis
2369
2370  !--------------------------------------------------------------------------
2371  ! check_stddev
2372  !--------------------------------------------------------------------------
2373  subroutine check_stddev(obsChannels, ompTb, flagsInovQc, actualNumChannel, numObsToProcess, &
2374                          sensorIndex, burpFileSatId, obsFlags)
2375    !
2376    !:Purpose: Perform quality control on the radiances by analysing the
2377    !           magnitude of the residuals.
2378
2379    !------------------------------------------------------------------
2380    ! Variable Definitions:
2381    ! ---------------------
2382    !   obsChannels      - input  -  channel numbers (residuals)
2383    !   ompTb            - input  -  radiance residuals (o-p)
2384    !   obsFlags         - input  -  radiance data flags (bit 7 on for data that
2385    !                                will not be assimilated)
2386    !   flagsInovQc      - output -  quality contol indicator for each channel of each
2387    !                                observation point
2388    !                                =0  ok
2389    !                                =1  not checked because FLAG bit 7 ON,
2390    !                                =2  reject by UTIL value or FLAG bit 6 OFF (data not bias corrected),
2391    !                                =3  reject by rogue check,
2392    !                                =4  reject by both UTIL value and rogue check.
2393    !                                ==> individual channels may be rejected from each obs pt
2394    !   actualNumChannel - input  -  number of residual channels
2395    !   numObsToProcess  - input  -  number of groups of NVAL*NELE
2396    !   sensorIndex      - input  -  number of satellite (index # --> 1-nsat)
2397    !   burpFileSatId    - input  -  identificateur du satellite
2398    !   rogueFac         -internal-  constant which determines the severity of the
2399    !                                quality control applied to the O-P magnitude
2400    !                                for each channel
2401    !   productRogueSTD  -internal-  product of rogueFac and standard deviation for
2402    !                                each channel
2403    !   oer_tovutil      -external-  UTIL values read from the total error statistics file
2404    !                                0 = blacklisted channel
2405    !                                1 = assimilated channel
2406    !   oer_toverrst     -external-  standard deviation statistics read from the total
2407    !                                error statistics file
2408    !
2409    implicit none
2410
2411    ! Arguments:
2412    integer,          intent(in)  :: obsChannels(:)   ! Channel numbers
2413    real   ,          intent(in)  :: ompTb(:)         ! Radiance residuals
2414    integer,          intent(out) :: flagsInovQc(:)   ! Flags for assimilation/rejection of obs
2415    integer,          intent(in)  :: actualNumChannel ! Number of channels
2416    integer,          intent(in)  :: numObsToProcess  ! Number of obs points to process
2417    integer,          intent(in)  :: sensorIndex      ! Identification number of satellite
2418    character(len=9), intent(in)  :: burpFileSatId    ! Satellite identification in BURP file
2419    integer,          intent(in)  :: obsFlags(:)      ! Radiance data flags
2420
2421    ! Locals:
2422    real, parameter :: factorCh1 = 2.0 ! factor for channel 1 O-P for rejection of channels 1-4
2423    real, parameter :: maxOmpCh8 = 5.0 ! max Abs(O-P) for channel 8 for rejection of channels 8-11 (units = K)
2424    integer         :: chanIndex
2425    integer         :: chanIndex1
2426    integer         :: chanIndex4
2427    integer         :: chanIndex8
2428    integer         :: chanIndex11
2429    integer         :: chanIndexLast
2430    integer         :: channelNumber
2431    integer         :: obsChanIndex
2432    integer         :: obsIndex
2433    real            :: productRogueSTD
2434    real            :: rogueFac(ssbg_maxNumChan)
2435
2436    !  Initialization. Assume all observations are to be kept.
2437
2438    ! rogueFac(1:7) should be consistent with bgck.satqc_amsua.f (for AMSUA ch. 3-10)
2439    ! rogueFac(8:11) should be consistent with bgck.satqc_amsub.f (for AMSUB ch. 2-5)
2440    ! rogueFac(12:18) should be consistent with ssmi_inovqc.ftn90 (for SSMI ch. 1-7)
2441    ! rogueFac(19:24) are for upper-atmospheric channels (strato/meso-sphere)
2442    rogueFac = (/2.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 2.0, &
2443         &       4.0, 4.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, &
2444         &       2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0/)
2445
2446    flagsInovQc(:) = 0
2447
2448    !  Loop through all observation points in the current record and check
2449    !  first whether a channel is to be systematically removed and second
2450    !  whether the O-P value for each channel falls within the acceptable
2451    !  range defined by rogueFac and oer_toverrst (sensorIndex identifies the satellite).
2452
2453    !  *** Only do check for data that could be assimilated ***
2454
2455    !  Also, apply multi-channel rejections for AMSU-like channels using
2456    !  O-P values from channels 1 and 8.
2457    !     If Abs(O-P) chan. 1 (AMSU-A 3) > 2*errtot, reject ch. 1-4 (AMSU-A 3-6)
2458    !     If Abs(O-P) chan. 8 (AMSU-B 2) > 5K, reject ch. 8-11 (AMSU-B 2-5)
2459
2460    !--------------------------------------------------------------------------
2461    !  1. Rogue check O-P for all data flagged for assimilation
2462    !--------------------------------------------------------------------------
2463
2464    HEADER0: do obsChanIndex = 1, numObsToProcess*actualNumChannel
2465
2466      channelNumber = obsChannels(obsChanIndex)
2467      productRogueSTD = rogueFac(channelNumber) * oer_toverrst(channelNumber,sensorIndex)
2468
2469      if ( .not. btest(obsFlags(obsChanIndex),7) ) then
2470
2471        if ( oer_tovutil(channelNumber,sensorIndex) == 0 .or. (.not. btest(obsFlags(obsChanIndex),6)) ) then
2472
2473          ! systematic rejection of this channel
2474          flagsInovQc(obsChanIndex) = 2
2475          if ( abs( ompTb(obsChanIndex) ) >= productRogueSTD ) then
2476            ! rogue check failure as well
2477            flagsInovQc(obsChanIndex) = 4
2478          end if
2479
2480        else
2481
2482          ! keep channel, now perform rogue check
2483          if ( abs( ompTb(obsChanIndex) ) >= productRogueSTD ) then
2484            flagsInovQc(obsChanIndex) = 3
2485          end if
2486
2487        end if
2488
2489      else
2490
2491        flagsInovQc(obsChanIndex) = 1
2492
2493      end if
2494
2495      !  Keep statistics of obs rejected by rogue check.
2496      if ( (flagsInovQc(obsChanIndex) > 2) .and. (ssbg_debug) ) then
2497        write(*,*) 'check_stddev: '
2498        write(*,*) burpFileSatId(2:9),' Rogue check reject: ',   &
2499             &  ' Obs = ',obsChanIndex,' Channel = ',channelNumber,         &
2500             &  ' Check value = ',productRogueSTD,             &
2501             &  ' O-P = ',ompTb(obsChanIndex)
2502      end if
2503
2504    end do HEADER0
2505
2506    !----------------------------------------------------------------------------------
2507    ! 2. Apply additional criteria for rejections of multiple AMSU-like channels using
2508    !    Channels 1 and 8 Abs(O-P) (for data that could be assimilated [bit 7 off])
2509    !----------------------------------------------------------------------------------
2510
2511    ! Loop over observation points (actualNumChannel = num. channels [24])
2512    HEADER1: do obsIndex = 1, numObsToProcess
2513
2514      chanIndexLast = obsIndex*actualNumChannel            ! index of ch.24 obs
2515      chanIndex1    = chanIndexLast - (actualNumChannel-1) ! index of ch.1 obs
2516      chanIndex4    = chanIndex1 + 3                       ! index of ch.4 obs
2517      chanIndex8    = chanIndex4 + 4                       ! index of ch.8 obs
2518      chanIndex11   = chanIndex8 + 3                       ! index of ch.11 obs
2519
2520      ! AMSU-A like ch. 1-4
2521      if ( sum(flagsInovQc(chanIndex1:chanIndex4)) /= 4 ) then
2522        productRogueSTD = factorCh1 * oer_toverrst(1,sensorIndex)
2523        if ( abs(ompTb(chanIndex1)) >= productRogueSTD ) then
2524          do chanIndex = chanIndex1, chanIndex4
2525            channelNumber = obsChannels(chanIndex)
2526            if (flagsInovQc(chanIndex) /= 1) flagsInovQc(chanIndex) = max(flagsInovQc(chanIndex),3)
2527          end do
2528        end if
2529      end if
2530
2531      ! AMSU-B like ch. 8-11
2532      if ( sum(flagsInovQc(chanIndex8:chanIndex11)) /= 4 ) then
2533        productRogueSTD = maxOmpCh8
2534        if ( abs(ompTb(chanIndex8)) >= productRogueSTD ) then
2535          do chanIndex = chanIndex8, chanIndex11
2536            channelNumber = obsChannels(chanIndex)
2537            if (flagsInovQc(chanIndex) /= 1) flagsInovQc(chanIndex) = max(flagsInovQc(chanIndex),3)
2538          end do
2539        end if
2540      end if
2541
2542    end do HEADER1
2543
2544  end subroutine check_stddev
2545
2546  !--------------------------------------------------------------------------
2547  ! check_topo
2548  !--------------------------------------------------------------------------
2549  subroutine check_topo(modelInterpTer, flagsInovQc, actualNumChannel, numObsToProcess)
2550
2551    !:Purpose: Perform rejection of observations for selected channels based
2552    !           on model surface height (for channels assimilated over land)
2553
2554    !           -- for a single satellite (burpFileSatId,sensorIndex)
2555    !           -- for a single box (nt observations)
2556
2557    !------------------------------------------------------------------
2558    ! Variable Definitions:
2559    ! ---------------------
2560    !   modelInterpTer    - input  -  model surface height (m) at each observation point
2561    !   flagsInovQc       - in/out -  quality control indicator for each channel of each
2562    !                                 observation point
2563    !                                 -- on INPUT
2564    !                                 =0  ok
2565    !                                 =1  not checked because FLAG bit 7 ON,
2566    !                                 =2  reject by UTIL value or bit 6 OFF (data not bias corrected),
2567    !                                 =3  reject by rogue check,
2568    !                                 =4  reject by both UTIL value and rogue check.
2569    !                                 -- on OUTPUT,
2570    !                                 =0  ok
2571    !                                 =1  not checked because FLAG bit 7 ON,
2572    !                                 =2  reject by UTIL value,
2573    !                                 =3  reject by rogue check,
2574    !                                 =4  reject by both UTIL value and rogue check.
2575    !                                 =5  rejection due to topography
2576    !                                 =6  rejection due to topography, reject by UTIL value
2577    !                                 =7  rejection due to topography, reject by rogue check
2578    !                                 =8  rejection due to topography, reject by UTIL value,
2579    !                                     reject by rogue check
2580    !                                 ==> individual channels may be rejected from each obs pt
2581    !   actualNumChannel  - input  -  number of residual channels
2582    !   numObsToProcess   - input  -  number of groups of NVAL*NELE
2583    !
2584    implicit none
2585
2586    ! Arguments:
2587    real,    intent(in)    :: modelInterpTer(:)  ! Model surface height (m) for each obs
2588    integer, intent(inout) :: flagsInovQc(:)     ! Flags for assimilation/rejection of obs
2589    integer, intent(in)    :: actualNumChannel   ! Number of channels
2590    integer, intent(in)    :: numObsToProcess    ! Number of obs points to process
2591
2592    ! Locals:
2593    integer, parameter :: nChanCheck=4             ! number of channels to check
2594    integer            :: chanIndex
2595    integer            :: chanIndex1
2596    integer            :: checkedChan(nChanCheck)
2597    integer            :: checkedChanIndex
2598    integer            :: obsIndex
2599    integer            :: topoReject
2600    logical            :: debugSupp
2601    real               :: heightLimit
2602    real               :: topoHeight
2603    real               :: topoLimit(nChanCheck)
2604
2605    !------------------------------------------------------------------
2606    ! Define channels to check and height limits (m) for rejection
2607    !------------------------------------------------------------------
2608    checkedChan = (/     4,     9,    10,    11 /)
2609    topoLimit   = (/  250., 1000., 2000., 2500. /)
2610
2611    if (ssbg_debug) then
2612       write(*,*) 'check_topo: Maximum input GZ = ', maxval(modelInterpTer)
2613       write(*,*) 'check_topo: numObsToProcess = ', numObsToProcess
2614    end if
2615
2616    !------------------------------------------------------------------
2617    ! Perform the check for the selected channels
2618    !------------------------------------------------------------------
2619
2620    !   Loop over all numObsToProcess observation locations
2621
2622    topoReject = 0
2623    HEADER: do obsIndex = 1, numObsToProcess
2624      debugSupp = .false.
2625      chanIndex1 = (obsIndex-1)*actualNumChannel + 1  ! index of ch.1 obs
2626      topoHeight = modelInterpTer(obsIndex)           ! model topography height [m]
2627
2628      if (ssbg_debug) then
2629        if (topoHeight == maxval(modelInterpTer) .and. topoHeight > minval(topoLimit)) then
2630          write(*,*) 'check_topo: ****** Max height point! topoHeight = ', topoHeight
2631          debugSupp = .true.
2632        end if
2633      end if
2634
2635      SUBHEADER: do checkedChanIndex = 1, nChanCheck        ! loop over channels to check (checkedChan)
2636        heightLimit = topoLimit(checkedChanIndex)   ! height limit [m] for channel checkedChan(checkedChanIndex)
2637        chanIndex = chanIndex1 + (checkedChan(checkedChanIndex)-1)  ! channel checkedChan(checkedChanIndex) index
2638        if ( flagsInovQc(chanIndex) /= 1 ) then
2639          if ( topoHeight > heightLimit ) then
2640            flagsInovQc(chanIndex) = max(1,flagsInovQc(chanIndex)) + 4
2641            if (ssbg_debug .and. debugSupp) write(*,*) 'check_topo: Incrementing topoReject for max topoHeight point for ch.= ', checkedChan(checkedChanIndex)
2642            topoReject = topoReject + 1
2643            if (ssbg_debug) then
2644              if ( topoReject <= nChanCheck ) then
2645                write(*,*) 'check_topo:'
2646                write(*,*) ' Channel =          ', checkedChan(checkedChanIndex), &
2647                     &     ' Height limit (m) = ', heightLimit,                   &
2648                     &     ' Model height (m) = ', topoHeight
2649              end if
2650            end if
2651          end if
2652        end if
2653      end do SUBHEADER
2654
2655    end do HEADER
2656
2657    if (ssbg_debug .and. (topoReject > 0) ) then
2658      write(*,*) 'check_topo: Number of topography rejections and observations for this box = ', topoReject, numObsToProcess*actualNumChannel
2659    end if
2660
2661  end subroutine check_topo
2662
2663  !--------------------------------------------------------------------------
2664  ! ssbg_updateObsSpaceAfterInovQc
2665  !--------------------------------------------------------------------------
2666  subroutine ssbg_updateObsSpaceAfterInovQc(obsSpaceData, headerIndex, flagsInovQc)
2667    !
2668    !:Purpose: Update obspacedata variables (obstTB and obs flags) after QC
2669    !
2670    implicit none
2671
2672    ! Arguments:
2673    type(struct_obs), intent(inout) :: obsSpaceData    ! ObsSpaceData object
2674    integer,          intent(in)    :: headerIndex     ! Current header index
2675    integer,          intent(in)    :: flagsInovQc(:)  ! Flags for assimilation/rejection of obs
2676
2677    ! Locals:
2678    integer, allocatable :: obsFlags(:)
2679    integer, allocatable :: obsGlobalFlag(:)
2680    integer, allocatable :: satScanPosition(:)
2681    logical              :: sensorIndexFound
2682    integer              :: actualNumChannel
2683    integer              :: bodyIndex
2684    integer              :: bodyIndexBeg
2685    integer              :: bodyIndexEnd
2686    integer              :: channelIndex
2687    integer              :: currentChannelNumber
2688    integer              :: dataIndex
2689    integer              :: headerCompt
2690    integer              :: instr
2691    integer              :: instrId
2692    integer              :: iSat
2693    integer              :: numObsToProcess
2694    integer              :: obsIndex
2695    integer              :: platf
2696    integer              :: platfId
2697    integer              :: sensorIndex
2698
2699    ! find tvs_sensor index corresponding to current obs
2700
2701    platf = obs_headElem_i( obsSpaceData, OBS_SAT, headerIndex )
2702    instr = obs_headElem_i( obsSpaceData, OBS_INS, headerIndex )
2703
2704    call tvs_mapSat( platf, platfId, iSat )
2705    call tvs_mapInstrum( instr, instrId )
2706
2707    sensorIndexFound = .false.
2708    HEADER: do sensorIndex = 1, tvs_nsensors
2709      if ( platfId == tvs_platforms(sensorIndex)  .and. &
2710           iSat    == tvs_satellites(sensorIndex) .and. &
2711           instrId == tvs_instruments(sensorIndex) ) then
2712        sensorIndexFound = .true.
2713        exit HEADER
2714      end if
2715    end do HEADER
2716    if ( .not. sensorIndexFound ) call utl_abort('ssbg_updateObsSpaceAfterInovQc: sensor Index not found')
2717
2718    ! find actual Number of channels
2719    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
2720
2721    headerCompt = 1
2722    numObsToProcess = 1
2723    ! Allocate Header elements
2724    call utl_reAllocate(obsGlobalFlag, numObsToProcess)
2725    call utl_reAllocate(satScanPosition, numObsToProcess)
2726
2727    ! Allocation
2728    call utl_reAllocate(obsFlags,numObsToProcess*actualNumChannel)
2729
2730    ! Read elements in obsspace
2731
2732    obsGlobalFlag(headerCompt)    = obs_headElem_i( obsSpaceData, OBS_ST1, headerIndex )
2733    satScanPosition(headerCompt)  = obs_headElem_i( obsSpaceData, OBS_FOV, headerIndex )
2734    bodyIndexBeg                  = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
2735    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
2736
2737    BODY2: do bodyIndex =  bodyIndexbeg, bodyIndexEnd
2738      currentChannelNumber = nint(obs_bodyElem_r( obsSpaceData,  OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
2739      obsFlags(currentChannelNumber) = obs_bodyElem_i( obsSpaceData, OBS_FLG, bodyIndex )
2740    end do BODY2
2741    ! Modify flags
2742
2743    !------------------------------------------------------------------
2744    ! 1 - Mark global flags if any flagsInovQc > 0
2745    !------------------------------------------------------------------
2746    do obsIndex = 1 , numObsToProcess
2747      if ( maxval(flagsInovQc) > 0 ) obsGlobalFlag(obsIndex) = ibset(obsGlobalFlag(obsIndex), 6)
2748    end do
2749
2750    !------------------------------------------------------------------
2751    ! 2 - Mark obs flags for each value of flagsInovQc
2752    !------------------------------------------------------------------
2753
2754    dataIndex = 0
2755    do obsIndex = 1 , numObsToProcess
2756      do channelIndex = 1, actualNumChannel
2757        dataIndex = dataIndex+1
2758        if (resetQc) obsFlags(dataIndex) = 0
2759
2760        select case (flagsInovQc(dataIndex))
2761        case(1)
2762          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2763        case(2)
2764          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),11)
2765        case(3)
2766          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2767          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),16)
2768        case(4)
2769          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2770          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),11)
2771          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),16)
2772        case(5)
2773          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2774          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),18)
2775        case(6)
2776          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2777          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),11)
2778          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),18)
2779        case(7)
2780          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2781          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),16)
2782          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),18)
2783        case(8)
2784          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),9)
2785          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),11)
2786          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),16)
2787          obsFlags(dataIndex) = ibset(obsFlags(dataIndex),18)
2788        end select
2789
2790      end do
2791    end do
2792
2793    !-----------------------------------------------------------------
2794    !    Subtract 270 from FOV values (element 005043).
2795    !-----------------------------------------------------------------
2796    do obsIndex = 1 , numObsToProcess
2797      if (satScanPosition(obsIndex) > 270) satScanPosition(obsIndex) = satScanPosition(obsIndex) - 270
2798    end do
2799
2800    ! write elements in obsspace
2801    call obs_headSet_i(obsSpaceData, OBS_FOV, headerIndex, satScanPosition(1))
2802    call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex, obsGlobalFlag(1))
2803
2804    bodyIndexBeg = obs_headElem_i( obsSpaceData, OBS_RLN, headerIndex )
2805    bodyIndexEnd = bodyIndexBeg + obs_headElem_i( obsSpaceData, OBS_NLV, headerIndex ) -1
2806    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
2807      currentChannelNumber=nint(obs_bodyElem_r( obsSpaceData,  OBS_PPP, bodyIndex ))-tvs_channelOffset(sensorIndex)
2808      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags(currentChannelNumber))
2809    end do BODY
2810
2811  end subroutine ssbg_updateObsSpaceAfterInovQc
2812
2813  !--------------------------------------------------------------------------
2814  ! ssbg_bgCheckSSMIS
2815  !--------------------------------------------------------------------------
2816  subroutine ssbg_bgCheckSSMIS(obsSpaceData)
2817    !
2818    !:Purpose: Do the background check for SSMIS data (satQC and inovQC).
2819    !
2820    implicit none
2821
2822    ! Arguments:
2823    type(struct_obs),     intent(inout) :: obsSpaceData           ! ObsSpaceData object
2824
2825    ! Locals:
2826    integer, allocatable                :: flagsInovQc(:)
2827    logical, allocatable                :: obsToReject(:)
2828    integer                             :: codtyp
2829    integer                             :: dataIndex
2830    integer                             :: dataIndex1
2831    integer                             :: headerIndex
2832    integer                             :: indexFlags
2833    integer                             :: inovQcSize
2834    integer                             :: statsInovQcFlags(10)
2835    logical                             :: otherDataPresent
2836    logical                             :: ssmisDataPresent
2837    real                                :: percentInovQcFlags(9)
2838
2839    write(*,*) 'ssbg_bgCheckSSMIS: Starting'
2840
2841    call utl_tmg_start(119,'--BgckSSMIS')
2842    otherDataPresent = .false.
2843    ssmisDataPresent = .false.
2844    call obs_set_current_header_list(obsSpaceData,'TO')
2845    HEADER0: do
2846      headerIndex = obs_getHeaderIndex(obsSpaceData)
2847      if (headerIndex < 0) exit HEADER0
2848      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
2849      if ( tvs_isIdBurpInst(codtyp,'ssmis' ) ) then
2850        ssmisDataPresent = .true.
2851      else
2852        otherDataPresent = .true.
2853      end if
2854    end do HEADER0
2855
2856    if ( ssmisDataPresent .and. otherDataPresent ) then
2857      call utl_abort ('ssbg_bgCheckSSMIS: Other data than SSMIS also included in obsSpaceData')
2858    endif
2859
2860    if ( .not. ssmisDataPresent ) then
2861      write(*,*) 'WARNING: WILL NOT RUN ssbg_bgCheckSSMIS since no SSMIS'
2862      return
2863    end if
2864
2865    statsInovQcFlags(:) = 0
2866    percentInovQcFlags(:) = 0.0
2867
2868    ! read nambgck
2869    call ssbg_init()
2870    !Quality Control loop over all observations
2871    !
2872    ! loop over all header indices of the specified family with surface obs
2873
2874    call obs_set_current_header_list(obsSpaceData,'TO')
2875    HEADER: do
2876      headerIndex = obs_getHeaderIndex(obsSpaceData)
2877      if (headerIndex < 0) exit HEADER
2878      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
2879      if ( .not. (tvs_isIdBurpInst(codtyp,'ssmis')) ) then
2880        write(*,*) 'WARNING in ssbg_bgCheckSSMIS: Observation with codtyp = ', codtyp, ' is not SSMIS'
2881        cycle HEADER
2882      end if
2883
2884      !###############################################################################
2885      ! STEP 1) call satQC SSMIS program                                             !
2886      !###############################################################################
2887      if (ssbg_debug) write(*,*) 'ssbg_bgCheckSSMIS: STEP 1) call satQC SSMIS program'
2888      call ssbg_satqcSsmis(obsSpaceData, headerIndex, obsToReject)
2889
2890      !###############################################################################
2891      ! STEP 2) update Flags after satQC SSMIS program                               !
2892      !###############################################################################
2893      if (ssbg_debug) write(*,*) 'ssbg_bgCheckSSMIS: STEP 2) update Flags after satQC SSMIS program'
2894      call ssbg_updateObsSpaceAfterSatQc(obsSpaceData, headerIndex, obsToReject)
2895
2896      !###############################################################################
2897      ! STEP 3) call inovQC SSMIS program                                            !
2898      !###############################################################################
2899      if (ssbg_debug) write(*,*) 'ssbg_bgCheckSSMIS: STEP 3) call inovQC SSMIS program'
2900      call ssbg_inovqcSsmis(obsSpaceData, headerIndex, flagsInovQc)
2901
2902      !###############################################################################
2903      ! STEP 4) update Flags after inovQC SSMIS program                              !
2904      !###############################################################################
2905      if (ssbg_debug) write(*,*) 'ssbg_bgCheckSSMIS: STEP 4) update Flags after inovQC SSMIS program'
2906      call ssbg_updateObsSpaceAfterInovQc(obsSpaceData, headerIndex, flagsInovQc)
2907
2908      !###############################################################################
2909      ! STEP 5) compute statistics of different inovQc flags types                   !
2910      !###############################################################################
2911      inovQcSize = size(flagsInovQc)
2912      if (maxval(flagsInovQc) > 8) call utl_abort('ssbg_bgCheckSSMIS: Problem with flagsInovQc, value greater than 8.')
2913      do dataIndex = 1,inovQcSize
2914        dataIndex1 = flagsInovQc(dataIndex)+1
2915        ! Counting number of flags with value flagsInovQc(dataIndex)
2916        statsInovQcFlags(dataIndex1) = statsInovQcFlags(dataIndex1) + 1
2917        ! Counting total number of flags (observations)
2918        statsInovQcFlags(10) = statsInovQcFlags(10) + 1
2919      end do
2920
2921    end do HEADER
2922
2923    !###############################################################################
2924    ! STEP 6) displaying statistics of inovQc flags                                !
2925    !###############################################################################
2926
2927    ! If data were not checked because all had the flag bit 7 ON, then set all percentage statistics to 0.
2928    if ( statsInovQcFlags(10) == statsInovQcFlags(2) ) then
2929      percentInovQcFlags(:) = 0.0
2930    else
2931      do indexFlags = 1,9
2932        percentInovQcFlags(indexFlags) = float(statsInovQcFlags(indexFlags))/float(statsInovQcFlags(10)-statsInovQcFlags(2))*100
2933      end do
2934    end if
2935
2936    write(*,*)   '------------------- Innovation Rejection Statistics -----------------------'
2937    write(*,*)   '     Flag description                                   Nm. Obs.  Prct (%) '
2938    write(*,256) ' Total number of observations :                        ', statsInovQcFlags(10)
2939    write(*,256) ' (1) Not checked because flag bit 7 ON :               ', statsInovQcFlags(2)
2940    write(*,256) ' Remaining number of observations :                    ', statsInovQcFlags(10)-statsInovQcFlags(2)
2941    write(*,257) ' (0) Observations that are OK :                        ', statsInovQcFlags(1), percentInovQcFlags(1)
2942    write(*,257) ' (2) Rejected by UTIL value or bit 6 OFF :             ', statsInovQcFlags(3), percentInovQcFlags(3)
2943    write(*,257) ' (3) Rejected by rogue check (O-P) :                   ', statsInovQcFlags(4), percentInovQcFlags(4)
2944    write(*,257) ' (4) Rejected by both UTIL and rogue check :           ', statsInovQcFlags(5), percentInovQcFlags(5)
2945    write(*,257) ' (5) Topography rejection :                            ', statsInovQcFlags(6), percentInovQcFlags(6)
2946    write(*,257) ' (6) Topography rejection and by UTIL value :          ', statsInovQcFlags(7), percentInovQcFlags(7)
2947    write(*,257) ' (7) Topography rejection and by rogue check :         ', statsInovQcFlags(8), percentInovQcFlags(8)
2948    write(*,257) ' (8) Topography rejection, by UTIL and rogue check :   ', statsInovQcFlags(9), percentInovQcFlags(9)
2949    write(*,*)   '---------------------------------------------------------------------------'
2950
2951256 format(A55,i9)
2952257 format(A55,i9,f7.2,' %')
2953
2954    call utl_tmg_stop(119)
2955
2956    write(*,*) 'ssbg_bgCheckSSMIS: Finished'
2957
2958  end subroutine ssbg_bgCheckSSMIS
2959
2960end module bgckSSMIS_mod