bgckMicrowave_mod sourceΒΆ

   1module bgckMicrowave_mod
   2  ! MODULE bgckMicrowave_mod (prefix='mwbg' category='1. High-level functionality')
   3  !
   4  !:Purpose: Perform background check and quality control for all microwave
   5  !          satellite radiance observations: AMSU-A, AMSU-B/MHS, ATMS, MWHS2.
   6  !
   7  use midasMpi_mod
   8  use MathPhysConstants_mod
   9  use utilities_mod
  10  use obsSpaceData_mod
  11  use tovsNL_mod
  12  use obsErrors_mod
  13  use codtyp_mod
  14
  15  implicit none
  16  save
  17  private
  18
  19  ! Public functions/subroutines
  20  public :: mwbg_bgCheckMW
  21  public :: mwbg_computeMwhs2SurfaceType
  22
  23  real(8) :: mwbg_clwQcThreshold
  24  real(8) :: mwbg_cloudyClwThresholdBcorr
  25  real(8) :: mwbg_minSiOverWaterThreshold ! for AMSUB/MHS
  26  real(8) :: mwbg_maxSiOverWaterThreshold ! for AMSUB/MHS
  27  real(8) :: mwbg_cloudySiThresholdBcorr  ! for AMSUB/MHS
  28  logical :: mwbg_debug
  29  logical :: mwbg_useUnbiasedObsForClw 
  30
  31  integer, parameter :: mwbg_maxScanAngle = 98
  32  real(8), parameter :: mwbg_realMissing = -99.0d0 
  33  integer, parameter :: mwbg_intMissing = -1
  34
  35  ! Module variable
  36
  37  integer, parameter :: mwbg_atmsNumSfcSensitiveChannel = 6
  38  character(len=128), parameter :: fileMgLg='fstglmg'  ! glace de mer file
  39  ! Upper limit for CLW (kg/m**2) for Tb rejection over water
  40  real(8), parameter :: clw_atms_nrl_LTrej = 0.175d0      ! lower trop chans 1-6, 16-20
  41  real(8), parameter :: clw_atms_nrl_UTrej = 0.2d0        ! upper trop chans 7-9, 21-22
  42  real(8), parameter :: clw_mwhs2_nrl_LTrej = 0.175d0
  43  real(8), parameter :: clw_mwhs2_nrl_UTrej = 0.2d0
  44  ! Other NRL thresholds
  45  real(8), parameter :: scatec_atms_nrl_LTrej = 9.0d0     ! lower trop chans 1-6, 16-22
  46  real(8), parameter :: scatec_atms_nrl_UTrej = 18.0d0    ! upper trop chans 7-9
  47  real(8), parameter :: scatbg_atms_nrl_LTrej = 10.0d0    ! lower trop chans 1-6
  48  real(8), parameter :: scatbg_atms_nrl_UTrej = 15.0d0    ! upper trop chans 7-9
  49  real(8), parameter :: scatec_mwhs2_nrl_LTrej = 9.0d0    ! all MWHS-2 channels (over water)
  50  real(8), parameter :: scatbg_mwhs2_cmc_LANDrej = 0.0d0  ! all MWHS-2 channels (all surfaces)
  51  real(8), parameter :: scatbg_mwhs2_cmc_ICErej = 40.0d0
  52  real(8), parameter :: scatbg_mwhs2_cmc_SEA = 15.0d0
  53  real(8), parameter :: mean_Tb_183Ghz_min = 240.0d0      ! min. value for Mean(Tb) chans. 18-22 
  54  
  55  integer, parameter :: mwbg_maxNumChan = 100
  56  integer, parameter :: mwbg_maxNumTest = 16
  57
  58  integer, allocatable :: rejectionCodArray(:,:,:)    ! number of rejection per sat. per channl per test
  59  integer, allocatable :: rejectionCodArray2(:,:,:)   ! number of rejection per channl per test for ATMS 2nd category of tests
  60
  61  ! namelist variables
  62  character(len=9)   :: instName                      ! instrument name
  63  real(4)            :: clwQcThreshold                ! 
  64  real(4)            :: cloudyClwThresholdBcorr       !
  65  real(4)            :: minSiOverWaterThreshold       ! min scattering index over water for AMSUB/MHS
  66  real(4)            :: maxSiOverWaterThreshold       ! max scattering index over water for AMSUB/MHS
  67  real(4)            :: cloudySiThresholdBcorr        !
  68  logical            :: useUnbiasedObsForClw          !
  69  logical            :: RESETQC                       ! reset Qc flags option
  70  logical            :: modLSQ                        !
  71  logical            :: debug                         ! debug mode
  72  logical            :: skipTestArr(mwbg_maxNumTest)  ! array to set to skip the test
  73
  74
  75  namelist /nambgck/instName, clwQcThreshold, &
  76                    useUnbiasedObsForClw, debug, RESETQC,  &
  77                    cloudyClwThresholdBcorr, modLSQ, &
  78                    minSiOverWaterThreshold, maxSiOverWaterThreshold, &
  79                    cloudySiThresholdBcorr, skipTestArr
  80                    
  81
  82contains
  83
  84  subroutine mwbg_init()
  85    !
  86    !:Purpose: This subroutine reads the namelist section NAMBGCK for the module.
  87    !
  88    implicit none
  89
  90    ! Locals:
  91    integer :: nulnam, ierr
  92    integer, external :: fnom, fclos
  93
  94    ! Default values for namelist variables
  95    debug                   = .false.
  96    clwQcThreshold          = 0.3 
  97    useUnbiasedObsForClw    = .false.
  98    cloudyClwThresholdBcorr = 0.05
  99    minSiOverWaterThreshold = -10.0
 100    maxSiOverWaterThreshold = 30.0
 101    cloudySiThresholdBcorr  = 5.0
 102    RESETQC                 = .false.
 103    modLSQ                  = .false.
 104    skipTestArr(:)          = .false.
 105
 106    nulnam = 0
 107    ierr = fnom(nulnam, './flnml','FTN+SEQ+R/O', 0)
 108    read(nulnam, nml=nambgck, iostat=ierr)
 109    if (ierr /= 0) call utl_abort('mwbg_init: Error reading namelist')
 110    if (mmpi_myid == 0) write(*, nml=nambgck)
 111    ierr = fclos(nulnam)
 112
 113    mwbg_debug = debug
 114    mwbg_clwQcThreshold = real(clwQcThreshold,8)
 115    mwbg_useUnbiasedObsForClw = useUnbiasedObsForClw
 116    mwbg_cloudyClwThresholdBcorr = real(cloudyClwThresholdBcorr,8)
 117    mwbg_minSiOverWaterThreshold = real(minSiOverWaterThreshold,8)
 118    mwbg_maxSiOverWaterThreshold = real(maxSiOverWaterThreshold,8)
 119    mwbg_cloudySiThresholdBcorr = real(cloudySiThresholdBcorr,8)
 120
 121    ! Allocation
 122    call utl_reAllocate(rejectionCodArray, mwbg_maxNumTest, mwbg_maxNumChan, tvs_nsensors)
 123    call utl_reAllocate(rejectionCodArray2, mwbg_maxNumTest, mwbg_maxNumChan, tvs_nsensors)
 124
 125  end subroutine mwbg_init 
 126
 127  !--------------------------------------------------------------------------
 128  ! extractParamForGrodyRun
 129  !--------------------------------------------------------------------------  
 130  subroutine extractParamForGrodyRun(tb23,   tb31,   tb50,   tb53,   tb89, &
 131                                     tb23FG, tb31FG, tb50FG, tb53FG, tb89FG, &
 132                                     headerIndex, sensorIndex, obsSpaceData)
 133    !
 134    !:Purpose: Compute  Grody parameters by extracting tb for required channels:
 135    !            - 23 Ghz = AMSU-A 1 = channel #28
 136    !            - 31 Ghz = AMSU-A 2 = channel #29
 137    !            - 50 Ghz = AMSU-A 3 = channel #30
 138    !            - 53 Ghz = AMSU-A 5 = channel #32
 139    !            - 89 Ghz = AMSU-A15 = channel #42
 140    !
 141    implicit none
 142
 143    ! Arguments:
 144    real(8),          intent(out)   :: tb23                    ! radiance frequence 23 Ghz   
 145    real(8),          intent(out)   :: tb31                    ! radiance frequence 31 Ghz
 146    real(8),          intent(out)   :: tb50                    ! radiance frequence 50 Ghz  
 147    real(8),          intent(out)   :: tb53                    ! radiance frequence 53 Ghz  
 148    real(8),          intent(out)   :: tb89                    ! radiance frequence 89 Ghz  
 149    real(8),          intent(out)   :: tb23FG                  ! radiance frequence 23 Ghz   
 150    real(8),          intent(out)   :: tb31FG                  ! radiance frequence 31 Ghz
 151    real(8),          intent(out)   :: tb50FG                  ! radiance frequence 50 Ghz  
 152    real(8),          intent(out)   :: tb53FG                  ! radiance frequence 53 Ghz  
 153    real(8),          intent(out)   :: tb89FG                  ! radiance frequence 89 Ghz        
 154    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
 155    integer,          intent(in)    :: headerIndex  ! current header Index 
 156    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice) 
 157
 158    ! Locals:
 159    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
 160    real(8) :: obsTb, ompTb, obsTbBiasCorr
 161
 162    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 163    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 164    
 165    tb23 = mwbg_realMissing
 166    tb31 = mwbg_realMissing
 167    tb50 = mwbg_realMissing
 168    tb53 = mwbg_realMissing
 169    tb89 = mwbg_realMissing
 170    tb23FG = mwbg_realMissing
 171    tb31FG = mwbg_realMissing
 172    tb50FG = mwbg_realMissing
 173    tb53FG = mwbg_realMissing
 174    tb89FG = mwbg_realMissing
 175
 176    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 177      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 178      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 179      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
 180      obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
 181      obsTbBiasCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
 182
 183      if ( obsTb /= mwbg_realMissing ) then
 184        if ( obsTbBiasCorr /= mwbg_realMissing ) then
 185          if ( obsChanNumWithOffset == 28 ) tb23 = obsTb - obsTbBiasCorr
 186          if ( obsChanNumWithOffset == 29 ) tb31 = obsTb - obsTbBiasCorr
 187          if ( obsChanNumWithOffset == 30 ) tb50 = obsTb - obsTbBiasCorr
 188          if ( obsChanNumWithOffset == 32 ) tb53 = obsTb - obsTbBiasCorr
 189          if ( obsChanNumWithOffset == 42 ) tb89 = obsTb - obsTbBiasCorr
 190        else
 191          if ( obsChanNumWithOffset == 28 ) tb23 = obsTb
 192          if ( obsChanNumWithOffset == 29 ) tb31 = obsTb
 193          if ( obsChanNumWithOffset == 30 ) tb50 = obsTb
 194          if ( obsChanNumWithOffset == 32 ) tb53 = obsTb
 195          if ( obsChanNumWithOffset == 42 ) tb89 = obsTb
 196        end if
 197
 198        if ( obsChanNumWithOffset == 28 ) tb23FG = obsTb - ompTb
 199        if ( obsChanNumWithOffset == 29 ) tb31FG = obsTb - ompTb
 200        if ( obsChanNumWithOffset == 30 ) tb50FG = obsTb - ompTb
 201        if ( obsChanNumWithOffset == 32 ) tb53FG = obsTb - ompTb
 202        if ( obsChanNumWithOffset == 42 ) tb89FG = obsTb - ompTb
 203      else
 204        if ( obsChanNumWithOffset == 28 ) tb23 = 0.0d0
 205        if ( obsChanNumWithOffset == 29 ) tb31 = 0.0d0
 206        if ( obsChanNumWithOffset == 30 ) tb50 = 0.0d0
 207        if ( obsChanNumWithOffset == 32 ) tb53 = 0.0d0
 208        if ( obsChanNumWithOffset == 42 ) tb89 = 0.0d0
 209
 210        if ( obsChanNumWithOffset == 28 ) tb23FG = 0.0d0  
 211        if ( obsChanNumWithOffset == 29 ) tb31FG = 0.0d0 
 212        if ( obsChanNumWithOffset == 30 ) tb50FG = 0.0d0 
 213        if ( obsChanNumWithOffset == 32 ) tb53FG = 0.0d0 
 214        if ( obsChanNumWithOffset == 42 ) tb89FG = 0.0d0 
 215      end if
 216    end do BODY
 217
 218  end subroutine extractParamForGrodyRun
 219
 220  !--------------------------------------------------------------------------
 221  ! extractParamForBennartzRun
 222  !--------------------------------------------------------------------------  
 223  subroutine extractParamForBennartzRun(tb89, tb150, tb1831, tb1832, tb1833, &
 224                                        tb89FG, tb150FG, tb89FgClear, tb150FgClear, &
 225                                        headerIndex, sensorIndex, obsSpaceData)
 226    !
 227    !:Purpose: Extract Parameters required to run bennaertz for required channels:
 228    !            - 89 Ghz = AMSU-B 1 = channel #43
 229    !            - 150 Ghz = AMSU-B 2 = channel #44
 230    !
 231    implicit none
 232
 233    ! Arguments:
 234    real(8),          intent(out)   :: tb89                    ! 89GHz radiance from observation
 235    real(8),          intent(out)   :: tb150                   ! 150GHz radiance from observation
 236    real(8),          intent(out)   :: tb1831                  ! 183GHz radiance from observation
 237    real(8),          intent(out)   :: tb1832                  ! 183GHz radiance from observation
 238    real(8),          intent(out)   :: tb1833                  ! 183GHz radiance from observation
 239    real(8),          intent(out)   :: tb89FG                  ! 89GHz radiance from background
 240    real(8),          intent(out)   :: tb150FG                 ! 150GHz radiance from background
 241    real(8),          intent(out)   :: tb89FgClear             ! 89GHz clear-sky radiance from background
 242    real(8),          intent(out)   :: tb150FgClear            ! 150GHz clear-sky radiance from background
 243    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
 244    integer,          intent(in)    :: headerIndex  ! current header Index 
 245    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice)
 246
 247    ! Locals:
 248    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset, codtyp
 249    real(8) :: obsTb, btClear, ompTb, obsTbBiasCorr
 250
 251    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
 252    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 253    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 254
 255    tb89   = mwbg_realMissing
 256    tb150  = mwbg_realMissing
 257    tb1831 = mwbg_realMissing
 258    tb1832 = mwbg_realMissing
 259    tb1833 = mwbg_realMissing
 260    tb89FG  = mwbg_realMissing
 261    tb150FG = mwbg_realMissing
 262    tb89FgClear  = mwbg_realMissing
 263    tb150FgClear = mwbg_realMissing
 264
 265    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 266      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 267      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 268      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
 269      obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
 270      obsTbBiasCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
 271      if (tvs_isInstrumAllskyHuAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
 272        btClear = obs_bodyElem_r(obsSpaceData, OBS_BTCL, bodyIndex)
 273      else
 274        btClear = mwbg_realMissing
 275      end if
 276
 277      if ( obsTb /= mwbg_realMissing ) then
 278        if ( obsTbBiasCorr /= mwbg_realMissing ) then
 279          if ( obsChanNumWithOffset == 43 ) tb89 = obsTb - obsTbBiasCorr
 280          if ( obsChanNumWithOffset == 44 ) tb150 = obsTb - obsTbBiasCorr
 281          if ( obsChanNumWithOffset == 45 ) tb1831 = obsTb - obsTbBiasCorr
 282          if ( obsChanNumWithOffset == 46 ) tb1832 = obsTb - obsTbBiasCorr
 283          if ( obsChanNumWithOffset == 47 ) tb1833 = obsTb - obsTbBiasCorr
 284        else
 285          if ( obsChanNumWithOffset == 43 ) tb89 = obsTb
 286          if ( obsChanNumWithOffset == 44 ) tb150 = obsTb
 287          if ( obsChanNumWithOffset == 45 ) tb1831 = obsTb
 288          if ( obsChanNumWithOffset == 46 ) tb1832 = obsTb
 289          if ( obsChanNumWithOffset == 47 ) tb1833 = obsTb
 290        end if
 291
 292        if ( obsChanNumWithOffset == 43 ) tb89FG  = obsTb - ompTb
 293        if ( obsChanNumWithOffset == 44 ) tb150FG = obsTb - ompTb
 294        
 295      else
 296        if ( obsChanNumWithOffset == 43 ) tb89 = 0.0d0
 297        if ( obsChanNumWithOffset == 44 ) tb150 = 0.0d0
 298        if ( obsChanNumWithOffset == 45 ) tb1831 = 0.0d0
 299        if ( obsChanNumWithOffset == 46 ) tb1832 = 0.0d0
 300        if ( obsChanNumWithOffset == 47 ) tb1833 = 0.0d0
 301
 302        if ( obsChanNumWithOffset == 43 ) tb89FG = 0.0d0
 303        if ( obsChanNumWithOffset == 44 ) tb150FG = 0.0d0
 304      end if
 305
 306      if (btClear /= mwbg_realMissing) then
 307        if (obsChanNumWithOffset == 43) tb89FgClear = btClear
 308        if (obsChanNumWithOffset == 44) tb150FgClear = btClear
 309      else
 310        if (obsChanNumWithOffset == 43) tb89FgClear = 0.0d0
 311        if (obsChanNumWithOffset == 44) tb150FgClear = 0.0d0      
 312      end if
 313    end do BODY
 314
 315  end subroutine extractParamForBennartzRun
 316
 317  !--------------------------------------------------------------------------
 318  ! amsuABTest10RttovRejectCheck
 319  !--------------------------------------------------------------------------
 320  subroutine amsuABTest10RttovRejectCheck(sensorIndex, RESETQC, qcIndicator, headerIndex, obsSpaceData)
 321    !
 322    !:Purpose: test 10: RTTOV reject check (single).
 323    !          Rejected datum flag has bit #9 on.
 324    !
 325    implicit none
 326
 327    ! Arguments:
 328    integer,          intent(in)    :: sensorIndex    ! numero de satellite (i.e. indice) 
 329    logical,          intent(in)    :: RESETQC        ! yes or not reset QC flag
 330    integer,          intent(inout) :: qcIndicator(:) ! indicateur du QC par canal
 331    type(struct_obs), intent(inout) :: obsSpaceData   ! obspaceData Object
 332    integer,          intent(in)    :: headerIndex    ! current header Index 
 333
 334    ! Locals:
 335    integer :: testIndex, IBIT, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 336    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 337    character(len=9) :: stnId
 338
 339    if (RESETQC) return
 340    testIndex = 10
 341
 342    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 343    
 344    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 345    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 346
 347    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 348      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 349      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 350      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 351      if (obsChanNumWithOffset /= 20) then
 352        IBIT = AND(obsFlags, 2**9)
 353        if (IBIT /= 0) then
 354          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 355          obsFlags = OR(obsFlags,2**7)
 356          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 357                rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex)+ 1
 358
 359          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 360
 361          if ( mwbg_DEBUG ) then
 362            write(*,*)stnId(2:9),' RTTOV REJECT.', &
 363                      'CHANNEL=', obsChanNumWithOffset, &
 364                      ' obsFlags= ',obsFlags
 365          end if
 366        end if
 367
 368      end if
 369    end do BODY
 370
 371  end subroutine amsuABTest10RttovRejectCheck
 372
 373  !--------------------------------------------------------------------------
 374  ! amsuABTest1TopographyCheck
 375  !--------------------------------------------------------------------------
 376  subroutine amsuABTest1TopographyCheck(sensorIndex, modelInterpTerrain, channelForTopoFilter, altitudeForTopoFilter, &
 377                                        qcIndicator, headerIndex, obsSpaceData)
 378    !
 379    !:Purpose: test 1: Topography check (partial)
 380    !          Channel 6 is rejected for topography >  250m.
 381    !          Channel 7 is rejected for topography > 2000m.
 382    !
 383    implicit none
 384
 385    ! Arguments:
 386    integer,          intent(in)    :: sensorIndex             ! numero de satellite (i.e. indice) 
 387    integer,          intent(in)    :: channelForTopoFilter(:) ! channel list for filter
 388    real(8),          intent(in)    :: altitudeForTopoFilter(:)! altitude threshold
 389    real(8),          intent(in)    :: modelInterpTerrain      ! topo aux point d'obs
 390    integer,          intent(inout) :: qcIndicator(:)          ! indicateur du QC par canal
 391    type(struct_obs), intent(inout) :: obsSpaceData         ! obspaceData Object
 392    integer,          intent(in)    :: headerIndex          ! current header Index 
 393
 394    ! Locals:
 395    integer :: numFilteringTest, indexFilteringTest, testIndex
 396    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd
 397    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 398    character(len=9) :: stnId
 399
 400    testIndex = 1
 401
 402    !check consistency between channelForTopoFilter and altitudeForTopoFilter
 403    if ( size(altitudeForTopoFilter) /= size(channelForTopoFilter) ) then 
 404      call utl_abort('ABORT: amsuABTest1TopographyCheck, no consistency between channel List and altitude list ')
 405    end if 
 406   
 407    numFilteringTest =  size(altitudeForTopoFilter) 
 408    indexFilteringTest = 1
 409
 410    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 411
 412    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 413    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 414
 415    do while ( indexFilteringTest <= numFilteringTest )
 416      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 417        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 418        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 419        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 420
 421        if (obsChanNumWithOffset == channelForTopoFilter(indexFilteringTest)) then
 422          if (modelInterpTerrain >= altitudeForTopoFilter(indexFilteringTest)) then
 423            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 424            obsFlags = OR(obsFlags,2**9)
 425            obsFlags = OR(obsFlags,2**18)
 426            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 427                  rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 428            if ( mwbg_DEBUG ) then
 429              write(*,*) stnId(2:9),' TOPOGRAPHY REJECT.', &
 430                         'CHANNEL=', obsChanNumWithOffset, &
 431                         ' TOPO= ',modelInterpTerrain
 432            end if
 433
 434            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 435
 436          end if
 437        end if
 438      end do BODY
 439      indexFilteringTest = indexFilteringTest + 1
 440    end do ! while ( indexFilteringTest <= numFilteringTest )
 441
 442  end subroutine amsuABTest1TopographyCheck
 443
 444  !--------------------------------------------------------------------------
 445  ! amsuABTest2LandSeaQualifierCheck 
 446  !--------------------------------------------------------------------------
 447  subroutine amsuABTest2LandSeaQualifierCheck(sensorIndex, qcIndicator, headerIndex, obsSpaceData)
 448    !
 449    !:Purpose: test 2: "Land/sea qualifier" code check (full)
 450    !          allowed values are: 0 land, 1 sea, 2 coast.
 451    !
 452    implicit none
 453
 454    ! Arguments:
 455    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
 456    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 457    type(struct_obs), intent(inout) :: obsSpaceData    ! obspaceData Object
 458    integer,          intent(in)    :: headerIndex     ! current header Index 
 459
 460    ! Locals:
 461    integer :: testIndex, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 462    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 463    character(len=9) :: stnId
 464  
 465    testIndex = 2
 466
 467    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
 468    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 469
 470    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 471    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 472
 473    if (landQualifierIndice < 0  .or. landQualifierIndice > 2) then
 474      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 475        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 476        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 477        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 478
 479        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 480        obsFlags = OR(obsFlags,2**9)
 481        obsFlags = OR(obsFlags,2**7)
 482        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 483          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 484
 485        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 486      end do BODY
 487
 488      if ( mwbg_DEBUG ) then
 489        write(*,*) stnId(2:9), 'LAND/SEA QUALifIER CODE', &
 490                   ' REJECT. landQualifierIndice=', landQualifierIndice
 491      end if
 492    end if
 493
 494  end subroutine amsuABTest2LandSeaQualifierCheck
 495
 496  !--------------------------------------------------------------------------
 497  !  amsuABTest3TerrainTypeCheck
 498  !--------------------------------------------------------------------------
 499  subroutine amsuABTest3TerrainTypeCheck(sensorIndex, qcIndicator, headerIndex, obsSpaceData)
 500    !
 501    !:Purpose: test 3: "Terrain type" code check (full)
 502    !          allowed values are: -1 missing, 0 sea-ice, 1 snow on land.
 503    !
 504    implicit none
 505
 506    ! Arguments:
 507    integer,          intent(in) :: sensorIndex     ! numero de satellite (i.e. indice) 
 508    integer,       intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 509    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
 510    integer,             intent(in) :: headerIndex  ! current header Index
 511
 512    ! Locals:
 513    integer :: testIndex, terrainTypeIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 514    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 515    character(len=9) :: stnId
 516
 517    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 518    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
 519
 520    ! If terrain type is missing, set it to -1 for the QC programs
 521    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
 522    
 523    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 524    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 525    
 526    testIndex = 3
 527    if ( terrainTypeIndice /= mwbg_intMissing ) then
 528      if (terrainTypeIndice < 0 .or. terrainTypeIndice > 1) then
 529        BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 530          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 531          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)        
 532          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 533
 534          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 535          obsFlags = OR(obsFlags,2**9)
 536          obsFlags = OR(obsFlags,2**7)
 537          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 538            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 539
 540          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 541        end do BODY
 542        if ( mwbg_debug ) then
 543          write(*,*)stnId(2:9),'TERRAIN type CODE', &
 544                    ' REJECT. TERRAIN=', terrainTypeIndice
 545        end if
 546      end if
 547    end if
 548
 549  end subroutine amsuABTest3TerrainTypeCheck
 550
 551  !--------------------------------------------------------------------------
 552  ! amsuABTest4FieldOfViewCheck 
 553  !--------------------------------------------------------------------------
 554  subroutine amsuABTest4FieldOfViewCheck(sensorIndex, maxScanAngleAMSU, qcIndicator, &
 555                                         headerIndex, obsSpaceData)
 556    !
 557    !:Purpose: test 4: Field of view number check (full)
 558    !          Field of view acceptable range is [1,maxScanAngleAMSU] for AMSU footprints.
 559    !
 560    implicit none
 561
 562    ! Arguments:
 563    integer,          intent(in)    :: sensorIndex      ! numero de satellite (i.e. indice) 
 564    integer,          intent(in)    :: maxScanAngleAMSU ! max scan angle 
 565    integer,          intent(inout) :: qcIndicator(:)   ! indicateur du QC par canal
 566    type(struct_obs), intent(inout) :: obsSpaceData     ! obspaceData Object
 567    integer,          intent(in)    :: headerIndex      ! current header Index 
 568
 569    ! Locals:
 570    integer :: testIndex, satScanPosition, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 571    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 572    character(len=9) :: stnId
 573
 574    satScanPosition = obs_headElem_i(obsSpaceData, OBS_FOV , headerIndex) 
 575    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 576
 577    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 578    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 579    
 580    testIndex = 4
 581    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 582      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 583      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 584      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 585
 586      if (satScanPosition < 1 .or. satScanPosition > maxScanAngleAMSU) then
 587        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 588        obsFlags = OR(obsFlags,2**9)
 589        obsFlags = OR(obsFlags,2**7)
 590        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 591          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 592
 593        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 594
 595        if ( mwbg_debug ) then
 596          write(*,*)stnId(2:9),'FIELD OF VIEW NUMBER', &
 597                    ' REJECT. FIELD OF VIEW= ', satScanPosition
 598        end if
 599      end if
 600    end do BODY
 601
 602  end subroutine amsuABTest4FieldOfViewCheck 
 603  
 604  !--------------------------------------------------------------------------
 605  ! amsuABTest5ZenithAngleCheck 
 606  !--------------------------------------------------------------------------
 607  subroutine amsuABTest5ZenithAngleCheck(sensorIndex, qcIndicator, headerIndex, obsSpaceData)
 608    !
 609    !:Purpose: test 5: Satellite zenith angle check (full)
 610    !          Satellite zenith angle acceptable range is [0.,60.].
 611    !
 612    implicit none
 613
 614    ! Arguments:
 615    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
 616    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 617    type(struct_obs), intent(inout) :: obsSpaceData    ! obspaceData Object
 618    integer,          intent(in)    :: headerIndex     ! current header Index 
 619
 620    ! Locals:
 621    integer :: testIndex, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 622    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 623    real(8) :: satZenithAngle
 624    character(len=9) :: stnId
 625
 626    testIndex = 5
 627
 628    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
 629    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 630
 631    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 632    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 633
 634    if ( satZenithAngle /= mwbg_realMissing ) then
 635      if (satZenithAngle < 0.0d0 .or. satZenithAngle > 60.0d0) then
 636        BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 637          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 638          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 639          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 640
 641          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 642          obsFlags = OR(obsFlags,2**9)
 643          obsFlags = OR(obsFlags,2**7)
 644          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 645              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 646
 647          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 648        end do BODY
 649
 650        if ( mwbg_debug ) then
 651          write(*,*) stnId(2:9),' SATELLITE ZENITH ANGLE', &
 652                     ' REJECT. satZenithAngle= ', &
 653                     satZenithAngle
 654        end if
 655      end if
 656    end if
 657
 658  end subroutine amsuABTest5ZenithAngleCheck 
 659
 660  !--------------------------------------------------------------------------
 661  ! amsuABTest6ZenAngleAndFovConsistencyCheck 
 662  !--------------------------------------------------------------------------
 663  subroutine amsuABTest6ZenAngleAndFovConsistencyCheck(sensorIndex, ZANGL, maxScanAngleAMSU, qcIndicator, &
 664                                                       headerIndex, obsSpaceData)
 665    !
 666    !:Purpose: test 6: "Sat. zenith angle"/"field of view" consistency check.  (full)
 667    !          Acceptable difference between "Satellite zenith angle"  and
 668    !          "approximate angle computed from field of view number" is 1.8 degrees.
 669    !
 670    implicit none
 671
 672    ! Arguments:
 673    integer,          intent(in)    :: sensorIndex      ! numero de satellite (i.e. indice) 
 674    real(8),          intent(in)    :: ZANGL            ! satellite constant param
 675    integer,          intent(in)    :: maxScanAngleAMSU ! max scan angle 
 676    integer,          intent(inout) :: qcIndicator(:)   ! indicateur du QC par canal
 677    type(struct_obs), intent(inout) :: obsSpaceData     ! obspaceData Object
 678    integer,          intent(in)    :: headerIndex      ! current header Index 
 679
 680    ! Locals:
 681    integer :: testIndex, satScanPosition, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 682    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 683    real(8) :: APPROXIM, ANGDif, satZenithAngle 
 684    character(len=9) :: stnId
 685
 686    testIndex = 6
 687
 688    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
 689    satScanPosition = obs_headElem_i(obsSpaceData, OBS_FOV , headerIndex) 
 690    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 691
 692    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 693    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 694
 695    if (satZenithAngle /= mwbg_realMissing .and. satScanPosition /=  mwbg_intMissing) then
 696      APPROXIM = ABS((satScanPosition - maxScanAngleAMSU / 2.0d0 - 0.5d0) * ZANGL)
 697      ANGDif = ABS(satZenithAngle - APPROXIM)
 698      if ( ANGDif > 1.8d0 ) then 
 699        BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 700          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 701          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 702          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 703
 704          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 705          obsFlags = OR(obsFlags,2**9)
 706          obsFlags = OR(obsFlags,2**7)
 707          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 708              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 709
 710          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 711        end do BODY
 712
 713        if ( mwbg_debug ) then
 714          write(*,*) stnId(2:9),' ANGLE/FIELD OF VIEW', &
 715                     ' INCONSISTENCY REJECT. satZenithAngle= ', &
 716                     satZenithAngle, ' FIELD OF VIEW= ',satScanPosition, &
 717                     ' ANGDif= ',ANGDif  
 718        end if
 719      end if
 720    end if
 721
 722  end subroutine amsuABTest6ZenAngleAndFovConsistencyCheck
 723
 724  !--------------------------------------------------------------------------
 725  !  amsuABTest7landSeaQualifyerAndModelLandSeaConsistencyCheck
 726  !--------------------------------------------------------------------------
 727  subroutine amsuABTest7landSeaQualifyerAndModelLandSeaConsistencyCheck(sensorIndex, modelInterpLandFrac, &
 728                                                                        qcIndicator, headerIndex, obsSpaceData)
 729    !
 730    !:Purpose: test 7: "Land/sea qual."/"model land/sea" consistency check (full). 
 731    !          Acceptable conditions are:
 732    !            - both over ocean (landQualifierIndice=1; mg<0.01), new threshold 0.20, jh dec 2000,
 733    !            - both over land  (landQualifierIndice=0; mg>0.80), new threshold 0.50, jh dec 2000.
 734    !          All other conditions are unacceptable.
 735    !
 736    implicit none
 737
 738    ! Arguments:
 739    integer,          intent(in)    :: sensorIndex         ! numero de satellite (i.e. indice) 
 740    real(8),          intent(in)    :: modelInterpLandFrac ! model interpolated land fraction
 741    integer,          intent(inout) :: qcIndicator(:)      ! indicateur du QC par canal
 742    type(struct_obs), intent(inout) :: obsSpaceData        ! obspaceData Object
 743    integer,          intent(in)    :: headerIndex         ! current header Index 
 744
 745    ! Locals:
 746    integer :: testIndex, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 747    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 748    character(len=9) :: stnId
 749
 750    testIndex = 7
 751
 752    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
 753    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex)
 754
 755    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 756    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 757
 758    if (landQualifierIndice /= mwbg_intMissing .and. &
 759        .not. (landQualifierIndice == 1 .and. modelInterpLandFrac < 0.20d0) .and. &
 760        .not. (landQualifierIndice == 0 .and. modelInterpLandFrac > 0.50d0)) then
 761      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 762        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 763        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 764        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 765
 766        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 767        obsFlags = OR(obsFlags,2**9)
 768        obsFlags = OR(obsFlags,2**7)
 769        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 770            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 771
 772        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 773      end do BODY
 774
 775      if ( mwbg_debug ) then
 776        write(*,*) stnId(2:9),' LAND/SEA QUALifIER', &
 777                   ' INCONSISTENCY REJECT. landQualifierIndice= ', &
 778                   landQualifierIndice, ' MODEL MASK= ', modelInterpLandFrac
 779      end if
 780    end if
 781
 782  end subroutine amsuABTest7landSeaQualifyerAndModelLandSeaConsistencyCheck 
 783
 784  !--------------------------------------------------------------------------
 785  !  amsuABTest9UncorrectedTbCheck
 786  !--------------------------------------------------------------------------
 787  subroutine amsuABTest9UncorrectedTbCheck(sensorIndex, RESETQC, qcIndicator, headerIndex, obsSpaceData)
 788    !
 789    !:Purpose: test 9: Uncorrected Tb check (single).
 790    !          Uncorrected datum (flag bit #6 off). In this case switch bit 11 ON.
 791    !
 792    implicit none
 793
 794    ! Arguments:
 795    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
 796    logical,          intent(in)    :: RESETQC         ! yes or not reset QC flag
 797    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 798    type(struct_obs), intent(inout) :: obsSpaceData    ! obspaceData Object
 799    integer,          intent(in)    :: headerIndex     ! current header Index 
 800
 801    ! Locals:
 802    integer :: testIndex, IBIT, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 803    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 804    character(len=9) :: stnId
 805
 806    if (RESETQC) return
 807    testIndex = 9
 808
 809    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 810
 811    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 812    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 813
 814    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 815      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 816      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 817      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 818
 819      if (obsChanNumWithOffset /= 20) then
 820        IBIT = AND(obsFlags, 2**6)
 821        if (IBIT == 0) then
 822          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 823          obsFlags = OR(obsFlags,2**11)
 824          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 825                rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex)+ 1
 826
 827          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 828
 829          if ( mwbg_debug ) then
 830            write(*,*) stnId(2:9),' UNCORRECTED TB REJECT.', &
 831                       'CHANNEL=', obsChanNumWithOffset, ' obsFlags= ',obsFlags
 832          end if
 833        end if
 834      end if
 835    end do BODY
 836
 837  end subroutine amsuABTest9UncorrectedTbCheck
 838 
 839  !--------------------------------------------------------------------------
 840  ! amsuABTest11RadianceGrossValueCheck
 841  !--------------------------------------------------------------------------
 842  subroutine amsuABTest11RadianceGrossValueCheck(sensorIndex, GROSSMIN, GROSSMAX, qcIndicator, &
 843                                                 headerIndex, obsSpaceData)
 844    !
 845    !:Purpose: test 11: Radiance observation "Gross" check (single). 
 846    !          Change this test from full to single. jh nov 2000.
 847    !
 848    implicit none
 849
 850    ! Arguments:
 851    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
 852    real(8),          intent(in)    :: GROSSMIN(:)     ! Gross val min 
 853    real(8),          intent(in)    :: GROSSMAX(:)     ! Gross val max 
 854    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 855    type(struct_obs), intent(inout) :: obsSpaceData    ! obspaceData Object
 856    integer,          intent(in)    :: headerIndex     ! current header Index 
 857
 858    ! Locals:
 859    integer :: testIndex, GROSSERROR, actualNumChannel, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 860    integer :: obsChanNum, obsChanNumWithOffset, obsFlags 
 861    real(8) :: obsTb
 862    character(len=9) :: stnId
 863
 864    testIndex = 11
 865    GROSSERROR = .FALSE.
 866
 867    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
 868    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 869
 870    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 871    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 872
 873    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 874      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 875      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 876      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 877      obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
 878
 879      if (obsChanNumWithOffset /= 20 .and. obsChanNumWithOffset >= 1 .and. &
 880          obsChanNumWithOffset <= actualNumChannel) then  
 881
 882        if (obsTb /= mwbg_realMissing .and. &
 883            (obsTb < GROSSMIN(obsChanNumWithOffset) .or. &
 884             obsTb > GROSSMAX(obsChanNumWithOffset))) then
 885          GROSSERROR = .TRUE.
 886          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 887          obsFlags = OR(obsFlags,2**9)
 888          obsFlags = OR(obsFlags,2**7)
 889          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 890                  rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex)+ 1
 891                  
 892          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 893
 894          if ( mwbg_debug ) then
 895            write(*,*) stnId(2:9),' GROSS CHECK REJECT.', &
 896                       'CHANNEL=', obsChanNumWithOffset, ' TB= ',obsTb
 897          end if
 898        end if
 899      end if
 900    end do BODY
 901
 902  end subroutine amsuABTest11RadianceGrossValueCheck 
 903  
 904  !--------------------------------------------------------------------------
 905  ! amsuaTest12GrodyClwCheck
 906  !--------------------------------------------------------------------------
 907  subroutine amsuaTest12GrodyClwCheck(sensorIndex, ICLWREJ, qcIndicator, headerIndex, obsSpaceData)
 908    !
 909    !:Purpose: test 12: Grody cloud liquid water check (partial).
 910    !
 911    !          For Cloud Liquid Water > clwQcThreshold, reject AMSUA-A channels 1-5 and 15.
 912    !
 913    implicit none
 914
 915    ! Arguments:
 916    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
 917    integer,          intent(in)    :: ICLWREJ(:)      ! rejection channel list
 918    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
 919    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
 920    integer,          intent(in)    :: headerIndex  ! current header Index 
 921
 922    ! Locals:
 923    integer :: testIndex, INDXCAN, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
 924    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
 925    real(8) :: clwUsedForQC, clwObsFGaveraged
 926    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
 927    logical :: surfTypeIsWater, cldPredMissing
 928    character(len=9) :: stnId
 929
 930    testIndex = 12
 931
 932    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
 933    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
 934    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
 935    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
 936
 937    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
 938    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
 939
 940    if ( tvs_mwAllskyAssim ) then
 941      clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
 942      clwUsedForQC = clwObsFGaveraged
 943      cldPredMissing = (cloudLiquidWaterPathObs == mwbg_realMissing .or. cloudLiquidWaterPathFG == mwbg_realMissing)
 944    else
 945      clwUsedForQC = cloudLiquidWaterPathObs
 946      cldPredMissing = (cloudLiquidWaterPathObs == mwbg_realMissing)
 947    end if
 948
 949    surfTypeIsWater = (landQualifierIndice == 1)
 950
 951    if (.not. cldPredMissing) then
 952      if (clwUsedForQC > mwbg_clwQcThreshold) then
 953        BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 954          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 955          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 956          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 957
 958          INDXCAN = utl_findloc(ICLWREJ(:),obsChanNumWithOffset)
 959          if ( INDXCAN /= 0 )  then
 960            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
 961            obsFlags = OR(obsFlags,2**9)
 962            obsFlags = OR(obsFlags,2**7)
 963            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
 964                      rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
 965            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 966          end if
 967        end do BODY
 968
 969        if ( mwbg_debug ) then
 970          write(*,*) stnId(2:9), 'Grody cloud liquid water check', &
 971                     ' REJECT. CLW= ',clwUsedForQC, ' SEUIL= ',mwbg_clwQcThreshold
 972        end if
 973      end if
 974
 975      ! In all-sky mode, turn on bit=23 for channels in ICLWREJ(:) as 
 976      ! cloud-affected radiances over sea when there is mismatch between 
 977      ! cloudLiquidWaterPathObs and cloudLiquidWaterPathFG (to be used in gen_bias_corr)
 978      clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
 979      IF (tvs_mwAllskyAssim .and. clwObsFGaveraged > mwbg_cloudyClwThresholdBcorr) then
 980        BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
 981          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
 982          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
 983          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
 984          INDXCAN = utl_findloc(ICLWREJ(:),obsChanNumWithOffset)
 985
 986          if ( INDXCAN /= 0 ) then
 987            obsFlags = OR(obsFlags,2**23)
 988            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
 989          end if
 990        end do BODY2
 991        
 992        if ( mwbg_debug ) then
 993          write(*,*) stnId(2:9), ' Grody cloud liquid water check', &
 994                     ' cloud-affected obs. CLW= ',clwUsedForQC, ', threshold= ', &
 995                     mwbg_cloudyClwThresholdBcorr
 996        end if
 997      end if
 998
 999    ! Reject surface sensitive observations over water, in all-sky mode, 
1000    ! if CLW is not retrieved, and is needed to define obs error.
1001    else if (tvs_mwAllskyAssim .and. surfTypeIsWater .and. cldPredMissing) then
1002      loopChannel: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1003        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1004        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1005        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1006        
1007        INDXCAN = utl_findloc(ICLWREJ(:),obsChanNumWithOffset)
1008        if ( INDXCAN /= 0 .and. oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex) ) then
1009          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1010          obsFlags = OR(obsFlags,2**9)
1011          obsFlags = OR(obsFlags,2**7)
1012          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1013                    rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1014
1015          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1016        end if
1017      end do loopChannel
1018
1019    end if
1020
1021  end subroutine amsuaTest12GrodyClwCheck 
1022
1023  !-------------------------------------------------------------------------
1024  ! amsubTest12DrynessIndexCheck
1025  !-------------------------------------------------------------------------
1026  subroutine amsubTest12DrynessIndexCheck(sensorIndex, tb1831, tb1833, modelInterpSeaIce, qcIndicator, &
1027                                          headerIndex, obsSpaceData, skipTestArr_opt)
1028    !
1029    !:Purpose: test 12: Dryness index check.
1030    !          The difference between channels AMSUB-3 and AMSUB-5 is used as an indicator
1031    !          of "dryness" of the atmosphere. In extreme dry conditions, channels AMSUB-3 4 and 5
1032    !          are sensitive to the surface.
1033    !          Therefore, various thresholds are used to reject channels AMSUB-3 4 and 5 over land and ice
1034    !
1035    implicit none
1036
1037    ! Arguments:
1038    integer,           intent(in)    :: sensorIndex        ! numero de satellite (i.e. indice) 
1039    real(8),           intent(in)    :: tb1831             ! tb for channel  
1040    real(8),           intent(in)    :: tb1833             ! tb for channel  
1041    real(8),           intent(in)    :: modelInterpSeaIce  ! topo interpolated to obs point
1042    integer,           intent(inout) :: qcIndicator(:)     ! indicateur du QC par canal
1043    type(struct_obs),  intent(inout) :: obsSpaceData       ! obspaceData Object
1044    integer,           intent(in)    :: headerIndex        ! current header Index
1045    logical, optional, intent(in)    :: skipTestArr_opt(:) ! array to set to skip the test
1046
1047    ! Locals:
1048    integer :: testIndex, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1049    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1050    real(8) :: drynessIndex
1051    character(len=9) :: stnId
1052    logical, save :: firstCall = .true.
1053
1054    testIndex = 12
1055    if (present(skipTestArr_opt)) then
1056      if (skipTestArr_opt(testIndex)) then
1057        if (firstCall) then
1058          firstCall = .false.
1059          write(*,*) 'amsubTest12DrynessIndexCheck: skipping this test'
1060        end if
1061        return
1062      end if
1063    end if
1064
1065    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
1066    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1067
1068    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1069    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1070
1071    drynessIndex = tb1831 - tb1833
1072    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1073      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1074      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1075      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1076
1077      if ( .not. ((landQualifierIndice == 1) .and. &
1078                  (modelInterpSeaIce < 0.01d0)) ) then
1079        if (obsChanNumWithOffset == 45 .and. drynessIndex > 0.0d0) then
1080          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1081          obsFlags = OR(obsFlags,2**9)
1082          obsFlags = OR(obsFlags,2**7)
1083          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1084            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1085
1086          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1087
1088          if (mwbg_debug) then
1089            write(*,*) stnId(2:9),' DRYNESS INDEX REJECT.',        &
1090                       'CHANNEL=', obsChanNumWithOffset, &
1091                       'INDEX= ',drynessIndex
1092          end if
1093
1094        else if (obsChanNumWithOffset == 46 .and. drynessIndex > -10.0d0) then
1095          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1096          obsFlags = OR(obsFlags,2**9)
1097          obsFlags = OR(obsFlags,2**7)
1098          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) =  &
1099            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1100
1101          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1102
1103          if (mwbg_debug) then
1104            write(*,*) stnId(2:9),' DRYNESS INDEX REJECT.',       &
1105                       'CHANNEL=', obsChanNumWithOffset,&
1106                       'INDEX= ',drynessIndex
1107          end if
1108        
1109        else if (obsChanNumWithOffset == 47 .and. drynessIndex > -20.0d0) then
1110          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1111          obsFlags = OR(obsFlags,2**9)
1112          obsFlags = OR(obsFlags,2**7)
1113          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1114            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1115
1116          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1117
1118          if (mwbg_debug) then
1119            write(*,*) stnId(2:9),' DRYNESS INDEX REJECT.',       &
1120                       'CHANNEL=', obsChanNumWithOffset,&
1121                       'INDEX= ',drynessIndex
1122          end if
1123        
1124        end if
1125      end if
1126    end do BODY
1127
1128  end subroutine amsubTest12DrynessIndexCheck
1129
1130  !--------------------------------------------------------------------------
1131  ! amsuaTest13GrodyScatteringIndexCheck
1132  !--------------------------------------------------------------------------
1133  subroutine amsuaTest13GrodyScatteringIndexCheck(sensorIndex, ISCATREJ, qcIndicator, headerIndex, obsSpaceData)
1134    !
1135    !:Purpose: test 13: Grody scattering index check (partial).
1136    !
1137    !          For Scattering Index > 9, reject AMSUA-A channels 1-6 and 15.
1138    !
1139    implicit none
1140
1141    ! Arguments:
1142    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
1143    integer,          intent(in)    :: ISCATREJ(:)     ! rejection channel list
1144    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
1145    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
1146    integer,          intent(in)    :: headerIndex  ! current header Index 
1147
1148    ! Locals:
1149    integer :: testIndex, INDXCAN, landQualifierIndice, terrainTypeIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1150    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1151    real(8) :: ZSEUILSCAT, scatIndexOverWaterObs
1152    character(len=9) :: stnId
1153
1154    testIndex = 13
1155    ZSEUILSCAT = 9.0
1156
1157    scatIndexOverWaterObs = obs_headElem_r(obsSpaceData, OBS_SIO, headerIndex)
1158    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
1159    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1160    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
1161
1162    ! If terrain type is missing, set it to -1 for the QC programs
1163    if (terrainTypeIndice ==  99) terrainTypeIndice = mwbg_intMissing
1164
1165    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1166    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1167
1168    if ( scatIndexOverWaterObs /= MPC_missingValue_R8 ) then
1169      if (landQualifierIndice  == 1 .and. terrainTypeIndice /= 0 .and. &   
1170          scatIndexOverWaterObs > ZSEUILSCAT) then
1171        BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1172          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1173          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1174          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1175
1176          INDXCAN = utl_findloc(ISCATREJ(:),obsChanNumWithOffset)
1177          if ( INDXCAN /= 0 )  then
1178            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1179            obsFlags = OR(obsFlags,2**9)
1180            obsFlags = OR(obsFlags,2**7)
1181            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1182                      rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1183
1184            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1185          end if
1186        end do BODY
1187
1188        if ( mwbg_debug ) then
1189          write(*,*) stnId(2:9), 'Grody scattering index check', &
1190                     ' REJECT. scatIndexOverWaterObs= ', scatIndexOverWaterObs, ' SEUIL= ',ZSEUILSCAT
1191        end if
1192      end if
1193    end if
1194
1195  end subroutine amsuaTest13GrodyScatteringIndexCheck
1196
1197  !--------------------------------------------------------------------------
1198  ! amsubTest13BennartzScatteringIndexCheck
1199  !--------------------------------------------------------------------------
1200  subroutine amsubTest13BennartzScatteringIndexCheck(sensorIndex, scatIndexOverLandObs, modelInterpSeaIce, &
1201                                                     qcIndicator, chanIgnoreInAllskyGenCoeff, &
1202                                                     headerIndex, obsSpaceData, skipTestArr_opt)
1203    !
1204    !:Purpose: test 13: Bennartz scattering index check (full).
1205    !          For Scattering Index: 
1206    !            - > 40 sea ice, 
1207    !            - > 15 sea,
1208    !            - > 0 land reject all AMSUB Channels
1209    !
1210    implicit none
1211
1212    ! Arguments:
1213    integer,           intent(in)    :: sensorIndex                   ! numero de satellite (i.e. indice) 
1214    real(8),           intent(in)    :: scatIndexOverLandObs          ! scattering index over land
1215    real(8),           intent(in)    :: modelInterpSeaIce             ! glace de mer
1216    integer,           intent(inout) :: qcIndicator(:)                ! indicateur du QC par canal
1217    integer,           intent(in)    :: chanIgnoreInAllskyGenCoeff(:) ! channels to exclude from genCoeff
1218    type(struct_obs),  intent(inout) :: obsSpaceData                  ! obspaceData Object
1219    integer,           intent(in)    :: headerIndex                   ! current header Index
1220    logical, optional, intent(in)    :: skipTestArr_opt(:)            ! array to set to skip the test
1221
1222    ! Locals:
1223    integer :: testIndex, chanIndex, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1224    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1225    real(8) :: ZSEUILSCATICE, ZSEUILSCATL, ZSEUILSCATW
1226    real(8) :: scatwUsedForQcThresh, scatwObsFGaveraged, scatwUsedForQC
1227    real(8) :: scatIndexOverWaterObs, scatIndexOverWaterFG
1228    character(len=9) :: stnId
1229    logical :: FULLREJCT, surfTypeIsSea, cldPredMissing
1230    logical, save :: firstCall = .true.
1231
1232    testIndex = 13
1233    if (present(skipTestArr_opt)) then
1234      if (skipTestArr_opt(testIndex)) then
1235        if (firstCall) then
1236          firstCall = .false.
1237          write(*,*) 'amsubTest13BennartzScatteringIndexCheck: skipping this test'
1238        end if
1239        return
1240      end if
1241    end if
1242
1243    ZSEUILSCATICE = 40.0d0
1244    ZSEUILSCATW   = 15.0d0
1245    ZSEUILSCATL   =  0.0d0
1246    FULLREJCT = .false.
1247    surfTypeIsSea = .false.
1248
1249    scatIndexOverWaterObs = obs_headElem_r(obsSpaceData, OBS_SIO, headerIndex)
1250    scatIndexOverWaterFG = obs_headElem_r(obsSpaceData, OBS_SIB, headerIndex)
1251    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
1252    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1253
1254    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1255    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1256
1257    if (landQualifierIndice == 1) then
1258      if ( modelInterpSeaIce > 0.01d0 ) then ! sea ice 
1259        if (scatIndexOverWaterObs /= MPC_missingValue_R8 .and. scatIndexOverWaterObs > ZSEUILSCATICE) then
1260          FULLREJCT = .TRUE.
1261        end if
1262      else                                    ! sea 
1263        surfTypeIsSea = .true.
1264
1265        if (tvs_mwAllskyAssim) then
1266          scatwObsFGaveraged = 0.5d0 * (scatIndexOverWaterObs + scatIndexOverWaterFG)
1267          scatwUsedForQC = scatwObsFGaveraged
1268          scatwUsedForQcThresh = mwbg_maxSiOverWaterThreshold
1269          cldPredMissing = (scatIndexOverWaterObs == MPC_missingValue_R8 .or. &
1270                            scatIndexOverWaterFG == MPC_missingValue_R8)
1271        else
1272          scatwUsedForQC = scatIndexOverWaterObs
1273          scatwUsedForQcThresh = ZSEUILSCATW
1274          cldPredMissing = (scatIndexOverWaterObs == MPC_missingValue_R8)
1275        end if
1276
1277        if (.not. cldPredMissing .and. scatwUsedForQC > scatwUsedForQcThresh) then
1278          FULLREJCT = .TRUE.
1279        end if
1280      end if
1281
1282    else                                      ! land
1283      if ( scatIndexOverLandObs /= mwbg_realMissing .and. scatIndexOverLandObs > ZSEUILSCATL ) then
1284        FULLREJCT = .TRUE.
1285      end if
1286    end if
1287    if ( FULLREJCT )  then
1288      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1289        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1290        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1291        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1292
1293        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1294        obsFlags = OR(obsFlags,2**9)
1295        obsFlags = OR(obsFlags,2**7)
1296        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1297          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1298
1299        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1300      end do BODY
1301
1302      if (mwbg_debug) then
1303        write(*,*)  stnId(2:9), ' BENNARTZ scattering index check REJECT, scatIndexOverWaterObs=', &
1304                    scatIndexOverWaterObs, ', scatIndexOverWaterFG=', scatIndexOverWaterFG, &
1305                    ', scatIndexOverLandObs= ',scatIndexOverLandObs
1306      end if
1307    end if ! if ( FULLREJCT )
1308
1309    if (tvs_mwAllskyAssim .and. surfTypeIsSea) then
1310      scatwObsFGaveraged = 0.5d0 * (scatIndexOverWaterObs + scatIndexOverWaterFG)
1311
1312      ! In all-sky mode, turn on bit=23 for channels in chanIgnoreInAllskyGenCoeff(:)
1313      ! as cloud-affected radiances over sea when there is mismatch between 
1314      ! scatIndexOverWaterObs and scatIndexOverWaterFG (to be used in gen_bias_corr)
1315      if (scatwObsFGaveraged > mwbg_cloudySiThresholdBcorr .or. cldPredMissing) then
1316        BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1317          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1318          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1319          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1320
1321          chanIndex = utl_findloc(chanIgnoreInAllskyGenCoeff(:),obsChanNumWithOffset)
1322          if (chanIndex == 0) cycle BODY2
1323          obsFlags = OR(obsFlags,2**23)
1324
1325          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1326        end do BODY2
1327
1328        if ( mwbg_debug ) then
1329          write(*,*) stnId(2:9),' BENNARTZ scattering index check', &
1330                     ' cloud-affected obs. scatwObsFGaveraged= ', scatwObsFGaveraged, ', threshold= ', &
1331                     mwbg_cloudySiThresholdBcorr
1332        end if
1333      end if
1334    end if
1335    
1336    if (tvs_mwAllskyAssim .and. landQualifierIndice == 1) then
1337      scatwObsFGaveraged = 0.5d0 * (scatIndexOverWaterObs + scatIndexOverWaterFG)
1338      cldPredMissing = (scatIndexOverWaterObs == MPC_missingValue_R8 .or. &
1339                        scatIndexOverWaterFG == MPC_missingValue_R8)
1340
1341      ! In all-sky mode, reject observations over sea if: 
1342      !   - scatwObsFGaveraged can not be computed.
1343      !   - scatwObsFGaveraged smaller than the minimum value
1344      !   - scatwObsFGaveraged greater than the maximum value
1345      ! scatwObsFGaveraged is needed to define obs error.
1346      if (cldPredMissing .or. scatwObsFGaveraged < mwbg_minSiOverWaterThreshold .or. &
1347          scatwObsFGaveraged > mwbg_maxSiOverWaterThreshold) then
1348
1349        loopChannel3: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1350          obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1351          obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1352          obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1353
1354          if (oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex)) then
1355            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1356            obsFlags = OR(obsFlags,2**9)
1357            obsFlags = OR(obsFlags,2**7)
1358            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1359                    rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1360            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1361          end if
1362        end do loopChannel3
1363      end if
1364
1365    end if ! if (tvs_mwAllskyAssim .and. surfTypeIsSea)
1366
1367  end subroutine amsubTest13BennartzScatteringIndexCheck
1368
1369  !--------------------------------------------------------------------------
1370  ! amsuaTest14RogueCheck
1371  !--------------------------------------------------------------------------
1372  subroutine amsuaTest14RogueCheck(sensorIndex, ROGUEFAC, ISFCREJ, qcIndicator, headerIndex, obsSpaceData)
1373    !
1374    !:Purpose: test 14: "Rogue check" for (O-P) Tb residuals out of range.
1375    !          (single/full). Les observations, dont le residu (O-P) 
1376    !          depasse par un facteur (roguefac) l'erreur totale des TOVS.
1377    !
1378    !          N.B.: a reject by any of the 3 surface channels produces the 
1379    !          rejection of AMSUA-A channels 1-5 and 15.
1380    !
1381    implicit none
1382
1383    ! Arguments:
1384    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
1385    real(8),          intent(in)    :: ROGUEFAC(:)     ! rogue factor 
1386    integer,          intent(in)    :: ISFCREJ(:)      ! rejection channel list
1387    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
1388    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
1389    integer,          intent(in)    :: headerIndex  ! current header Index 
1390
1391    ! Locals:
1392    integer :: testIndex, INDXCAN, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd
1393    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1394    real(8) :: XCHECKVAL, clwThresh1, clwThresh2, errThresh1, errThresh2
1395    real(8) :: sigmaObsErrUsed, clwObsFGaveraged
1396    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
1397    real(8) :: ompTb
1398    logical :: SFCREJCT, surfTypeIsWater
1399    character(len=9) :: stnId
1400
1401    testIndex = 14
1402
1403    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
1404    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
1405    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
1406    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1407    surfTypeIsWater = (landQualifierIndice == 1)
1408
1409    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1410    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1411
1412    SFCREJCT = .FALSE.
1413    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1414      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1415      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1416      if (obsChanNumWithOffset /= 20) then
1417        ! using state-dependent obs error only over water.
1418        ! obs over sea-ice will be rejected in test 15.
1419        if ( tvs_mwAllskyAssim .and. oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex) &
1420             .and. surfTypeIsWater ) then
1421          clwThresh1 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,1)
1422          clwThresh2 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,2)
1423          errThresh1 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,1)
1424          errThresh2 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,2)
1425          clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
1426          if (cloudLiquidWaterPathObs == mwbg_realMissing .or. cloudLiquidWaterPathFG == mwbg_realMissing) then
1427            sigmaObsErrUsed = MPC_missingValue_R8
1428          else
1429            sigmaObsErrUsed = calcStateDepObsErr(clwThresh1,clwThresh2,errThresh1, &
1430                                                      errThresh2,clwObsFGaveraged)
1431          end if
1432        else
1433          sigmaObsErrUsed = oer_toverrst(obsChanNumWithOffset,sensorIndex)
1434        end if
1435        ! For sigmaObsErrUsed=MPC_missingValue_R8 (cloudLiquidWaterPathObs[FG]=mwbg_realMissing
1436        ! in all-sky mode), the observation is already rejected in test 12.
1437        XCHECKVAL = ROGUEFAC(obsChanNumWithOffset) * sigmaObsErrUsed
1438        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1439        ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
1440
1441        if (ompTb /= mwbg_realMissing .and. &
1442            ABS(ompTb) >= XCHECKVAL .and. &
1443            sigmaObsErrUsed /= MPC_missingValue_R8) then
1444          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1445          obsFlags = OR(obsFlags,2**9)
1446          obsFlags = OR(obsFlags,2**16)
1447          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1448              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1 
1449
1450          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1451
1452          if ( mwbg_debug ) then
1453            write(*,*) stnId(2:9),'ROGUE CHECK REJECT.NO.', &
1454                       ' CHANNEL= ',obsChanNumWithOffset, &
1455                       ' CHECK VALUE= ',XCHECKVAL, &
1456                       ' TBOMP= ',ompTb
1457          end if
1458          if (obsChanNumWithOffset == 28 .or. obsChanNumWithOffset == 29 .or. obsChanNumWithOffset == 30) SFCREJCT = .TRUE.
1459        end if ! if (ompTb /= mwbg_realMissing
1460
1461      end if ! if (obsChanNumWithOffset /= 20)
1462    end do BODY
1463
1464    if ( SFCREJCT ) then
1465      BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1466        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1467        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1468        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1469
1470        INDXCAN = utl_findloc(ISFCREJ(:),obsChanNumWithOffset)
1471        if ( INDXCAN /= 0 )  then
1472          if ( qcIndicator(obsChanNum) /= testIndex ) then
1473            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1474            obsFlags = OR(obsFlags,2**9)
1475            obsFlags = OR(obsFlags,2**16)
1476            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1477                      rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1478
1479            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1480          end if
1481        end if
1482      end do BODY2
1483    end if ! SFCREJCT
1484
1485  end subroutine amsuaTest14RogueCheck
1486
1487  !--------------------------------------------------------------------------
1488  ! amsubTest14RogueCheck
1489  !--------------------------------------------------------------------------
1490  subroutine amsubTest14RogueCheck(sensorIndex, ROGUEFAC, ICH2OMPREJ, qcIndicator, &
1491                                   headerIndex, obsSpaceData, skipTestArr_opt)
1492    !
1493    !:Purpose: test 14: "Rogue check" for (O-P) Tb residuals out of range. (single)
1494    !          Also, remove CH2,3,4,5 if CH2 |O-P|>5K (partial)
1495    !
1496    implicit none
1497
1498    ! Arguments:
1499    integer,           intent(in)    :: sensorIndex        ! numero de satellite (i.e. indice) 
1500    real(8),           intent(in)    :: ROGUEFAC(:)        ! rogue factor 
1501    integer,           intent(in)    :: ICH2OMPREJ(:)      ! rejection channel list
1502    integer,           intent(inout) :: qcIndicator(:)     ! indicateur du QC par canal
1503    type(struct_obs),  intent(inout) :: obsSpaceData       ! obspaceData Object
1504    integer,           intent(in)    :: headerIndex        ! current header Index
1505    logical, optional, intent(in)    :: skipTestArr_opt(:) ! array to set to skip the test
1506
1507    ! Locals:
1508    integer :: testIndex, INDXCAN, landQualifierIndice, terrainTypeIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1509    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1510    real(8) :: XCHECKVAL, siThresh1, siThresh2, errThresh1, errThresh2
1511    real(8) :: sigmaObsErrUsed, scatwObsFGaveraged 
1512    real(8) :: scatIndexOverWaterObs, scatIndexOverWaterFG
1513    real(8) :: ompTb
1514    character(len=9) :: stnId
1515    logical :: CH2OMPREJCT, ch2OmpRejectInAllsky, channelIsAllsky, surfTypeIsWater
1516    logical, save :: firstCall = .true.
1517
1518    testIndex = 14
1519    if (present(skipTestArr_opt)) then
1520      if (skipTestArr_opt(testIndex)) then
1521        if (firstCall) then
1522          firstCall = .false.
1523          write(*,*) 'amsubTest14RogueCheck: skipping this test'
1524        end if
1525        return
1526      end if
1527    end if
1528
1529    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex)
1530    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
1531
1532    ! If terrain type is missing, set it to -1 for the QC programs
1533    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
1534
1535    surfTypeIsWater = (landQualifierIndice == 1 .and. terrainTypeIndice /= 0)
1536    ch2OmpRejectInAllsky = .false.
1537    CH2OMPREJCT = .FALSE.
1538
1539    scatIndexOverWaterObs = obs_headElem_r(obsSpaceData, OBS_SIO, headerIndex)
1540    scatIndexOverWaterFG = obs_headElem_r(obsSpaceData, OBS_SIB, headerIndex)
1541    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1542
1543    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1544    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1545
1546    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1547      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1548      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1549      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1550      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
1551
1552      if ( obsChanNumWithOffset /= 20 ) then
1553        channelIsAllsky = (tvs_mwAllskyAssim .and. &
1554                           oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex) .and. &
1555                           surfTypeIsWater)
1556        ! using state-dependent obs error only over water.
1557        if (channelIsAllsky) then
1558          siThresh1 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,1)
1559          siThresh2 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,2)
1560          errThresh1 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,1)
1561          errThresh2 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,2)
1562          scatwObsFGaveraged = 0.5 * (scatIndexOverWaterObs + scatIndexOverWaterFG)
1563          if (scatIndexOverWaterObs == MPC_missingValue_R8 .or. &
1564              scatIndexOverWaterFG == MPC_missingValue_R8) then
1565            sigmaObsErrUsed = MPC_missingValue_R8
1566          else
1567            sigmaObsErrUsed = calcStateDepObsErr(siThresh1,siThresh2,errThresh1, &
1568                                                    errThresh2,scatwObsFGaveraged)
1569          end if
1570        else
1571          sigmaObsErrUsed = oer_toverrst(obsChanNumWithOffset,sensorIndex)
1572        end if
1573        ! For sigmaObsErrUsed=MPC_missingValue_R8 (scatIndexOverWaterObs[FG]=mwbg_realMissing
1574        ! in all-sky mode), the observation is already rejected in test 13.
1575        XCHECKVAL = ROGUEFAC(obsChanNumWithOffset) * sigmaObsErrUsed
1576        if (ompTb /= mwbg_realMissing .and. &
1577            abs(ompTb) >= XCHECKVAL .and. &
1578            sigmaObsErrUsed /= MPC_missingValue_R8) then
1579          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1580          obsFlags = OR(obsFlags,2**9)
1581          obsFlags = OR(obsFlags,2**16)
1582          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1583              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1584
1585          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1586
1587          ch2OmpRejectInAllSky = (channelIsAllsky .and. obsChanNumWithOffset == 44)
1588
1589          if (mwbg_debug) then
1590            write(*,*) stnId(2:9),'ROGUE CHECK REJECT.NO.', &
1591                       ' CHANNEL= ',obsChanNumWithOffset, &
1592                       ' CHECK VALUE= ',XCHECKVAL, &
1593                       ' TBOMP= ',ompTb
1594          end if
1595        end if ! if (ompTb /= mwbg_realMissing
1596
1597        if (obsChanNumWithOffset == 44 .and. ompTb /= mwbg_realMissing) then
1598          if (channelIsAllsky) then
1599            if (ch2OmpRejectInAllSky) CH2OMPREJCT = .true.
1600          else
1601            if (abs(ompTb) >= 5.0) CH2OMPREJCT = .true.
1602          end if
1603        end if ! if (obsChanNumWithOffset == 44
1604      end if ! if ( obsChanNumWithOffset /= 20 )
1605    end do BODY
1606
1607    if (CH2OMPREJCT .and. landQualifierIndice == 1 .and. terrainTypeIndice /= 0) then
1608      BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1609        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1610        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1611        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1612
1613        INDXCAN = utl_findloc(ICH2OMPREJ(:),obsChanNumWithOffset)
1614        if ( INDXCAN /= 0 )  then
1615          if ( qcIndicator(obsChanNum) /= testIndex ) then
1616            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1617            obsFlags = OR(obsFlags,2**9)
1618            obsFlags = OR(obsFlags,2**16)
1619            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1620                  rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
1621
1622            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1623          end if
1624        end if
1625      end do BODY2
1626    end if ! if (CH2OMPREJCT
1627
1628  end subroutine amsubTest14RogueCheck
1629
1630  !--------------------------------------------------------------------------
1631  ! amsuABTest15ChannelSelectionWithTovutil
1632  !--------------------------------------------------------------------------
1633  subroutine amsuABTest15ChannelSelectionWithTovutil(sensorIndex, modelInterpSeaIce, ISFCREJ2, qcIndicator, &
1634                                                     headerIndex, obsSpaceData)
1635    !
1636    !:Purpose: test 15: Channel Selection using array oer_tovutil(chan,sat):
1637    !            - = 0 blacklisted, 
1638    !            - = 1 assmilate, 
1639    !            - = 2 assimilate over open water only
1640    !          We set QC flag bits 7 and 9 ON for channels with oer_tovutil=2
1641    !          over land or sea-ice.
1642    !
1643    !          We set QC flag bits 7 and 9 ON for channels
1644    !          1-3, 15 over land or sea-ice REGARDLESS of oer_tovutil value 
1645    !          (but oer_tovutil=0 always for these unassimilated channels).
1646    !
1647    implicit none
1648
1649    ! Arguments:
1650    integer,          intent(in)    :: sensorIndex       ! numero de satellite (i.e. indice) 
1651    real(8),          intent(in)    :: modelInterpSeaIce ! gl
1652    integer,          intent(in)    :: ISFCREJ2(:)       ! rejection channel list
1653    integer,          intent(inout) :: qcIndicator(:)    ! indicateur du QC par canal
1654    type(struct_obs), intent(inout) :: obsSpaceData   ! obspaceData Object
1655    integer,          intent(in)    :: headerIndex    ! current header Index 
1656
1657    ! Locals:
1658    integer :: testIndex, ITRN, INDXCAN, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1659    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
1660    integer :: terrainTypeIndice
1661    logical :: SFCREJCT
1662    character(len=9) :: stnId
1663
1664    testIndex = 15
1665
1666    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
1667    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1668    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
1669
1670    ! If terrain type is missing, set it to -1 for the QC programs
1671    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
1672
1673    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1674    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1675
1676    ITRN = terrainTypeIndice
1677    if (landQualifierIndice  == 1 .and. terrainTypeIndice == mwbg_intMissing .and. &
1678        modelInterpSeaIce >= 0.01d0) then
1679      ITRN = 0
1680    end if        
1681    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1682      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1683      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1684      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1685
1686      INDXCAN = utl_findloc(ISFCREJ2(:),obsChanNumWithOffset)
1687      if ( INDXCAN /= 0 )  then
1688        if (landQualifierIndice  == 0 .or. ITRN == 0)  then
1689          obsFlags = OR(obsFlags,2**9)
1690          obsFlags = OR(obsFlags,2**7)
1691
1692          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1693        end if
1694      end if
1695      if ( oer_tovutil(obsChanNumWithOffset,sensorIndex) /= 1 ) then
1696        SFCREJCT = .FALSE.
1697        if ( oer_tovutil(obsChanNumWithOffset,sensorIndex) == 0 ) then
1698          SFCREJCT = .TRUE.
1699          obsFlags = OR(obsFlags,2**11)
1700        else 
1701          if (landQualifierIndice == 0 .or. ITRN == 0)  then
1702            SFCREJCT = .TRUE.
1703            obsFlags = OR(obsFlags,2**9)
1704            obsFlags = OR(obsFlags,2**7)
1705          end if
1706        end if
1707        if ( SFCREJCT ) then
1708          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1709          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = & 
1710              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1 
1711
1712          if ( mwbg_debug ) then
1713              write(*,*)stnId(2:9),'CHANNEL REJECT: ', &
1714                    ' CHANNEL= ',obsChanNumWithOffset
1715          end if
1716        end if
1717      end if
1718
1719      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1720
1721      if ( mwbg_debug ) write(*,*) 'qcIndicator = ', qcIndicator(obsChanNum)
1722    end do BODY
1723
1724  end subroutine amsuABTest15ChannelSelectionWithTovutil
1725
1726  !--------------------------------------------------------------------------
1727  ! amsuaTest16ExcludeExtremeScattering
1728  !--------------------------------------------------------------------------
1729  subroutine amsuaTest16ExcludeExtremeScattering(sensorIndex, qcIndicator, headerIndex, obsSpaceData)
1730    !
1731    !:Purpose: Exclude radiances affected extreme scattering in deep convective region.
1732    !          For channel 5, if BT_cld-BT_clr < -0.5 OR O-BT_clr < -0.5, reject channels 4-5.
1733    !
1734    implicit none
1735
1736    ! Arguments:
1737    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
1738    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
1739    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
1740    integer,          intent(in)    :: headerIndex  ! current header Index 
1741
1742    ! Locals:
1743    integer :: testIndex, INDXCAN, landQualifierIndice, bodyIndex, bodyIndexBeg, bodyIndexEnd 
1744    integer :: obsChanNum, obsChanNumWithOffset, obsFlags, codtyp
1745    real(8) :: BTcloudy, simulatedCloudEffect, observedCloudEffect
1746    real(8) :: obsTb, btClear, ompTb
1747    logical :: surfTypeIsWater, rejectLowPeakingChannels
1748    character(len=9) :: stnId
1749
1750    integer, dimension(2), parameter :: lowPeakingChannelsList = (/ 31, 32 /)
1751
1752    testIndex = 16
1753
1754    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex)
1755    surfTypeIsWater = (landQualifierIndice == 1)
1756    if (.not. (tvs_mwAllskyAssim .and. surfTypeIsWater)) return 
1757
1758    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
1759
1760    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
1761    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1762    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1763    
1764    rejectLowPeakingChannels = .false.
1765    loopChannel2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1766      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1767      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
1768      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1769      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
1770      obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
1771      if (tvs_isInstrumAllskyTtAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
1772        btClear = obs_bodyElem_r(obsSpaceData, OBS_BTCL, bodyIndex)
1773      else
1774        btClear = mwbg_realMissing
1775      end if
1776
1777      if ( obsChanNumWithOffset /= 32 ) cycle loopChannel2
1778
1779      BTcloudy = obsTb - ompTb
1780      simulatedCloudEffect = BTcloudy - btClear
1781      observedCloudEffect = obsTb - btClear
1782      if ( simulatedCloudEffect < -0.5d0 .or. observedCloudEffect < -0.5d0 ) then
1783        rejectLowPeakingChannels = .true.
1784      end if
1785
1786      exit loopChannel2
1787    end do loopChannel2
1788
1789    ! reject channel 4-5
1790    if ( rejectLowPeakingChannels ) then
1791      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1792        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1793        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)      
1794        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
1795
1796        INDXCAN = utl_findloc(lowPeakingChannelsList(:),obsChanNumWithOffset)
1797        if ( INDXCAN /= 0 )  then
1798          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
1799          obsFlags = OR(obsFlags,2**9)
1800          obsFlags = OR(obsFlags,2**16)
1801          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
1802              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1 
1803
1804          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1805        end if
1806
1807        if ( mwbg_debug ) then
1808          write(*,*) stnId(2:9),' extreme scattering check reject: ', &
1809                     ' obs location index = ', obsChanNum, &
1810                     ' channel = 1-5'
1811        end if
1812      end do BODY
1813    end if ! rejectLowPeakingChannels
1814
1815  end subroutine amsuaTest16ExcludeExtremeScattering
1816
1817  !--------------------------------------------------------------------------
1818  ! mwbg_tovCheckAmsua
1819  !--------------------------------------------------------------------------
1820  subroutine mwbg_tovCheckAmsua(qcIndicator, sensorIndex, modelInterpLandFrac, modelInterpTerrain, &
1821                                modelInterpSeaIce, RESETQC, headerIndex, obsSpaceData)
1822    !
1823    !:Purpose: Effectuer le controle de qualite des radiances tovs.
1824    !
1825    !          Quinze tests sont effectues menant aux erreurs suivantes:
1826    !            - 1) topography reject,
1827    !            - 2) invalid land/sea qualifier,
1828    !            - 3) invalid terrain type,
1829    !            - 4) invalid field of view number,
1830    !            - 5) satellite zenith angle out of range,
1831    !            - 6) inconsistent field of view and sat. zenith angle,
1832    !            - 7) inconsistent land/sea qualifier and model mask,
1833    !            - 8) inconsistent terrain type and model ice, (NOT USED)
1834    !            - 9) uncorrected radiance,
1835    !            - 10) rejected by RTTOV,
1836    !            - 11) radiance gross check failure,
1837    !            - 12) cloud liquid water reject,
1838    !            - 13) scattering index reject,
1839    !            - 14) radiance residual rogue check failure,
1840    !            - 15) channel reject (channel selection).
1841    !            - **) set terrain type to sea ice given certain conditions
1842    !
1843    implicit none
1844
1845    ! Arguments:
1846    type(struct_obs),     intent(inout) :: obsSpaceData        ! obspaceData Object
1847    integer,              intent(in)    :: headerIndex         ! current header Index 
1848    integer,              intent(in)    :: sensorIndex         ! numero de satellite (i.e. indice)
1849    real(8),              intent(in)    :: modelInterpLandFrac ! masque terre/mer du modele
1850    real(8),              intent(in)    :: modelInterpTerrain  ! topographie du modele
1851    real(8),              intent(in)    :: modelInterpSeaIce   ! etendue de glace du modele
1852    logical,              intent(in)    :: RESETQC             ! reset du controle de qualite?
1853    integer, allocatable, intent(out)   :: qcIndicator(:)      ! indicateur controle de qualite tovs par canal (=0 ok, >0 rejet)
1854
1855    ! Locals:
1856    integer, parameter :: maxScanAngleAMSU = 30 
1857    real(8), parameter :: cloudyClwThreshold = 0.3d0
1858    real(8), parameter :: ZANGL = 117.6/maxScanAngleAMSU
1859    integer :: KCHKPRF, JI, rain, snow, newInformationFlag, actualNumChannel
1860    integer :: ICLWREJ(6), ISFCREJ(6), ISFCREJ2(4), ISCATREJ(7), channelForTopoFilter(2)
1861    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsFlags
1862    real(8), allocatable :: GROSSMIN(:), GROSSMAX(:), ROGUEFAC(:)
1863    real(8) :: EPSILON, tb23, tb31, tb50, tb53, tb89
1864    real(8) :: tb23FG, tb31FG, tb50FG, tb53FG, tb89FG 
1865    real(8) :: ice, tpw, scatIndexOverLandObs, altitudeForTopoFilter(2)
1866    logical, save :: LLFIRST = .true.
1867
1868    EPSILON = 0.01
1869
1870    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1871    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
1872
1873    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
1874    allocate(ROGUEFAC(actualNumChannel+tvs_channelOffset(sensorIndex)))
1875    ROGUEFAC(:) =(/ 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
1876                    4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
1877                    4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 2.0d0, 2.0d0, 2.0d0, &
1878                    3.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
1879                    4.0d0, 2.0d0/)
1880    ICLWREJ(:) = (/ 28, 29, 30, 31, 32, 42 /)
1881    ISFCREJ(:) = (/ 28, 29, 30, 31, 32, 42 /)
1882    ISCATREJ(:) = (/ 28, 29, 30, 31, 32, 33, 42 /)
1883    ISFCREJ2(:) = (/ 28, 29, 30, 42 /)
1884
1885    allocate(GROSSMIN(actualNumChannel+tvs_channelOffset(sensorIndex)))
1886    GROSSMIN(:) = (/ 200.0d0, 190.0d0, 190.0d0, 180.0d0, 180.0d0, 180.0d0, 170.0d0, &
1887                     170.0d0, 180.0d0, 170.0d0, 170.0d0, 170.0d0, 180.0d0, 180.0d0, &
1888                     180.0d0, 180.0d0, 170.0d0, 180.0d0, 180.0d0, 000.0d0, 120.0d0, &
1889                     190.0d0, 180.0d0, 180.0d0, 180.0d0, 190.0d0, 200.0d0, 120.0d0, &
1890                     120.0d0, 160.0d0, 190.0d0, 190.0d0, 200.0d0, 190.0d0, 180.0d0, &
1891                     180.0d0, 180.0d0, 180.0d0, 190.0d0, 190.0d0, 200.0d0, 130.0d0 /)
1892
1893    allocate(GROSSMAX(actualNumChannel+tvs_channelOffset(sensorIndex)))
1894    GROSSMAX(:) = (/ 270.0d0, 250.0d0, 250.0d0, 250.0d0, 260.0d0, 280.0d0, 290.0d0, &
1895                     320.0d0, 300.0d0, 320.0d0, 300.0d0, 280.0d0, 320.0d0, 300.0d0, &
1896                     290.0d0, 280.0d0, 330.0d0, 350.0d0, 350.0d0, 000.0d0, 310.0d0, &
1897                     300.0d0, 250.0d0, 250.0d0, 270.0d0, 280.0d0, 290.0d0, 310.0d0, &
1898                     310.0d0, 310.0d0, 300.0d0, 300.0d0, 260.0d0, 250.0d0, 250.0d0, &
1899                     250.0d0, 260.0d0, 260.0d0, 270.0d0, 280.0d0, 290.0d0, 330.0d0 /)  
1900    channelForTopoFilter(:) = (/ 33, 34 /)
1901    altitudeForTopoFilter(:) = (/ 250.0d0, 2000.0d0/)
1902
1903    ! Allocation
1904    allocate(qcIndicator(actualNumChannel))
1905    qcIndicator(:) = 0
1906
1907    ! Initialisation, la premiere fois seulement!
1908    if (LLFIRST) then
1909       rejectionCodArray(:,:,:) = 0
1910       LLFIRST = .FALSE.
1911    end if
1912    ! fill newInformationFlag with zeros ONLY for consistency with ATMS
1913    newInformationFlag = 0
1914    if ( RESETQC ) then
1915      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1916        obsFlags = 0
1917        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
1918      end do BODY
1919    end if
1920
1921    ! Grody parameters are   extract required channels:
1922    call extractParamForGrodyRun (tb23,   tb31,   tb50,   tb53,   tb89, &
1923                                  tb23FG, tb31FG, tb50FG, tb53FG, tb89FG, &
1924                                  headerIndex, sensorIndex, obsSpaceData)
1925    
1926    !  Run Grody AMSU-A algorithms.
1927    call grody (tb23, tb31, tb50, tb53, tb89, tb23FG, tb31FG, &
1928                ice, tpw, &
1929                rain, snow, scatIndexOverLandObs, &
1930                headerIndex, obsSpaceData)   
1931
1932    ! 10) test 10: RTTOV reject check (single)
1933    ! Rejected datum flag has bit #9 on.
1934    call amsuABTest10RttovRejectCheck (sensorIndex, RESETQC, qcIndicator, headerIndex, obsSpaceData)
1935
1936    ! 1) test 1: Topography check (partial)
1937    ! Channel 6 is rejected for topography >  250m.
1938    ! Channel 7 is rejected for topography > 2000m.
1939    call amsuABTest1TopographyCheck (sensorIndex, modelInterpTerrain, channelForTopoFilter, altitudeForTopoFilter, &
1940                                     qcIndicator, headerIndex, obsSpaceData)
1941 
1942    ! 2) test 2: "Land/sea qualifier" code check (full)
1943    ! allowed values are: 0 land, 1 sea, 2 coast.
1944    call amsuABTest2LandSeaQualifierCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
1945
1946    ! 3) test 3: "Terrain type" code check (full)
1947    ! allowed values are: -1 missing, 0 sea-ice, 1 snow on land.
1948    call amsuABTest3TerrainTypeCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
1949 
1950    ! 4) test 4: Field of view number check (full)
1951    ! Field of view acceptable range is [1,maxScanAngleAMSU]  for AMSU footprints.
1952    call amsuABTest4FieldOfViewCheck (sensorIndex, maxScanAngleAMSU, qcIndicator, &
1953                                      headerIndex, obsSpaceData)
1954
1955    ! 5) test 5: Satellite zenith angle check (full)
1956    ! Satellite zenith angle acceptable range is [0.,60.].
1957    call amsuABTest5ZenithAngleCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
1958
1959    ! 6) test 6: "Sat. zenith angle"/"field of view" consistency check.  (full)
1960    ! Acceptable difference between "Satellite zenith angle"  and
1961    ! "approximate angle computed from field of view number" is 1.8 degrees.
1962    call amsuABTest6ZenAngleAndFovConsistencyCheck (sensorIndex, ZANGL, maxScanAngleAMSU, qcIndicator, &
1963                                                    headerIndex, obsSpaceData) 
1964
1965    ! 7) test 7: "Land/sea qual."/"model land/sea" consistency check.    (full)
1966    ! Acceptable conditions are:
1967    !       a) both over ocean (landQualifierIndice=1; mg<0.01), new threshold 0.20, jh dec 2000,
1968    !       b) both over land  (landQualifierIndice=0; mg>0.80), new threshold 0.50, jh dec 2000.
1969    ! Other conditions are unacceptable.
1970    call amsuABTest7landSeaQualifyerAndModelLandSeaConsistencyCheck (sensorIndex, modelInterpLandFrac, &
1971                                                                     qcIndicator, headerIndex, obsSpaceData)
1972
1973    ! 8) test 8: "Terrain type"/"Land/sea qual."/"model ice" consistency check. (full)
1974    ! Unacceptable conditions are:
1975    !        a) terrain is sea-ice and model has no ice(terrainTypeIndice=0; gl<0.01).
1976    !        b) terrain is sea-ice and land/sea qualifier is land (terrainTypeIndice=0; landQualifierIndice=0).
1977    !        c) terrain is snow on land and land/sea qualifier is sea (terrainTypeIndice=1; landQualifierIndice=1).
1978    !        d) terrain is missing, land/sea qualifier is sea and model has ice(terrainTypeIndice=-1; landQualifierIndice=1; gl>0.01). (enleve jh, jan 2001)
1979    ! NOT doNE ANYMORE 
1980    
1981    ! 9) test 9: Uncorrected Tb check (single)
1982    ! Uncorrected datum (flag bit #6 off). In this case switch bit 11 ON.
1983    call amsuABTest9UncorrectedTbCheck (sensorIndex, RESETQC, qcIndicator, headerIndex, obsSpaceData) 
1984
1985    ! 11) test 11: Radiance observation "Gross" check (single) 
1986    !  Change this test from full to single. jh nov 2000.
1987    call amsuABTest11RadianceGrossValueCheck (sensorIndex, GROSSMIN, GROSSMAX, qcIndicator, &
1988                                              headerIndex, obsSpaceData)
1989
1990    ! 12) test 12: Grody cloud liquid water check (partial)
1991    ! For Cloud Liquid Water > clwQcThreshold, reject AMSUA-A channels 1-5 and 15.
1992    call amsuaTest12GrodyClwCheck (sensorIndex, ICLWREJ, qcIndicator, headerIndex, obsSpaceData)
1993
1994    ! 13) test 13: Grody scattering index check (partial)
1995    ! For Scattering Index > 9, reject AMSUA-A channels 1-6 and 15.
1996    call amsuaTest13GrodyScatteringIndexCheck (sensorIndex, ISCATREJ, qcIndicator, headerIndex, obsSpaceData)
1997
1998    ! 14) test 14: "Rogue check" for (O-P) Tb residuals out of range. (single/full)
1999    ! Les observations, dont le residu (O-P) depasse par un facteur (roguefac) l'erreur totale des TOVS.
2000    ! N.B.: a reject by any of the 3 surface channels produces the rejection of AMSUA-A channels 1-5 and 15. 
2001    call amsuaTest14RogueCheck (sensorIndex, ROGUEFAC, ISFCREJ, qcIndicator, headerIndex, obsSpaceData)
2002
2003    ! 15) test 15: Channel Selection using array oer_tovutil(chan,sat)
2004    !  oer_tovutil = 0 (blacklisted)
2005    !                1 (assmilate)
2006    !                2 (assimilate over open water only)
2007    !
2008    !  We also set QC flag bits 7 and 9 ON for channels with oer_tovutil=2 
2009    !  over land or sea-ice
2010    !    and 
2011    !  we set QC flag bits 7 and 9 ON for channels 1-3,15 over land
2012    !  or sea-ice REGARDLESS of oer_tovutil value (but oer_tovutil=0 always for
2013    !  these unassimilated channels).
2014    call amsuABTest15ChannelSelectionWithTovutil (sensorIndex, modelInterpSeaIce, ISFCREJ2, qcIndicator, &
2015                                                  headerIndex, obsSpaceData)
2016
2017    ! 16) test 16: exclude radiances affected by extreme scattering in deep convective region in all-sky mode.
2018    call amsuaTest16ExcludeExtremeScattering(sensorIndex, qcIndicator, headerIndex, obsSpaceData) 
2019
2020    !  Synthese de la controle de qualite au niveau de chaque point
2021    !  d'observation. Code:
2022    !            =0, aucun rejet,
2023    !            >0, au moins un canal rejete.
2024
2025    KCHKPRF = 0
2026    do JI = 1, actualNumChannel
2027      KCHKPRF = MAX(KCHKPRF,qcIndicator(JI))
2028    end do
2029
2030    if ( mwbg_debug ) write(*,*) 'KCHKPRF = ', KCHKPRF
2031
2032    ! reset global marker flag (55200) and mark it if observtions are rejected
2033    call resetQcCases(RESETQC, KCHKPRF, headerIndex, obsSpaceData)
2034
2035    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
2036
2037    !###############################################################################
2038    ! FINAL STEP: set terrain type to sea ice given certain conditions
2039    !###############################################################################
2040    call setTerrainTypeToSeaIce(modelInterpSeaIce, headerIndex, obsSpaceData)
2041
2042  end subroutine mwbg_tovCheckAmsua
2043
2044  !--------------------------------------------------------------------------
2045  ! mwbg_tovCheckAmsub
2046  !--------------------------------------------------------------------------
2047  subroutine mwbg_tovCheckAmsub(qcIndicator, sensorIndex, modelInterpLandFrac, modelInterpTerrain, &
2048                                modelInterpSeaIce, RESETQC, headerIndex, obsSpaceData)
2049    !
2050    !:Purpose: Effectuer le controle de qualite des radiances tovs.
2051    !
2052    !          Quinze tests sont effectues menant aux erreurs suivantes:
2053    !            - 1) topography reject,
2054    !            - 2) invalid land/sea qualifier,
2055    !            - 3) invalid terrain type,
2056    !            - 4) invalid field of view number,
2057    !            - 5) satellite zenith angle out of range,
2058    !            - 6) inconsistent field of view and sat. zenith angle,
2059    !            - 7) inconsistent land/sea qualifier and model mask,
2060    !            - 8) inconsistent terrain type and model ice
2061    !            - 9) uncorrected radiance,
2062    !            - 10) rejected by RTTOV,
2063    !            - 11) radiance gross check failure,
2064    !            - 12) drynes index reject
2065    !            - 13) scattering index reject,
2066    !            - 14) radiance residual rogue check failure,
2067    !            - 15) channel reject (channel selection).
2068    !            - **) set terrain type to sea ice given certain conditions
2069    !
2070    implicit none 
2071
2072    ! Arguments:
2073    type(struct_obs),     intent(inout) :: obsSpaceData        ! obspaceData Object
2074    integer,              intent(in)    :: headerIndex         ! current header Index 
2075    integer,              intent(in)    :: sensorIndex         ! numero de satellite (i.e. indice)
2076    real(8),              intent(in)    :: modelInterpLandFrac ! masque terre/mer du modele
2077    real(8),              intent(in)    :: modelInterpTerrain  ! topographie du modele
2078    real(8),              intent(in)    :: modelInterpSeaIce   ! etendue de glace du modele
2079    logical,              intent(in)    :: RESETQC             ! reset du controle de qualite?
2080    integer, allocatable, intent(out)   :: qcIndicator(:)      ! indicateur controle de qualite tovs par canal (=0 ok, >0 rejet)
2081
2082    ! Locals:
2083    integer, parameter  :: maxScanAngleAMSU = 90 
2084    real(8), parameter  :: ZANGL =  117.6d0 / maxScanAngleAMSU
2085    
2086    integer :: KCHKPRF, JI, newInformationFlag, actualNumChannel
2087    integer :: ISFCREJ(2), ICH2OMPREJ(4), ISFCREJ2(1), chanIgnoreInAllskyGenCoeff(5), channelForTopoFilter(3)
2088    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsFlags
2089    real(8), allocatable :: GROSSMIN(:), GROSSMAX(:), ROGUEFAC(:)
2090    real(8) :: tb89, tb150, tb1831, tb1832, tb1833
2091    real(8) :: tb89FG, tb150FG, tb89FgClear, tb150FgClear, scatIndexOverLandObs
2092    real(8) :: altitudeForTopoFilter(3)
2093    logical, save :: LLFIRST = .true.
2094
2095    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
2096    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
2097
2098    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
2099    allocate(ROGUEFAC(actualNumChannel+tvs_channelOffset(sensorIndex)))
2100    ROGUEFAC(:) =(/ 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
2101                    4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
2102                    4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 2.0d0, 2.0d0, 2.0d0, &
2103                    3.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, &
2104                    4.0d0, 2.0d0, 2.0d0, 2.0d0, 4.0d0, 4.0d0, 4.0d0 /)
2105
2106    ISFCREJ(:) = (/ 43, 44 /)
2107    ISFCREJ2(:) = (/ 43 /)
2108    ICH2OMPREJ(:) = (/ 44, 45, 46, 47 /)
2109    allocate(GROSSMIN(actualNumChannel+tvs_channelOffset(sensorIndex)))
2110    GROSSMIN(:) = (/ 200.0d0, 190.0d0, 190.0d0, 180.0d0, 180.0d0, 180.0d0, 170.0d0, &
2111                     170.0d0, 180.0d0, 170.0d0, 170.0d0, 170.0d0, 180.0d0, 180.0d0, &
2112                     180.0d0, 180.0d0, 170.0d0, 180.0d0, 180.0d0, 000.0d0, 120.0d0, &
2113                     190.0d0, 180.0d0, 180.0d0, 180.0d0, 190.0d0, 200.0d0, 120.0d0, &
2114                     120.0d0, 160.0d0, 190.0d0, 190.0d0, 200.0d0, 190.0d0, 180.0d0, &
2115                     180.0d0, 180.0d0, 180.0d0, 190.0d0, 190.0d0, 200.0d0, 130.0d0, &
2116                     130.0d0, 130.0d0, 130.0d0, 130.0d0, 130.0d0 /)
2117    allocate(GROSSMAX(actualNumChannel+tvs_channelOffset(sensorIndex)))
2118    GROSSMAX(:) = (/ 270.0d0, 250.0d0, 250.0d0, 250.0d0, 260.0d0, 280.0d0, 290.0d0, &
2119                     320.0d0, 300.0d0, 320.0d0, 300.0d0, 280.0d0, 320.0d0, 300.0d0, &
2120                     290.0d0, 280.0d0, 330.0d0, 350.0d0, 350.0d0, 000.0d0, 310.0d0, &
2121                     300.0d0, 250.0d0, 250.0d0, 270.0d0, 280.0d0, 290.0d0, 310.0d0, &
2122                     310.0d0, 310.0d0, 300.0d0, 300.0d0, 260.0d0, 250.0d0, 250.0d0, &
2123                     250.0d0, 260.0d0, 260.0d0, 270.0d0, 280.0d0, 290.0d0, 330.0d0, &
2124                     330.0d0, 330.0d0, 330.0d0, 330.0d0, 330.0d0 /)  
2125
2126    channelForTopoFilter(:) = (/ 45, 46, 47 /)
2127    altitudeForTopoFilter(:) = (/ 2500.0d0, 2000.0d0, 1000.0d0 /)
2128
2129    ! Channels excluded from genCoeff in all-sky mode
2130    chanIgnoreInAllskyGenCoeff(:) = (/43, 44, 45, 46, 47/)
2131
2132    ! Allocation
2133    allocate(qcIndicator(actualNumChannel))
2134    qcIndicator(:) = 0
2135
2136    ! Initialisation, la premiere fois seulement!
2137    if (LLFIRST) then
2138      rejectionCodArray(:,:,:) = 0
2139      LLFIRST = .FALSE.
2140    end if
2141    ! fill newInformationFlag with zeros ONLY for consistency with ATMS
2142    newInformationFlag = 0
2143    if ( RESETQC ) then
2144      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
2145        obsFlags = 0
2146        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
2147      end do BODY
2148    end if
2149
2150    ! Bennartz parameters are   extract required channels:
2151    call extractParamForBennartzRun (tb89, tb150, tb1831, tb1832, tb1833, &
2152                                     tb89FG, tb150FG, tb89FgClear, tb150FgClear, &
2153                                     headerIndex, sensorIndex, obsSpaceData)
2154    
2155    !  Run Bennartz AMSU-B algorithms.
2156    call bennartz (tb89, tb150, tb89FG, tb150FG, tb89FgClear, tb150FgClear, &
2157                   scatIndexOverLandObs, &
2158                   headerIndex, obsSpaceData)
2159
2160    ! 10) test 10: RTTOV reject check (single)
2161    ! Rejected datum flag has bit #9 on.
2162    call amsuABTest10RttovRejectCheck (sensorIndex, RESETQC, qcIndicator, headerIndex, obsSpaceData)
2163
2164    ! 1) test 1: Topography check (partial)
2165    ! Channel 3- 45 is rejected for topography >  2500m.
2166    ! Channel 4 - 46 is rejected for topography > 2000m.
2167    ! Channel 5 - 47 is rejected for topography > 1000m.
2168    call amsuABTest1TopographyCheck (sensorIndex, modelInterpTerrain, channelForTopoFilter, altitudeForTopoFilter, &
2169                                     qcIndicator, headerIndex, obsSpaceData)
2170 
2171    ! 2) test 2: "Land/sea qualifier" code check (full)
2172    ! allowed values are: 0, land,
2173    !                     1, sea,
2174    !                     2, coast.
2175    call amsuABTest2LandSeaQualifierCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
2176
2177    ! 3) test 3: "Terrain type" code check (full)
2178    ! allowed values are: -1 missing, 0 sea-ice, 1 snow on land.
2179    call amsuABTest3TerrainTypeCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
2180 
2181    ! 4) test 4: Field of view number check (full)
2182    ! Field of view acceptable range is [1,maxScanAngleAMSU]  for AMSU footprints.
2183    call amsuABTest4FieldOfViewCheck (sensorIndex, maxScanAngleAMSU, qcIndicator, &
2184                                      headerIndex, obsSpaceData)
2185
2186    ! 5) test 5: Satellite zenith angle check (full)
2187    ! Satellite zenith angle acceptable range is [0.,60.].
2188    call amsuABTest5ZenithAngleCheck (sensorIndex, qcIndicator, headerIndex, obsSpaceData)
2189
2190    ! 6) test 6: "Sat. zenith angle"/"field of view" consistency check.  (full)
2191    ! Acceptable difference between "Satellite zenith angle"  and
2192    ! "approximate angle computed from field of view number" is 1.8 degrees.
2193    call amsuABTest6ZenAngleAndFovConsistencyCheck (sensorIndex, ZANGL, maxScanAngleAMSU, qcIndicator, &
2194                                                    headerIndex, obsSpaceData) 
2195
2196    ! 7) test 7: "Land/sea qual."/"model land/sea" consistency check.    (full)
2197    ! Acceptable conditions are:
2198    !       a) both over ocean (landQualifierIndice=1; mg<0.01), new threshold 0.20, jh dec 2000,
2199    !       b) both over land  (landQualifierIndice=0; mg>0.80), new threshold 0.50, jh dec 2000.
2200    ! Other conditions are unacceptable.
2201    call amsuABTest7landSeaQualifyerAndModelLandSeaConsistencyCheck (sensorIndex, modelInterpLandFrac, &
2202                                                                     qcIndicator, headerIndex, obsSpaceData)
2203
2204    ! 8) test 8: "Terrain type"/"Land/sea qual."/"model ice" consistency check. (full)
2205    ! Unacceptable conditions are:
2206    !        a) terrain is sea-ice and model has no ice(terrainTypeIndice=0; gl<0.01).
2207    !        b) terrain is sea-ice and land/sea qualifier is land (terrainTypeIndice=0; landQualifierIndice=0).
2208    !        c) terrain is snow on land and land/sea qualifier is sea (terrainTypeIndice=1; landQualifierIndice=1).
2209    !        d) terrain is missing, land/sea qualifier is sea and model has ice(terrainTypeIndice=-1; landQualifierIndice=1; gl>0.01). (enleve jh, jan 2001)
2210    ! NOT doNE ANYMORE 
2211    
2212    ! 9) test 9: Uncorrected Tb check (single) SKIP FOR NOW
2213    ! Uncorrected datum (flag bit #6 off). In this case switch bit 11 ON.
2214    ! call amsuABTest9UncorrectedTbCheck (sensorIndex, RESETQC, qcIndicator, &
2215    !                                     headerIndex, obsSpaceData) 
2216
2217    ! 11) test 11: Radiance observation "Gross" check (single) 
2218    !  Change this test from full to single. jh nov 2000.
2219    call amsuABTest11RadianceGrossValueCheck (sensorIndex, GROSSMIN, GROSSMAX, qcIndicator, &
2220                                              headerIndex, obsSpaceData)
2221
2222    ! 12) test 12:  Dryness index check 
2223    !The difference between channels AMSUB-3 and AMSUB-5 is used as an indicator
2224    !of "dryness" of the atmosphere. In extreme dry conditions, channels AMSUB-3 4 and 5
2225    ! are sensitive to the surface.
2226    ! Therefore, various thresholds are used to reject channels AMSUB-3 4 and 5
2227    !  over land and ice
2228    call amsubTest12DrynessIndexCheck (sensorIndex, tb1831, tb1833, modelInterpSeaIce, qcIndicator, &
2229                                       headerIndex, obsSpaceData, skipTestArr_opt=skipTestArr(:))
2230
2231    ! 13) test 13: Bennartz scattering index check (full)
2232    call amsubTest13BennartzScatteringIndexCheck(sensorIndex, scatIndexOverLandObs, modelInterpSeaIce, &
2233                                                 qcIndicator, chanIgnoreInAllskyGenCoeff, &
2234                                                 headerIndex, obsSpaceData, skipTestArr_opt=skipTestArr(:))
2235
2236    ! 14) test 14: "Rogue check" for (O-P) Tb residuals out of range. (single/full)
2237    ! Les observations, dont le residu (O-P) depasse par un facteur (roguefac) l'erreur totale des TOVS.
2238    ! N.B.: a reject by any of the 3 surface channels produces the rejection of AMSUA-A channels 1-5 and 15. 
2239    call amsubTest14RogueCheck(sensorIndex, ROGUEFAC, ICH2OMPREJ, qcIndicator, &
2240                               headerIndex, obsSpaceData, skipTestArr_opt=skipTestArr(:))
2241
2242    ! 15) test 15: Channel Selection using array oer_tovutil(chan,sat)
2243    !  oer_tovutil = 0 (blacklisted)
2244    !                1 (assmilate)
2245    !                2 (assimilate over open water only)
2246    !
2247    !  We also set QC flag bits 7 and 9 ON for channels with oer_tovutil=2 
2248    !  over land or sea-ice
2249    !    and 
2250    !  we set QC flag bits 7 and 9 ON for channels 1-3,15 over land
2251    !  or sea-ice REGARDLESS of oer_tovutil value (but oer_tovutil=0 always for
2252    !  these unassimilated channels).
2253    call amsuABTest15ChannelSelectionWithTovutil (sensorIndex, modelInterpSeaIce, ISFCREJ2, qcIndicator, &
2254                                                  headerIndex, obsSpaceData)
2255
2256    !  Synthese de la controle de qualite au niveau de chaque point
2257    !  d'observation. Code:
2258    !            =0, aucun rejet,
2259    !            >0, au moins un canal rejete.
2260
2261    KCHKPRF = 0
2262    do JI = 1, actualNumChannel
2263      KCHKPRF = MAX(KCHKPRF,qcIndicator(JI))
2264    end do
2265
2266    if ( mwbg_debug ) write(*,*)'KCHKPRF = ', KCHKPRF
2267
2268    ! reset global marker flag (55200) and mark it if observtions are rejected
2269    call resetQcCases(RESETQC, KCHKPRF, headerIndex, obsSpaceData)
2270
2271    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
2272
2273    !###############################################################################
2274    ! FINAL STEP: set terrain type to sea ice given certain conditions
2275    !###############################################################################
2276    call setTerrainTypeToSeaIce(modelInterpSeaIce, headerIndex, obsSpaceData)
2277
2278  end subroutine mwbg_tovCheckAmsub
2279
2280  !--------------------------------------------------------------------------
2281  ! mwbg_qcStats
2282  !--------------------------------------------------------------------------
2283  subroutine mwbg_qcStats(instName, qcIndicator, sensorIndex, satelliteId, LDprint)
2284    !
2285    !:Purpose: Cumuler ou imprimer des statistiques decriptives des rejets tovs.
2286    !
2287    implicit none 
2288
2289    ! Arguments:
2290    character(*),      intent(in) :: instName       ! Instrument Name
2291    integer,           intent(in) :: qcIndicator(:) ! indicateur controle de qualite tovs par canal (=0 ok, >0 rejet)
2292    integer,           intent(in) :: sensorIndex    ! numero d'identificateur du satellite
2293    character(len=15), intent(in) :: satelliteId(:) ! identificateur du satellite
2294    logical,           intent(in) :: LDprint        ! mode: imprimer ou cumuler?
2295
2296    ! Locals:
2297    integer :: numSats, JI, JJ, JK, INTOTOBS, INTOTACC, actualNumChannel, channelIndex
2298    integer, allocatable, save :: INTOT(:)    ! INTOT(tvs_nsensors)
2299    integer, allocatable, save :: INTOTRJF(:) ! INTOTRJF(tvs_nsensors)
2300    integer, allocatable, save :: INTOTRJP(:) ! INTOTRJP(tvs_nsensors)
2301    integer, allocatable :: obsChannels(:)
2302    logical :: FULLREJCT, FULLACCPT
2303    logical, save :: LLFIRST = .true.
2304
2305    ! Initialize
2306    if ( LLFIRST ) then
2307      allocate(INTOT(tvs_nsensors))
2308      allocate(INTOTRJF(tvs_nsensors))
2309      allocate(INTOTRJP(tvs_nsensors))
2310      INTOTRJF(:) = 0
2311      INTOTRJP(:) = 0
2312      INTOT(:)  = 0
2313      LLFIRST = .false.
2314    end if
2315    
2316    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
2317    allocate(obsChannels(actualNumChannel))
2318    do channelIndex = 1, actualNumChannel
2319      obsChannels(channelIndex) = channelIndex + tvs_channelOffset(sensorIndex)
2320    end do
2321
2322    if (.not. LDprint ) then
2323      ! Accumulate statistics on rejects
2324      INTOT(sensorIndex) = INTOT(sensorIndex) + 1
2325      ! Fully accepted, fully rejected or partially rejected?
2326      FULLREJCT = .true.
2327      FULLACCPT = .true.
2328      if (instName == "AMSUA") then 
2329        do JI = 1, actualNumChannel
2330          if ( obsChannels(JI) /= 20 ) then
2331            if ( qcIndicator(JI) /= 0 ) then
2332              FULLACCPT = .false.
2333            else
2334              FULLREJCT = .false.
2335            end if
2336          end if
2337        end do
2338        if ( FULLREJCT ) then
2339          INTOTRJF(sensorIndex) = INTOTRJF(sensorIndex) + 1
2340        end if
2341        if ( .not. FULLREJCT .and. .not.FULLACCPT ) then
2342          INTOTRJP(sensorIndex) = INTOTRJP(sensorIndex) + 1
2343        end if
2344      else if  (instName == "ATMS") then 
2345        do JI = 1, actualNumChannel
2346          if ( qcIndicator(JI) /= 0 ) then
2347            FULLACCPT = .false.
2348          else
2349            FULLREJCT = .false.
2350          end if
2351        end do
2352        if ( FULLREJCT ) then
2353          INTOTRJF(sensorIndex) = INTOTRJF(sensorIndex) + 1
2354        end if
2355        if ( .not. FULLREJCT .and. .not.FULLACCPT ) then
2356          INTOTRJP(sensorIndex) = INTOTRJP(sensorIndex) + 1
2357        end if
2358      end if
2359      
2360    else
2361
2362      numSats = size(satelliteId)
2363      ! Print statistics
2364      do JK = 1, numSats
2365
2366        INTOTOBS = INTOT(JK)
2367        INTOTACC = INTOTOBS - INTOTRJF(JK) - INTOTRJP(JK)
2368          write(*,'(/////50("*"))')
2369          write(*,'(     50("*")/)')
2370          write(*,'(T5,"SUMMARY OF QUALITY CONTROL FOR ", &
2371           A8)') satelliteId(JK) 
2372          write(*,'(T5,"------------------------------------- ",/)')
2373          write(*,'( &
2374           " - TOTAL NUMBER OF OBS.    = ",I10,/ &
2375           " - TOTAL FULL REJECTS      = ",I10,/ &
2376           " - TOTAL PARTIAL REJECTS   = ",I10,/ &
2377           "   ------------------------------------",/ &
2378           "   TOTAL FULLY ACCEPTED    = ",I10,/)') &
2379            INTOTOBS, INTOTRJF(JK), INTOTRJP(JK), INTOTACC
2380
2381        if (instName == "AMSUA" .or. instName == "AMSUB") then         
2382          write(*,'(//,1x,114("-"))')
2383           write(*,'(t10,"|",t47,"REJECTION CATEGORIES")')
2384          write(*,'(" CHANNEL",t10,"|",105("-"))')
2385          write(*,'(t10,"|",16i7)') (JI,JI=1,mwbg_maxNumTest)
2386          write(*,'(1x,"--------|",105("-"))')
2387          do JJ = 1, actualNumChannel 
2388             write(*,'(3X,I2,t10,"|",16I7)') JJ,(rejectionCodArray(JI,JJ,JK), &
2389                                      JI=1,mwbg_maxNumTest)
2390          end do
2391          write(*,'(1x,114("-"))')
2392          print *, ' '
2393          print *, ' '
2394          print *, ' -----------------------------------------------------'
2395          print *, ' Definition of rejection categories:'
2396          print *, ' -----------------------------------------------------'
2397          print *, '  1 - topography reject'
2398          print *, '  2 - invalid land/sea qualifier'
2399          print *, '  3 - invalid terrain type'
2400          print *, '  4 - invalid field of view number'
2401          print *, '  5 - satellite zenith angle out of range '
2402          print *, '  6 - inconsistent field of view and sat. zenith angle'
2403          print *, '  7 - inconsistent land/sea qualifier and model mask'
2404          print *, '  8 - inconsistent terrain type and land/sea', &
2405                   ' qualifier/model ice (NOT doNE)'
2406          print *, '  9 - uncorrected radiance'
2407          print *, ' 10 - rejected by RTTOV'
2408          print *, ' 11 - radiance gross check failure'
2409          print *, ' 12 - cloud liquid water reject'
2410          print *, ' 13 - scattering index reject'
2411          print *, ' 14 - radiance residual rogue check failure'
2412          print *, ' 15 - rejection by channel selection'
2413          print *, ' -----------------------------------------------------'
2414          print *, ' ' 
2415
2416        else if (instName == "ATMS" .or. instName == "MWHS2") then
2417          write(*,'(//,1x,59("-"))')
2418          write(*,'(t10,"|",t19,"1. REJECTION CATEGORIES")')
2419          write(*,'(" CHANNEL",t10,"|",50("-"))')
2420          write(*,'(t10,"|",5i7)') (JI,JI=1,mwbg_maxNumTest)
2421          write(*,'(1x,"--------|",50("-"))')
2422          do JJ = 1, actualNumChannel 
2423            write(*,'(3X,I2,t10,"|",5I7)') JJ,(rejectionCodArray(JI,JJ,JK), &
2424                                        JI=1,mwbg_maxNumTest)
2425          end do
2426          write(*,'(1x,59("-"))')
2427          write(*,'(//,1x,59("-"))')
2428          write(*,'(t10,"|",t19,"2. QC2 REJECT CATEGORIES")')
2429          write(*,'(" CHANNEL",t10,"|",50("-"))') 
2430          write(*,'(t10,"|",5i7)') (JI,JI=1,mwbg_maxNumTest)
2431          write(*,'(1x,"--------|",50("-"))')
2432          do JJ = 1, actualNumChannel
2433            write(*,'(3X,I2,t10,"|",5I7)') JJ,(rejectionCodArray2(JI,JJ,JK), &
2434                                        JI=1,mwbg_maxNumTest)          
2435          end do
2436          print *, ' '
2437          print *, ' '
2438          print *, ' -----------------------------------------------------'
2439          print *, ' Definition of rejection categories: '
2440          print *, ' -----------------------------------------------------'
2441          print *, '  1 - first bgckAtms/bgckMwhs2 program reject [bit 7]'
2442          print *, '  2 - topography reject'
2443          print *, '  3 - uncorrected radiance'
2444          print *, '  4 - innovation (O-P) based reject'
2445          print *, '  5 - rejection by channel selection'
2446          print *, ' -----------------------------------------------------'
2447          print *, ' '
2448          print *, ' QC2 REJECT numbers in Table 2 are for data that '
2449          print *, ' passed test 1 (data with QC flag bit 7 OFF)'
2450          print *, ' '
2451        end if 
2452      end do
2453    end if
2454
2455  end subroutine mwbg_qcStats
2456
2457  !--------------------------------------------------------------------------
2458  ! resetQcC
2459  !--------------------------------------------------------------------------
2460  subroutine resetQcCases(RESETQC, KCHKPRF, headerIndex, obsSpaceData)
2461    !
2462    !:Purpose: allumer la bit (6) indiquant que l'observation a un element
2463    !          rejete par le controle de qualite de l'AO.
2464    !
2465    !          N.B.: si on est en mode resetqc, on remet le marqueur global a
2466    !          sa valeur de defaut, soit 1024,  avant de faire la mise a jour.
2467    !
2468    implicit none
2469
2470    ! Arguments:
2471    logical,          intent(in)    :: RESETQC      ! reset the quality control flags before adding the new ones ?
2472    integer,          intent(in)    :: KCHKPRF      ! indicateur global controle de qualite tovs. Code:
2473    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
2474    integer,          intent(in)    :: headerIndex  ! current header index
2475
2476    ! Locals:
2477    integer :: obsGlobalMarker
2478
2479    obsGlobalMarker = obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex)
2480
2481    if (RESETQC) obsGlobalMarker = 1024  
2482    if (KCHKPRF /= 0) obsGlobalMarker = OR (obsGlobalMarker,2**6)
2483    if (mwbg_debug) write(*,*) ' KCHKPRF   = ', KCHKPRF, ', NEW FLAGS = ', obsGlobalMarker
2484
2485    call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex, obsGlobalMarker)
2486
2487  end subroutine resetQcCases
2488
2489  !--------------------------------------------------------------------------
2490  ! setTerrainTypeToSeaIce
2491  !--------------------------------------------------------------------------
2492  subroutine setTerrainTypeToSeaIce(modelInterpSeaIce, headerIndex, obsSpaceData)
2493    !
2494    !:Purpose: Dans les conditions suivantes:
2495    !            1) l'indicateur terre/mer indique l'ocean (landQualifierIndice=1),
2496    !            2) le "terrain type" est manquant (terrainTypeIndice=-1),
2497    !            3) le modele indique de la glace (gl >= 0.01),
2498    !          on specifie "sea ice" pour le "terrain type" (terrainTypeIndice=0).
2499    !
2500    implicit none 
2501    
2502    ! Arguments:
2503    real(8),          intent(in)    :: modelInterpSeaIce ! sea ice
2504    type(struct_obs), intent(inout) :: obsSpaceData      ! obspaceData Object
2505    integer,          intent(in)    :: headerIndex       ! current header Index 
2506
2507    ! Locals: 
2508    integer :: landQualifierIndice, terrainTypeIndice
2509
2510    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
2511    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
2512
2513    ! If terrain type is missing, set it to -1 for the QC programs
2514    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
2515
2516    if ( mwbg_debug ) then
2517      write(*,*) ' OLD TERRAIN type = ', terrainTypeIndice, &
2518                 ', landQualifierIndice = ', landQualifierIndice, &
2519                 ', modelInterpSeaIce = ', modelInterpSeaIce
2520    end if
2521
2522    if (landQualifierIndice == 1 .and. terrainTypeIndice == mwbg_intMissing .and. &
2523        modelInterpSeaIce >= 0.01d0) then
2524      terrainTypeIndice = 0
2525      call obs_headSet_i(obsSpaceData, OBS_TTYP, headerIndex, terrainTypeIndice)
2526    end if
2527
2528    if ( mwbg_debug ) write(*,*) 'NEW TERRAIN type = ', terrainTypeIndice
2529    
2530  end subroutine setTerrainTypeToSeaIce
2531
2532  !--------------------------------------------------------------------------
2533  ! GRODY
2534  !--------------------------------------------------------------------------
2535  subroutine GRODY (tb23, tb31, tb50, tb53, tb89, tb23FG, tb31FG, &
2536                    ice, tpw, &
2537                    rain, snow, scatIndexOverLandObs, &
2538                    headerIndex, obsSpaceData)
2539    !
2540    !:Purpose: Compute the following parameters using 5 AMSU-A channels:
2541    !            - sea ice, 
2542    !            - total precipitable water, 
2543    !            - cloud liquid water, 
2544    !            - ocean/land rain, 
2545    !            - <snow> cover/glacial ice,   
2546    !            - scattering index (sur la terre et sur l'eau).  
2547    !          The four channels used are: 23Ghz, 31Ghz, 50Ghz and 89Ghz.  
2548    !          REGERENCES N. Grody, NOAA/NESDIS, ....  
2549    !  
2550
2551    implicit none
2552
2553    ! Arguments:
2554    real(8),          intent(in)    :: tb23                 ! 23Ghz brightness temperature (K)
2555    real(8),          intent(in)    :: tb31                 ! 31Ghz brightness temperature (K)
2556    real(8),          intent(in)    :: tb50                 ! 50Ghz brightness temperature (K)
2557    real(8),          intent(in)    :: tb53                 ! 53Ghz brightness temperature (K)
2558    real(8),          intent(in)    :: tb89                 ! 89Ghz brightness temperature (K)
2559    real(8),          intent(in)    :: tb23FG               ! 23Ghz brightness temperature from background (K)
2560    real(8),          intent(in)    :: tb31FG               ! 31Ghz brightness temperature from background (K)
2561    real(8),          intent(out)   :: ice                  ! sea ice concentration (0-100%)
2562    real(8),          intent(out)   :: tpw                  ! total precipitable water (0-70mm)
2563    integer,          intent(out)   :: rain                 ! rain identification (0=no rain; 1=rain)
2564    integer,          intent(out)   :: snow                 ! snow cover and glacial ice id: 0=no snow; 1=snow; 2=glacial ice
2565    real(8),          intent(out)   :: scatIndexOverLandObs ! scattering index over land
2566    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
2567    integer,          intent(in)    :: headerIndex  ! current header Index 
2568
2569    ! Locals:
2570    real(8) :: df1, df2, df3, a, b, c, d, e23
2571    real(8) :: ei, cosz, tt, scat, sc31, t23, t31, t50, t89
2572    real(8) :: sc50, par, t53
2573    real(8) :: dif285t23, dif285t31, epsilon
2574    real(8) :: dif285t23FG, dif285t31FG
2575    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
2576    real(8) :: scatIndexOverWaterObs, landQualifierIndice
2577    real(8) :: obsLat, obsLon, satZenithAngle
2578    integer :: codtyp, ier
2579
2580    data epsilon / 1.E-30 /
2581
2582    logical chan15Missing 
2583    !
2584    ! Notes: In the case where an output parameter cannot be calculated, the
2585    !        value of this parameter is to mwbg_realMissing
2586
2587    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
2588    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
2589    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
2590    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
2591
2592    ! Convert lat/lon to degrees
2593    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
2594    if (obsLon > 180.0d0) obsLon = obsLon - 360.0d0
2595    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
2596
2597    ! 1) Initialise output parameters:
2598    ice = mwbg_realMissing
2599    tpw = mwbg_realMissing
2600    cloudLiquidWaterPathObs = mwbg_realMissing
2601    cloudLiquidWaterPathFG = mwbg_realMissing
2602    scatIndexOverLandObs = mwbg_realMissing
2603    scatIndexOverWaterObs = mwbg_realMissing
2604    rain = nint(mwbg_realMissing)
2605    snow = nint(mwbg_realMissing)
2606
2607    ! 2) Validate input parameters:
2608    if ( tb23 < 120.0d0 .or. tb23 > 350.0d0 .or. &
2609         tb31 < 120.0d0 .or. tb31 > 350.0d0 .or. &
2610         tb50 < 120.0d0 .or. tb50 > 350.0d0 .or. &
2611         tb53 < 120.0d0 .or. tb53 > 350.0d0 .or. &
2612         satZenithAngle < -90.0d0 .or. satZenithAngle > 90.0d0 .or. &
2613         obsLat < -90.0d0 .or. obsLat > 90.0d0 .or. &
2614         landQualifierIndice < 0 .or. landQualifierIndice > 1 ) then
2615      ier = 1
2616    else
2617      ier = 0
2618    end if
2619
2620    !3) Compute parameters:
2621    if ( ier == 0 ) then
2622      cosz   = cosd(satZenithAngle)
2623      t23 = tb23
2624      t31 = tb31
2625      t50 = tb50
2626      t53 = tb53
2627      dif285t23  =max(285.0d0-t23,epsilon)
2628      dif285t23FG=max(285.0d0-tb23FG,epsilon)
2629      dif285t31  =max(285.0d0-t31,epsilon)
2630      dif285t31FG=max(285.0d0-tb31FG,epsilon)
2631
2632      chan15Missing = .false.
2633      if (tb89 < 120.0d0 .or. tb89 > 350.0d0) then 
2634        chan15Missing = .true.
2635      end if
2636
2637      if (.not. chan15Missing) then
2638        t89 = tb89
2639        ! scattering indices:
2640        scatIndexOverWaterObs = -113.2d0 + (2.41d0 - 0.0049d0 * t23) * t23 + 0.454d0 * t31 - t89 
2641        scatIndexOverLandObs = t23 - t89
2642      end if
2643
2644      ! discriminate functions:
2645      df1 =  2.85d0 + 0.020d0 * t23 - 0.028d0 * t50 ! used to identify (also remove) sea ice
2646      df2 =  5.10d0 + 0.078d0 * t23 - 0.096d0 * t50 ! used to identify (also remove) warm deserts
2647      df3 = 10.20d0 + 0.036d0 * t23 - 0.074d0 * t50 ! used to identify (also remove) cold deserts
2648
2649      if (landQualifierIndice == 1) then
2650
2651        ! Ocean Parameters
2652
2653        !3.1) Sea ice:
2654        if (abs(obsLat) < 50.0d0) then
2655          ice = 0.0
2656        else
2657          if ( df1 < 0.45 ) then
2658            ice = 0.0
2659          else
2660            a =  1.7340d0 - 0.6236d0 * cosz
2661            b =  0.0070d0 + 0.0025d0 * cosz
2662            c = -0.00106d0 
2663            d = -0.00909d0
2664            e23 = a + b * t31 + c * t23 + d * t50 ! theoretical 23Ghz sfc emissivity (0.3-1.)
2665            if ( (t23-t31) >= 5. ) then   ! fov contains multiyear or new ice/water
2666              ei = 0.88d0
2667            else
2668              ei = 0.95d0
2669            end if
2670            ice = 100 * (e23 - 0.45d0) / (ei - 0.45d0) ! sea-ice concentration within fov (0-100%) 
2671            ice = min(100.0d0,max(0.0d0,ice)) / 100.0d0   !jh (0.-1.)
2672          end if
2673        end if
2674
2675        ! 3.2) Total precipitable water:
2676        ! identify and remove sea ice
2677        if (abs(obsLat) > 50.0d0 .and. df1 > 0.2d0) then  
2678          tpw = mwbg_realMissing
2679        else
2680          a =  247.92d0 - (69.235d0 - 44.177d0 * cosz) * cosz
2681          b = -116.27d0
2682          c = 73.409d0
2683          tpw = a + b * log(dif285t23) + c * log(dif285t31)
2684          tpw = tpw * cosz           ! theoretical total precipitable water (0-70mm)
2685          tpw = 0.942d0 * tpw - 2.17d0   ! corrected   total precipitable water 
2686          tpw = min(70.0d0,max(0.0d0,tpw))   ! jh     
2687        end if
2688
2689        !3.3) Cloud liquid water from obs (cloudLiquidWaterPathObs) and background state (cloudLiquidWaterPathFG):
2690        ! identify and remove sea ice
2691        if (abs(obsLat) > 50.0d0 .and. df1 > 0.0d0) then  
2692          cloudLiquidWaterPathObs = mwbg_realMissing
2693          cloudLiquidWaterPathFG = mwbg_realMissing
2694        else
2695          a =  8.240d0 - (2.622d0 - 1.846d0 * cosz) * cosz
2696          b =  0.754d0
2697          c = -2.265d0
2698          cloudLiquidWaterPathObs = a + b * log(dif285t23) + c * log(dif285t31)
2699          cloudLiquidWaterPathObs = cloudLiquidWaterPathObs * cosz         ! theoretical cloud liquid water (0-3mm)
2700          cloudLiquidWaterPathObs = cloudLiquidWaterPathObs - 0.03d0       ! corrected cloud liquid water 
2701          cloudLiquidWaterPathObs = min(3.0d0,max(0.0d0,cloudLiquidWaterPathObs))
2702
2703          cloudLiquidWaterPathFG = a + b * log(dif285t23FG) + c * log(dif285t31FG)
2704          cloudLiquidWaterPathFG = cloudLiquidWaterPathFG * cosz           ! theoretical cloud liquid water (0-3mm)
2705          cloudLiquidWaterPathFG = cloudLiquidWaterPathFG - 0.03d0         ! corrected cloud liquid water 
2706          cloudLiquidWaterPathFG = min(3.,max(0.,cloudLiquidWaterPathFG))
2707        end if
2708
2709        if (.not. chan15Missing) then
2710          !3.4) Ocean rain: 0=no rain; 1=rain.
2711          ! identify and remove sea ice
2712          if (abs(obsLat) > 50.0d0 .and. df1 > 0.0d0) then  
2713            rain = nint(mwbg_realMissing)
2714          else                                   ! remove non-precipitating clouds
2715            if (cloudLiquidWaterPathObs > 0.3d0 .or. scatIndexOverLandObs> 9.0d0) then 
2716              rain = 1
2717            else
2718              rain = 0
2719            end if
2720          end if
2721        end if
2722
2723      else
2724
2725        if (.not. chan15Missing) then
2726          ! Land Parameters
2727
2728          ! 3.5) Rain  over land: 0=no rain; 1=rain.
2729          tt = 168.0d0 + 0.49d0 * t89
2730          if (scatIndexOverLandObs >= 3.0d0) then
2731            rain = 1
2732          else 
2733            rain = 0
2734          end if
2735          
2736          ! remove snow cover
2737          if (t23 <= 261.0d0 .and. t23 < tt) rain = 0
2738
2739          ! remove warm deserts
2740          if (t89 > 273.0d0 .or. df2 < 0.6d0) rain = 0
2741
2742          ! 3.6) Snow cover and glacial ice: 0=no snow; 1=snow; 2=glacial ice.
2743          tt = 168.0d0 + 0.49d0 * t89
2744          scat = t23 - t89
2745          sc31 = t23 - t31
2746          sc50 = t31 - t50
2747          par  = t50 - t53
2748
2749          ! re-frozen snow
2750          if (t89 < 255.0d0 .and. scat < sc31) scat = sc31
2751
2752          ! identify glacial ice
2753          if (scat < 3.0d0 .and. t23 < 215.0d0) snow = 2
2754          if (scat >= 3.0d0) then
2755            snow = 1
2756          else
2757            snow = 0
2758          end if
2759
2760          ! remove precipitation
2761          if (t23 >= 262.0d0 .or. t23 >= tt) snow = 0
2762          ! remove deserts
2763          if (df3 <= 0.35d0) snow = 0
2764
2765          ! high elevation deserts
2766          if (scat < 15.0d0 .and. sc31 < 3.0d0 .and. par > 2.0d0) snow = 0
2767
2768          ! remove frozen ground
2769          if (scat < 9.0d0 .and. sc31 < 3.0d0 .and. sc50 < 0.0d0) snow = 0
2770        end if
2771
2772      end if
2773
2774    end if
2775
2776    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
2777    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
2778    if (tvs_isInstrumAllskyTtAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
2779      call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
2780    end if
2781
2782    if (scatIndexOverWaterObs /= mwbg_realMissing) then
2783      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, scatIndexOverWaterObs)
2784    else
2785      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, MPC_missingValue_R8)
2786    end if
2787
2788    if (mwbg_DEBUG) then
2789      write(*,*) 'GRODY: tb23, tb31, tb50, tb89, satZenithAngle, obsLat, landQualifierIndice = ', &
2790                tb23, tb31, tb50, tb89, satZenithAngle, obsLat, landQualifierIndice
2791      write(*,*) 'GRODY: ier, ice, tpw, cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, rain, snow=', &
2792                  ier, ice, tpw, cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, rain, snow
2793    end if
2794
2795  end subroutine GRODY
2796
2797  !------------------------------------------------------------------------------------
2798  ! bennartz
2799  !------------------------------------------------------------------------------------
2800  subroutine bennartz (tb89, tb150, tb89FG, tb150FG, tb89FgClear, tb150FgClear, &
2801                       scatIndexOverLandObs, &
2802                       headerIndex, obsSpaceData)
2803    !
2804    !:Purpose: Compute the following parameters using 2 AMSU-B channels:
2805    !            - scattering index (over land and ocean).
2806    !          The two channels used are: 89Ghz, 150Ghz.
2807    !
2808    !          REGERENCES: Bennartz, R., A. Thoss, A. Dybbroe and D. B. Michelson, 
2809    !          1999: Precipitation Analysis from AMSU, Nowcasting SAF, 
2810    !          Swedish Meteorologicali and Hydrological Institute, 
2811    !          Visiting Scientist Report, November 1999.
2812    !
2813    implicit none
2814
2815    ! Arguments:
2816    real(8),          intent(in)    :: tb89                 ! 89Ghz AMSU-B brightness temperature (K)
2817    real(8),          intent(in)    :: tb150                ! 150Ghz AMSU-B brightness temperature (K)
2818    real(8),          intent(in)    :: tb89FG               ! 89Ghz AMSU-B brightness temperature from background (K)
2819    real(8),          intent(in)    :: tb150FG              ! 150Ghz AMSU-B brightness temperature from background (K)
2820    real(8),          intent(in)    :: tb89FgClear          ! 89Ghz clear-sky brightness temperature from background (K)
2821    real(8),          intent(in)    :: tb150FgClear         ! 150Ghz clear-sky brightness temperature from background (K)
2822    real(8),          intent(out)   :: scatIndexOverLandObs ! scattering index over land
2823    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
2824    integer,          intent(in)    :: headerIndex  ! current header Index
2825
2826    ! Locals:
2827    integer :: landQualifierIndice, ier
2828    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
2829    real(8) :: scatIndexOverWaterObs, scatIndexOverWaterFG
2830    real(8) :: satZenithAngle
2831    integer :: codtyp
2832
2833    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
2834    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
2835
2836    ! Notes: In the case where an output parameter cannot be calculated, the
2837    !        value of this parameter is to mwbg_realMissing
2838
2839    ! 1) Initialise output parameters
2840    scatIndexOverLandObs = mwbg_realMissing
2841    scatIndexOverWaterObs = mwbg_realMissing
2842    scatIndexOverWaterFG = mwbg_realMissing
2843    cloudLiquidWaterPathObs = mwbg_realMissing
2844    cloudLiquidWaterPathFG = mwbg_realMissing
2845
2846    ! 2) Validate input parameters
2847    if (tb89  < 120.0d0 .or. tb89  > 350.0d0 .or. &
2848        tb150 < 120.0d0 .or. tb150 > 350.0d0 .or. & 
2849        satZenithAngle < -90.0d0 .or. satZenithAngle > 90.0d0 .or. & 
2850        landQualifierIndice < 0 .or. landQualifierIndice > 1) then
2851      ier = 1
2852    else
2853      ier = 0      
2854    end if 
2855
2856    ! 3) Compute parameters
2857    if (ier == 0) then
2858      if (landQualifierIndice == 1) then
2859          if (tvs_mwAllskyAssim) then
2860            scatIndexOverWaterObs = (tb89 - tb150) - (tb89FgClear - tb150FgClear)
2861            scatIndexOverWaterFG = (tb89FG - tb150FG) - (tb89FgClear - tb150FgClear)
2862          else
2863            scatIndexOverWaterObs = (tb89 - tb150) - (-39.2010d0 + 0.1104d0 * satZenithAngle)
2864          end if
2865        else
2866          scatIndexOverLandObs = (tb89 - tb150) - (0.158d0 + 0.0163d0 * satZenithAngle)
2867      end if ! if (landQualifierIndice == 1)
2868    else if (ier /= 0) then 
2869      write(*,*) 'bennartz: input Parameters are not all valid: '
2870      write(*,*) 'bennartz: tb89, tb150, satZenithAngle, landQualifierIndice = ', &
2871                  tb89, tb150, satZenithAngle, landQualifierIndice
2872      write(*,*) 'bennartz: ier, scatIndexOverLandObs, scatIndexOverWaterObs, scatIndexOverWaterFG=', &
2873                  ier, scatIndexOverLandObs, scatIndexOverWaterObs, scatIndexOverWaterFG
2874    end if ! if (ier == 0)
2875
2876    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
2877    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
2878    if (tvs_isInstrumAllskyTtAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
2879      call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
2880    end if
2881
2882    if (scatIndexOverWaterObs /= mwbg_realMissing) then
2883      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, scatIndexOverWaterObs)
2884    else
2885      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, MPC_missingValue_R8)
2886    end if
2887
2888    if (tvs_isInstrumAllskyHuAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
2889      if (scatIndexOverWaterFG /= mwbg_realMissing) then
2890        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, scatIndexOverWaterFG)
2891      else
2892        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, MPC_missingValue_R8)
2893      end if
2894    end if
2895
2896  end subroutine bennartz
2897
2898  !--------------------------------------------------------------------------
2899  ! atmsMwhs2Test1Flagbit7Check
2900  !--------------------------------------------------------------------------
2901  subroutine atmsMwhs2Test1Flagbit7Check (itest, sensorIndex, qcIndicator, &
2902                                          B7CHCK, headerIndex, obsSpaceData)
2903    !
2904    !:Purpose: test 1: Check flag bit 7 on from the first bgckAtms/bgckMwhs2 program
2905    !          Includes observations flagged for cloud liquid water, scattering index,
2906    !          dryness index plus failure of several QC checks.
2907    !
2908    implicit none
2909
2910    ! Arguments:
2911    integer,          intent(in)    :: itest(:)        ! test number
2912    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
2913    integer,          intent(inout) :: B7CHCK(:)       ! bit=7 of channel is on (=1) or off(=0)
2914    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
2915    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
2916    integer,          intent(in)    :: headerIndex  ! current header Index 
2917
2918    ! Locals:
2919    integer :: testIndex, IBIT, bodyIndex, bodyIndexBeg, bodyIndexEnd
2920    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
2921    character(len=9) :: stnId
2922
2923    testIndex = 1
2924    if ( itest(testIndex) /= 1 ) return
2925
2926    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
2927
2928    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
2929    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
2930
2931    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
2932      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
2933      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
2934      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
2935
2936      IBIT = AND(obsFlags, 2**7)
2937      if (IBIT /= 0) then
2938        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
2939        B7CHCK(obsChanNum) = 1
2940        obsFlags = OR(obsFlags,2**9)
2941        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
2942              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
2943
2944        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
2945
2946        if ( mwbg_debug ) then
2947          write(*,*)stnId(2:9),' first bgckAtms/bgckMwhs2 program REJECT.', &
2948                    'CHANNEL=', obsChanNumWithOffset, &
2949                    ' obsFlags= ',obsFlags
2950        end if
2951      end if
2952    end do BODY
2953
2954  end subroutine atmsMwhs2Test1Flagbit7Check
2955
2956  !--------------------------------------------------------------------------
2957  ! atmsMwhs2Test2TopographyCheck
2958  !--------------------------------------------------------------------------
2959  subroutine atmsMwhs2Test2TopographyCheck(itest, sensorIndex, &
2960                                           modelInterpTerrain, ICHTOPO, ZCRIT, B7CHCK, qcIndicator, &
2961                                           headerIndex, obsSpaceData)
2962    !
2963    !:Purpose: test 2: Topography check (partial)
2964    !
2965    implicit none
2966
2967    ! Arguments:
2968    integer,          intent(in)    :: itest(:)           ! test number
2969    integer,          intent(in)    :: sensorIndex        ! numero de satellite (i.e. indice) 
2970    real(8),          intent(in)    :: modelInterpTerrain ! topo aux point d'obs
2971    integer,          intent(in)    :: ICHTOPO(:)         ! rejection channel list
2972    real(8),          intent(in)    :: ZCRIT(:)           ! criteria for topo check
2973    integer,          intent(inout) :: qcIndicator(:)     ! indicateur du QC par canal
2974    integer,          intent(inout) :: B7CHCK(:)          ! bit=7 of channel is on (=1) or off(=0)
2975    type(struct_obs), intent(inout) :: obsSpaceData    ! obspaceData Object
2976    integer,          intent(in)    :: headerIndex     ! current header Index 
2977
2978    ! Locals:
2979    integer :: testIndex, INDXTOPO, bodyIndex, bodyIndexBeg, bodyIndexEnd 
2980    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
2981    character(len=9) :: stnId
2982
2983    testIndex = 2
2984
2985    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
2986
2987    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
2988    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
2989
2990    if ( itest(testIndex) /= 1 ) return
2991
2992    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
2993      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
2994      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
2995      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
2996          
2997      INDXTOPO = utl_findloc(ICHTOPO(:),obsChanNumWithOffset)
2998      if ( INDXTOPO > 0 ) then
2999        if (modelInterpTerrain >= ZCRIT(INDXTOPO)) then
3000          qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3001          obsFlags = OR(obsFlags,2**9)
3002          obsFlags = OR(obsFlags,2**18)
3003          rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3004                rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3005          if ( B7CHCK(obsChanNum) == 0 ) then
3006            rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3007                rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1                 
3008          end if
3009
3010          call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3011
3012          if ( mwbg_debug ) then
3013            write(*,*) stnId(2:9),' TOPOGRAPHY REJECT.', &
3014                       'CHANNEL=', obsChanNumWithOffset, &
3015                       ' TOPO= ',modelInterpTerrain
3016          end if
3017        end if
3018      end if
3019    end do BODY
3020
3021  end subroutine atmsMwhs2Test2TopographyCheck
3022
3023  !--------------------------------------------------------------------------
3024  ! atmsMwhs2Test3UncorrectedTbCheck
3025  !--------------------------------------------------------------------------
3026  subroutine atmsMwhs2Test3UncorrectedTbCheck(itest, sensorIndex, RESETQC, B7CHCK, qcIndicator, &
3027                                              headerIndex, obsSpaceData)
3028    !
3029    !:Purpose: Test 3: Uncorrected Tb check (single)
3030    !          Uncorrected datum (flag bit #6 off). 
3031    !          In this case switch bit 11 ON.
3032    !
3033    implicit none
3034
3035    ! Arguments:
3036    integer,          intent(in)    :: itest(:)        ! test number
3037    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
3038    logical,          intent(in)    :: RESETQC         ! resetqc logical
3039    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
3040    integer,          intent(inout) :: B7CHCK(:)       ! bit=7 of channel is on (=1) or off(=0)
3041    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
3042    integer,          intent(in)    :: headerIndex  ! current header Index 
3043
3044    ! Locals:
3045    integer :: testIndex, IBIT, bodyIndex, bodyIndexBeg, bodyIndexEnd 
3046    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
3047    character(len=9) :: stnId
3048     
3049    if (RESETQC) return
3050    testIndex = 3
3051    if ( itest(testIndex) /= 1 ) return
3052
3053    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
3054
3055    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3056    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3057
3058    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3059      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3060      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3061      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3062
3063      IBIT = AND(obsFlags, 2**6)
3064      if (IBIT == 0) then
3065        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3066        obsFlags = OR(obsFlags,2**11)
3067        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3068            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3069        if ( B7CHCK(obsChanNum) == 0 ) then
3070          rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3071              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1                 
3072        end if
3073
3074        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3075
3076        if ( mwbg_debug ) then
3077          write(*,*) stnId(2:9),' UNCORRECTED TB REJECT.', &
3078                     'CHANNEL=', obsChanNumWithOffset, &
3079                     ' obsFlags= ',obsFlags
3080        end if
3081      end if
3082    end do BODY
3083
3084  end subroutine atmsMwhs2Test3UncorrectedTbCheck
3085
3086  !--------------------------------------------------------------------------
3087  ! atmsTest4RogueCheck
3088  !--------------------------------------------------------------------------
3089  subroutine atmsTest4RogueCheck(itest, sensorIndex, ROGUEFAC, waterobs, ISFCREJ, ICH2OMPREJ, &
3090                                 B7CHCK, qcIndicator, headerIndex, obsSpaceData)
3091    !
3092    !:Purpose: test 4: "Rogue check" for (O-P) Tb residuals out of range (single/full).
3093    !          Also, over WATER remove CH.17-22 if CH.17 |O-P|>5K (partial) 
3094    !          Les observations, dont le residu (O-P) 
3095    !          depasse par un facteur (roguefac) l'erreur totale des TOVS.
3096    !
3097    !          N.B.: a reject by any of the 3 amsua surface channels 1-3 produces the 
3098    !          rejection of ATMS sfc/tropospheric channels 1-6 and 16-17.
3099    !
3100    !          OVER OPEN WATER ch. 17 Abs(O-P) > 5K produces rejection of all ATMS amsub channels 17-22.
3101    !
3102    implicit none
3103
3104    ! Arguments:
3105    integer,          intent(in)    :: itest(:)        ! test number
3106    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice) 
3107    real(8),          intent(in)    :: ROGUEFAC(:)     ! rogue factor 
3108    logical,          intent(in)    :: waterobs        ! open water obs
3109    integer,          intent(in)    :: ISFCREJ(:)      ! rejection surface channel list
3110    integer,          intent(in)    :: ICH2OMPREJ(:)   ! rejection channel list
3111    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
3112    integer,          intent(inout) :: B7CHCK(:)       ! bit=7 of channel is on (=1) or off(=0)
3113    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
3114    integer,          intent(in)    :: headerIndex  ! current header Index
3115
3116    ! Locals:
3117    integer :: testIndex, INDXCAN, newInformationFlag, bodyIndex, bodyIndexBeg, bodyIndexEnd 
3118    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
3119    real(8) :: XCHECKVAL, clwThresh1, clwThresh2, errThresh1, errThresh2
3120    real(8) :: sigmaObsErrUsed, clwObsFGaveraged 
3121    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, ompTb
3122    logical :: SFCREJCT, CH2OMPREJCT, IBIT 
3123    character(len=9) :: stnId
3124
3125    testIndex = 4
3126    if ( itest(testIndex) /= 1 ) return
3127
3128    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3129    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3130
3131    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
3132    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
3133    newInformationFlag = obs_headElem_i(obsSpaceData, OBS_INFG, headerIndex)
3134    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex)
3135
3136    SFCREJCT = .FALSE.
3137    CH2OMPREJCT = .FALSE.
3138    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3139      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3140      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3141
3142      ! using state-dependent obs error only over water.
3143      ! obs over sea-ice will be rejected in test 15.
3144      if ( tvs_mwAllskyAssim .and. oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex) .and. waterobs ) then
3145        clwThresh1 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,1)
3146        clwThresh2 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,2)
3147        errThresh1 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,1)
3148        errThresh2 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,2)
3149        clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
3150        if (cloudLiquidWaterPathObs == mwbg_realMissing .or. &
3151            cloudLiquidWaterPathFG == mwbg_realMissing) then
3152          sigmaObsErrUsed = MPC_missingValue_R8
3153        else
3154          sigmaObsErrUsed = calcStateDepObsErr(clwThresh1,clwThresh2,errThresh1, &
3155                                                  errThresh2,clwObsFGaveraged)
3156        end if
3157      else
3158        sigmaObsErrUsed = oer_toverrst(obsChanNumWithOffset,sensorIndex)
3159      end if
3160      ! For sigmaObsErrUsed=MPC_missingValue_R8 (cloudLiquidWaterPathObs[FG]=mwbg_realMissing
3161      ! in all-sky mode), the observation is flagged for rejection in 
3162      ! mwbg_reviewAllCritforFinalFlagsAtms.
3163      XCHECKVAL = ROGUEFAC(obsChanNumWithOffset) * sigmaObsErrUsed
3164      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3165      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
3166
3167      if (ompTb /= mwbg_realMissing .and. ABS(ompTb) >= XCHECKVAL .and. &
3168          sigmaObsErrUsed /= MPC_missingValue_R8) then
3169        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3170        obsFlags = OR(obsFlags,2**9)
3171        obsFlags = OR(obsFlags,2**16)
3172        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) =  &
3173            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3174        if ( B7CHCK(obsChanNum) == 0 ) then
3175          rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3176              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1                 
3177        end if
3178
3179        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3180
3181        if ( mwbg_debug ) then
3182          write(*,*) stnId(2:9),'ROGUE CHECK REJECT.NO.', &
3183                     ' CHANNEL= ',obsChanNumWithOffset, &
3184                     ' CHECK VALUE= ',XCHECKVAL, &
3185                     ' TBOMP= ',ompTb, &
3186                     ' TOVERRST= ',oer_toverrst(obsChanNumWithOffset,sensorIndex)
3187        end if
3188
3189        if (obsChanNumWithOffset == 1 .or. obsChanNumWithOffset == 2 .or. &
3190            obsChanNumWithOffset == 3) then
3191          SFCREJCT = .TRUE.
3192        end if
3193      end if ! if (ompTb /= mwbg_realMissing
3194
3195      if (obsChanNumWithOffset == 17 .and. ompTb /= mwbg_realMissing .and. &
3196          ABS(ompTb) > 5.0d0) then
3197        CH2OMPREJCT = .TRUE.
3198      end if
3199    end do BODY
3200
3201    if ( SFCREJCT ) then
3202      BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3203        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3204        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3205        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3206
3207        INDXCAN = utl_findloc(ISFCREJ(:),obsChanNumWithOffset)
3208        if ( INDXCAN /= 0 ) then
3209          if ( qcIndicator(obsChanNum) /= testIndex ) then
3210            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3211            obsFlags = OR(obsFlags,2**9)
3212            obsFlags = OR(obsFlags,2**16)
3213            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3214                    rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3215            if ( B7CHCK(obsChanNum) == 0 ) then
3216              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3217                  rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1                 
3218            end if
3219
3220            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3221          end if ! if ( qcIndicator(obsChanNum)
3222        end if ! if ( INDXCAN /= 0 )
3223      end do BODY2
3224    end if ! SFCREJCT
3225
3226    !  amsub channels 17-22 obs are rejected if, for ch17 ABS(O-P) > 5K
3227    !    Apply over open water only (bit 0 ON in QC integer newInformationFlag).
3228    !    Only apply if obs not rejected in this test already.
3229    IBIT = AND(newInformationFlag, 2**0)
3230    if ( CH2OMPREJCT .and. (IBIT /= 0) ) then
3231      BODY3: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3232        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3233        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3234        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3235
3236        INDXCAN = utl_findloc(ICH2OMPREJ(:),obsChanNumWithOffset)
3237        if ( INDXCAN /= 0 )  then
3238          if ( qcIndicator(obsChanNum) /= testIndex ) then
3239            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3240            obsFlags = OR(obsFlags,2**9)
3241            obsFlags = OR(obsFlags,2**16)
3242
3243            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3244                    rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3245            if ( B7CHCK(obsChanNum) == 0 ) then
3246              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3247                  rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1                 
3248            end if
3249
3250            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3251          end if ! if ( qcIndicator(obsChanNum)
3252        end if ! if ( INDXCAN /= 0 )
3253      end do BODY3
3254    end if ! if ( CH2OMPREJCT
3255
3256  end subroutine atmsTest4RogueCheck
3257
3258  !--------------------------------------------------------------------------
3259  ! Mwhs2Test4RogueCheck
3260  !--------------------------------------------------------------------------
3261  subroutine Mwhs2Test4RogueCheck(itest, sensorIndex, ROGUEFAC, waterobs, ICH2OMPREJ, &
3262                                  B7CHCK, qcIndicator, headerIndex, obsSpaceData)
3263    !
3264    !:Purpose: test 4: "Rogue check" for (O-P) Tb residuals out of range (single/full).
3265    !          Also, over WATER remove CH.10-15 if CH.10 |O-P|>5K (full)
3266    !          Les observations, dont le residu (O-P)
3267    !          depasse par un facteur (roguefac) l'erreur totale des TOVS.
3268    !
3269    !          OVER OPEN WATER ch. 10 Abs(O-P) > 5K produces rejection of all ATMS amsub channels 10-15.
3270    !
3271    implicit none
3272
3273    ! Arguments:
3274    integer,          intent(in)    :: itest(:)        ! test number
3275    integer,          intent(in)    :: sensorIndex     ! numero de satellite (i.e. indice)
3276    real(8),          intent(in)    :: ROGUEFAC(:)     ! rogue factor
3277    logical,          intent(in)    :: waterobs        ! open water obs
3278    integer,          intent(in)    :: ICH2OMPREJ(:)   ! rejection channel list
3279    integer,          intent(inout) :: qcIndicator(:)  ! indicateur du QC par canal
3280    integer,          intent(inout) :: B7CHCK(:)       ! bit=7 of channel is on (=1) or off(=0)
3281    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
3282    integer,          intent(in)    :: headerIndex  ! current header Index 
3283
3284    ! Locals:
3285    integer :: testIndex, INDXCAN, newInformationFlag, bodyIndex, bodyIndexBeg, bodyIndexEnd 
3286    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
3287    real(8) :: XCHECKVAL, clwThresh1, clwThresh2, sigmaThresh1, sigmaThresh2
3288    real(8) :: sigmaObsErrUsed, clwObsFGaveraged
3289    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, ompTb
3290    logical :: CH2OMPREJCT, IBIT
3291    character(len=9) :: stnId
3292
3293    testIndex = 4
3294    if ( itest(testIndex) /= 1 ) return
3295
3296    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3297    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3298
3299    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
3300    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
3301    newInformationFlag = obs_headElem_i(obsSpaceData, OBS_INFG, headerIndex)
3302    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex)
3303
3304    CH2OMPREJCT = .FALSE.
3305    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3306      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3307      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3308
3309      ! using state-dependent obs error only over water.
3310      ! obs over sea-ice will be rejected in test 15.
3311      if ( tvs_mwAllskyAssim .and. oer_useStateDepSigmaObs(obsChanNumWithOffset,sensorIndex) .and. waterobs ) then
3312        clwThresh1 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,1)
3313        clwThresh2 = oer_cldPredThresh(obsChanNumWithOffset,sensorIndex,2)
3314        sigmaThresh1 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,1)
3315        sigmaThresh2 = oer_errThreshAllsky(obsChanNumWithOffset,sensorIndex,2)
3316        clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
3317        if ( cloudLiquidWaterPathObs == mwbg_realMissing ) then
3318          sigmaObsErrUsed = MPC_missingValue_R8
3319        else
3320          sigmaObsErrUsed = calcStateDepObsErr(clwThresh1,clwThresh2,sigmaThresh1, &
3321                                                  sigmaThresh2,clwObsFGaveraged)
3322        end if
3323      else
3324        sigmaObsErrUsed = oer_toverrst(obsChanNumWithOffset,sensorIndex)
3325      end if
3326      ! For sigmaObsErrUsed=MPC_missingValue_R8 (cloudLiquidWaterPathObs=mwbg_realMissing
3327      ! in all-sky mode), the observation is flagged for rejection in
3328      ! mwbg_reviewAllCritforFinalFlagsMwhs2.
3329      XCHECKVAL = ROGUEFAC(obsChanNumWithOffset) * sigmaObsErrUsed
3330      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3331      ompTb = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
3332      
3333      if (ompTb /= mwbg_realMissing .and. ABS(ompTb) >= XCHECKVAL .and. &
3334          sigmaObsErrUsed /= MPC_missingValue_R8) then
3335        qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3336        obsFlags = OR(obsFlags,2**9)
3337        obsFlags = OR(obsFlags,2**16)
3338
3339        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) =  &
3340            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3341        if ( B7CHCK(obsChanNum) == 0 ) then
3342          rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3343              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3344        end if
3345
3346        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3347
3348        if ( mwbg_debug ) then
3349          write(*,*) stnId(2:9),'ROGUE CHECK REJECT.NO.', &
3350                     ' CHANNEL= ',obsChanNumWithOffset, &
3351                     ' CHECK VALUE= ',XCHECKVAL, &
3352                     ' TBOMP= ',ompTb, &
3353                     ' TOVERRST= ',oer_toverrst(obsChanNumWithOffset,sensorIndex)
3354        end if
3355      end if ! if (ompTb /= mwbg_realMissing
3356
3357      if (obsChanNumWithOffset == 10 .and. ompTb /= mwbg_realMissing .and. ABS(ompTb) > 5.0d0) then
3358        CH2OMPREJCT = .TRUE.
3359      end if
3360    end do BODY
3361
3362    ! Channels 10-15 are rejected if, for ch10 ABS(O-P) > 5K
3363    ! Apply over open water only (bit 0 ON in QC integer newInformationFlag).
3364    ! Only apply if obs not rejected in this test already.
3365    IBIT = AND(newInformationFlag, 2**0)
3366    if ( CH2OMPREJCT .and. (IBIT /= 0) ) then
3367      BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3368        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3369        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3370        obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3371
3372        INDXCAN = utl_findloc(ICH2OMPREJ(:),obsChanNumWithOffset)
3373        if ( INDXCAN /= 0 )  then
3374          if ( qcIndicator(obsChanNum) /= testIndex ) then
3375            qcIndicator(obsChanNum) = MAX(qcIndicator(obsChanNum),testIndex)
3376            obsFlags = OR(obsFlags,2**9)
3377            obsFlags = OR(obsFlags,2**16)
3378            rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3379                    rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3380            if ( B7CHCK(obsChanNum) == 0 ) then
3381              rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) = &
3382                  rejectionCodArray2(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3383            end if
3384
3385            call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3386          end if
3387        end if ! if ( INDXCAN /= 0 )
3388      end do BODY2
3389    end if ! if ( CH2OMPREJCT
3390
3391  end subroutine Mwhs2Test4RogueCheck
3392
3393  !--------------------------------------------------------------------------
3394  ! atmsMwhs2Test5ChannelSelectionUsingTovutil
3395  !--------------------------------------------------------------------------
3396  subroutine atmsMwhs2Test5ChannelSelectionUsingTovutil(itest, sensorIndex, &
3397                                                        headerIndex, obsSpaceData)
3398    !
3399    !:Purpose: test 5: Channel selection using array oer_tovutil(chan,sat):
3400    !            - = 0 blacklisted, 
3401    !            - = 1 assmilate
3402    !
3403    implicit none
3404
3405    ! Arguments:
3406    integer,          intent(in)    :: itest(:)     ! test number
3407    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice) 
3408    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
3409    integer,          intent(in)    :: headerIndex  ! current header Index 
3410
3411    ! Locals:
3412    integer :: testIndex, bodyIndex, bodyIndexBeg, bodyIndexEnd 
3413    integer :: obsChanNum, obsChanNumWithOffset, obsFlags
3414    character(len=9) :: stnId
3415
3416    testIndex = 5
3417    if ( itest(testIndex) /= 1 ) return
3418
3419    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3420    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3421
3422    stnId = obs_elem_c(obsSpaceData, 'STID', headerIndex) 
3423
3424    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3425      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
3426      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
3427      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
3428
3429      if ( oer_tovutil(obsChanNumWithOffset,sensorIndex) == 0 ) then
3430        obsFlags = OR(obsFlags,2**8)
3431        rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) = &
3432              rejectionCodArray(testIndex,obsChanNumWithOffset,sensorIndex) + 1
3433
3434        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3435
3436        if ( mwbg_debug ) then
3437          write(*,*) stnId(2:9),'CHANNEL REJECT: ', &
3438                     ' CHANNEL= ',obsChanNumWithOffset                  
3439        end if
3440      end if ! if ( oer_tovutil
3441    end do BODY
3442
3443  end subroutine atmsMwhs2Test5ChannelSelectionUsingTovutil
3444
3445  !--------------------------------------------------------------------------
3446  ! mwbg_tovCheckAtms 
3447  !--------------------------------------------------------------------------
3448  subroutine mwbg_tovCheckAtms(qcIndicator, sensorIndex, modelInterpTerrain, &
3449                               RESETQC, headerIndex, obsSpaceData)
3450    !
3451    !:Purpose: Effectuer le controle de qualite des radiances tovs.
3452    !
3453    implicit none
3454
3455    ! Arguments:
3456    type(struct_obs),     intent(inout) :: obsSpaceData       ! obspaceData Object
3457    integer,              intent(in)    :: headerIndex        ! current header Index 
3458    integer,              intent(in)    :: sensorIndex        ! numero de satellite (i.e. indice)
3459    real(8),              intent(in)    :: modelInterpTerrain ! topographie du modele
3460    logical,              intent(in)    :: RESETQC            ! reset du controle de qualite?
3461    integer, allocatable, intent(out)   :: qcIndicator(:) ! indicateur controle de qualite tovs par canal (=0 ok, >0 rejet)
3462
3463    ! Locals:
3464    integer, parameter :: maxScanAngleAMSU = 96
3465    integer, parameter :: ilsmOpt = 1    ! OPTION for values of MG (land/sea mask) and LG (ice) 
3466                                         !  at each observation point using values on 5x5 mesh 
3467                                         !  centered at each point.
3468                                         !  ilsmOpt = 1 --> use MAX value from all 25 mesh points
3469                                         !  ilsmOpt = 2 --> use value at central mesh point (obs location)
3470                                         !  ilsmOpt = 3 --> use AVG value from all 25 mesh points
3471    integer :: calcLandQualifierIndice, calcTerrainTypeIndice, KCHKPRF
3472    integer :: iRej, iNumSeaIce, JI, actualNumChannel
3473    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsFlags
3474    integer :: ISFCREJ(8), ICH2OMPREJ(6)
3475    integer, allocatable :: B7CHCK(:)
3476    integer :: ITEST(mwbg_maxNumTest), chanIgnoreInAllskyGenCoeff(6), ICHTOPO(5)
3477    logical :: waterobs, grossrej, reportHasMissingTb
3478    logical :: cloudobs, iwvreject, precipobs
3479    real(8) :: zdi, scatec, scatbg, SeaIce, riwv, ZCRIT(5)
3480    real(8), allocatable :: ROGUEFAC(:)
3481    logical, allocatable :: qcRejectLogic(:)
3482    logical, save :: LLFIRST = .true.
3483    integer, save :: numReportWithMissingTb
3484    integer, save :: drycnt                 ! Number of pts flagged for AMSU-B Dryness Index
3485    integer, save :: landcnt                ! Number of obs pts found over land/ice
3486    integer, save :: rejcnt                 ! Number of problem obs pts (Tb err, QCfail)
3487    integer, save :: iwvcnt                 ! Number of pts with Mean 183 Ghz Tb < 240K
3488    integer, save :: pcpcnt                 ! Number of scatter/precip obs
3489    integer, save :: cldcnt                 ! Number of water point covered by cloud
3490    integer, save :: flgcnt                 ! Total number of filtered obs pts
3491    integer, save :: seaIcePointNum         ! Number of waterobs points converted to sea ice points
3492    integer, save :: clwMissingPointNum     ! Number of points where cloudLiquidWaterPath/SI missing
3493                                            !   over water due bad data
3494
3495    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3496    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3497
3498    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
3499    allocate(ROGUEFAC(actualNumChannel+tvs_channelOffset(sensorIndex)))
3500    ROGUEFAC(:) = (/2.0d0, 2.0d0, 2.0d0, 3.0d0, 3.0d0, 4.0d0, 4.0d0, 4.0d0, &
3501                    4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 2.0d0, &
3502                    2.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0/)
3503    if ( tvs_mwAllskyAssim ) ROGUEFAC(1:3) = 3.0
3504
3505    ! Channel sets for rejection in test 9 
3506    ! These LT channels are rejected if O-P fails rogue check for window ch. 1, 2, or 3
3507    ISFCREJ(:) = (/1, 2, 3, 4, 5, 6, 16, 17/)
3508    !   These AMSU-B channels are rejected if ch. 17 O-P fails rogue check over OPEN WATER only    
3509    ICH2OMPREJ(:) = (/17, 18, 19, 20, 21, 22/)
3510
3511    !  Data for TOPOGRAPHY CHECK
3512    !   Channel AMSUA-6 (atms ch 7) is rejected for topography  >  250m.
3513    !   Channel AMSUA-7 (atms ch 8) is rejected for topography  > 2000m.
3514    !   Channel AMSUB-3 (atms ch 22) is rejected for topography > 2500m.
3515    !                    atms ch 21  is rejected for topography > 2250m.
3516    !   Channel AMSUB-4 (atms ch 20) is rejected for topography > 2000m.
3517    ICHTOPO(:) = (/7, 8, 20, 21, 22/)
3518    ZCRIT(:) = (/250.0d0, 2000.0d0, 2000.0d0, 2250.0d0, 2500.0d0/)
3519
3520    !  Test selection (0=skip test, 1=do test)
3521    !              1  2  3  4  5
3522    ITEST(:) = 0
3523    ITEST(1:5) = (/1, 1, 1, 1, 1/)
3524
3525    ! Channels excluded from gen_bias_corr in all-sky mode
3526    chanIgnoreInAllskyGenCoeff(:) = (/ 1, 2, 3, 4, 5, 6/)
3527
3528    ! Initialisation, la premiere fois seulement!
3529    if (LLFIRST) then
3530      numReportWithMissingTb = 0
3531      flgcnt = 0
3532      landcnt = 0
3533      rejcnt = 0
3534      cldcnt = 0
3535      iwvcnt = 0
3536      pcpcnt = 0
3537      drycnt = 0
3538      seaIcePointNum = 0
3539      clwMissingPointNum = 0
3540      rejectionCodArray(:,:,:)  = 0
3541      rejectionCodArray2(:,:,:) = 0
3542      LLFIRST = .FALSE.
3543    end if
3544
3545    ! PART 1 TESTS:
3546
3547    !###############################################################################
3548    ! STEP 1 ) Determine which obs pts are over open water (i.e NOT near coasts or
3549    !          over/near land/ice) using model MG and LG fields from glbhyb2 ANAL
3550    !###############################################################################
3551    call atmsMwhs2landIceMask(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, ilsmOpt, &
3552                              headerIndex, obsSpaceData)
3553
3554    !###############################################################################
3555    ! STEP 2 ) Check for values of TB that are missing or outside physical limits.
3556    !###############################################################################
3557    call mwbg_grossValueCheck(50.0d0, 380.0d0, grossrej, headerIndex, sensorIndex, obsSpaceData)
3558
3559    !###############################################################################
3560    ! STEP 3 ) Preliminary QC checks --> set qcRejectLogic(actualNumChannel)=.true.
3561    !          for data that fail QC
3562    !###############################################################################
3563    call mwbg_firstQcCheckAtms(qcRejectLogic, grossrej, calcLandQualifierIndice, calcTerrainTypeIndice, &
3564                               reportHasMissingTb, headerIndex, sensorIndex, obsSpaceData)
3565
3566    if ( reportHasMissingTb ) numReportWithMissingTb = numReportWithMissingTb + 1
3567    !  Exclude problem points from further calculations
3568    if ( COUNT(qcRejectLogic(:)) == actualNumChannel ) grossrej = .true.
3569
3570    !###############################################################################
3571    ! STEP 4 ) mwbg_nrlFilterAtms returns cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, scatec, scatbg and also does sea-ice
3572    !          detection missing value for cloudLiquidWaterPathObs, scatec, scatbg is mwbg_realMissing (e.g. over
3573    !          land or sea-ice).Sets calcTerrainTypeIndice=0 (sea ice) for points where retrieved SeaIce
3574    !          >=0.55. Does nothing if calcTerrainTypeIndice=0 (sea ice) and retrieved SeaIce<0.55.
3575    !###############################################################################
3576    call mwbg_nrlFilterAtms(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, grossrej, &
3577                            scatec, scatbg, iNumSeaIce, iRej, SeaIce, &
3578                            headerIndex, sensorIndex, obsSpaceData)
3579
3580    seaIcePointNum = seaIcePointNum + iNumSeaIce
3581    clwMissingPointNum = clwMissingPointNum + iRej
3582
3583    !###############################################################################
3584    ! STEP 5 ) Apply NRL cloud filter, scattering index and sea-ice detection algorithms
3585    !          to OPEN WATER (waterobs=true) points.
3586    ! Points with SeaIce>0.55 are set to sea-ice points (waterobs --> false)
3587    !###############################################################################
3588    call mwbg_flagDataUsingNrlCritAtms(scatec, scatbg, SeaIce, grossrej, waterobs, mwbg_useUnbiasedObsForClw, &
3589                                       iwvreject, cloudobs, precipobs, cldcnt , riwv, zdi, &
3590                                       headerIndex, sensorIndex, obsSpaceData)
3591
3592    !###############################################################################
3593    ! STEP 6 ) ! Review all the checks previously made to determine which obs are to be
3594    !            accepted for assimilation and which are to be flagged for exclusion
3595    !            (obsFlags).
3596    !            grossrej()  = .true. if any channel had a gross error at the point
3597    !            cloudobs()  = .true. if CLW > clw_atms_nrl_LTrej (0.175) or precipobs
3598    !            precipobs() = .true. if precip. detected through NRL scattering indices
3599    !            waterobs()  = .true. if open water point
3600    !            iwvreject() = .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry
3601    !            for ch.20-22 over land)
3602    !###############################################################################
3603    call mwbg_reviewAllCritforFinalFlagsAtms(qcRejectLogic, grossrej, waterobs, &
3604                                             precipobs, scatec, scatbg, &
3605                                             iwvreject, riwv, &
3606                                             zdi, drycnt, landcnt, &
3607                                             rejcnt, iwvcnt, pcpcnt, flgcnt, &
3608                                             chanIgnoreInAllskyGenCoeff, &
3609                                             headerIndex, sensorIndex, obsSpaceData)
3610
3611    !###############################################################################
3612    ! PART 2 TESTS:
3613    !###############################################################################
3614
3615    ! allocations
3616    allocate(qcIndicator(actualNumChannel))
3617    allocate(B7CHCK(actualNumChannel))
3618    !  Initialisations
3619    qcIndicator(:) = 0
3620    B7CHCK(:) = 0
3621
3622    if ( RESETQC ) then
3623      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3624        obsFlags = 0
3625        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3626      end do BODY
3627    end if
3628
3629    ! 1) test 1: Check flag bit 7 on from the first bgckAtms program
3630    !  Includes observations flagged for cloud liquid water, scattering index,
3631    !  dryness index plus failure of several QC checks.
3632    call atmsMwhs2Test1Flagbit7Check (itest, sensorIndex, qcIndicator, &
3633                                      B7CHCK, headerIndex, obsSpaceData)
3634
3635    ! 2) test 2: Topography check (partial)
3636    call atmsMwhs2Test2TopographyCheck (itest, sensorIndex, &
3637                                        modelInterpTerrain, ICHTOPO, ZCRIT, B7CHCK, qcIndicator, &
3638                                        headerIndex, obsSpaceData)
3639
3640    ! 3) test 3: Uncorrected Tb check (single)
3641    !  Uncorrected datum (flag bit #6 off). In this case switch bit 11 ON.
3642    call atmsMwhs2Test3UncorrectedTbCheck (itest, sensorIndex, RESETQC, B7CHCK, qcIndicator, &
3643                                           headerIndex, obsSpaceData)
3644
3645    ! 4) test 4: "Rogue check" for (O-P) Tb residuals out of range. (single/full)
3646    !             Also, over WATER remove CH.17-22 if CH.17 |O-P|>5K (partial)
3647    !  Les observations, dont le residu (O-P) depasse par un facteur (roguefac)
3648    !   l'erreur totale des TOVS.
3649    !  N.B.: a reject by any of the 3 amsua surface channels 1-3 produces the
3650    !           rejection of ATMS sfc/tropospheric channels 1-6 and 16-17.
3651    !  OVER OPEN WATER
3652    !    ch. 17 Abs(O-P) > 5K produces rejection of all ATMS amsub channels 17-22.
3653    call atmsTest4RogueCheck (itest, sensorIndex, ROGUEFAC, waterobs, ISFCREJ, ICH2OMPREJ, &
3654                              B7CHCK, qcIndicator, headerIndex, obsSpaceData)
3655
3656    ! 5) test 5: Channel selection using array oer_tovutil(chan,sat)
3657    !  oer_tovutil = 0 (blacklisted)
3658    !                1 (assmilate)
3659    call atmsMwhs2Test5ChannelSelectionUsingTovutil(itest, sensorIndex, &
3660                                                    headerIndex, obsSpaceData)
3661
3662    !  Synthese de la controle de qualite au niveau de chaque point
3663    !  d'observation. Code:
3664    !            =0 aucun rejet, >0 au moins un canal rejete.
3665    KCHKPRF = 0
3666    do JI = 1, actualNumChannel
3667      KCHKPRF = MAX(KCHKPRF,qcIndicator(JI))
3668    end do
3669
3670    if ( mwbg_debug ) write(*,*)'KCHKPRF = ', KCHKPRF
3671
3672    ! reset global marker flag (55200) and mark it if observtions are rejected
3673    call resetQcCases(RESETQC, KCHKPRF, headerIndex, obsSpaceData)
3674
3675    if(mwbg_debug) then
3676      write(*,*) ' --------------------------------------------------------------- '
3677      write(*,*) ' Number of BURP file reports where Tb set to mwbg_realMissing  = ', numReportWithMissingTb
3678      write(*,*) ' --------------------------------------------------------------- '
3679      write(*,*) ' 1. Number of obs pts found over land/ice           = ', landcnt
3680      write(*,*) ' 2. Number of problem obs pts (Tb err, QCfail)      = ', rejcnt
3681      write(*,*) ' 3. Number of cloudy obs  (CLW > clw_min)           = ', cldcnt
3682      write(*,*) ' 4. Number of scatter/precip obs                    = ', pcpcnt
3683      write(*,*) ' 5. Number of pts with Mean 183 Ghz Tb < 240K       = ', iwvcnt
3684      write(*,*) ' 6. Number of pts flagged for AMSU-B Dryness Index  = ', drycnt
3685      write(*,*) ' --------------------------------------------------------------- '
3686      write(*,*) ' Total number of filtered obs pts                   = ', flgcnt
3687      write(*,*) ' ----------------------------------------------------------------'
3688      write(*,*) ' '
3689      write(*,*) ' Number of waterobs points converted to sea ice points         = ', seaIcePointNum
3690      write(*,*) ' Number of points where CLW/SI missing over water due bad data = ', clwMissingPointNum
3691      write(*,*) ' --------------------------------------------------------------- '
3692
3693      write(*,*) '   Meaning of newInformationFlag flag bits: '
3694      write(*,*) ' '
3695      write(*,*) '      BIT    Meaning'
3696      write(*,*) '       0     off=land or sea-ice, on=open water away from coast'
3697      write(*,*) '       1     Mean 183 Ghz [ch. 18-22] is missing'
3698      write(*,*) '       2     NRL CLW is missing (over water)'
3699      write(*,*) '       3     NRL > clw_atms_nrl_LTrej (0.175 kg/m2) (cloudobs)'
3700      write(*,*) '       4     scatec/scatbg > Lower Troposphere limit 9/10 (precipobs)'
3701      write(*,*) '       5     Mean 183 Ghz [ch. 18-22] Tb < 240K'
3702      write(*,*) '       6     CLW > clw_atms_nrl_UTrej (0.200 kg/m2)'
3703      write(*,*) '       7     Dryness Index rejection (for ch. 22)'
3704      write(*,*) '       8     scatec/scatbg > Upper Troposphere limit 18/15'
3705      write(*,*) '       9     Dryness Index rejection (for ch. 21)'
3706      write(*,*) '      10     Sea ice > 0.55 detected'
3707      write(*,*) '      11     Gross error in Tb (any chan.) or other QC problem (all channels rejected)'
3708      write(*,*) ' '
3709      write(*,*) '   New Element 13209 in BURP file = CLW (kg/m2)'
3710      write(*,*) '   New Element 13208 in BURP file = ECMWF Scattering Index'
3711      write(*,*) '   New Element 25174 in BURP file = newInformationFlag flag'
3712      write(*,*) ' '
3713    end if
3714
3715  end subroutine mwbg_tovCheckAtms
3716
3717  !--------------------------------------------------------------------------
3718  ! mwbg_tovCheckMwhs2
3719  !--------------------------------------------------------------------------
3720  subroutine mwbg_tovCheckMwhs2(qcIndicator, sensorIndex, modelInterpTerrain, &
3721                                RESETQC, modLSQ, lastHeader, headerIndex, obsSpaceData)
3722    !
3723    !:Purpose: Effectuer le controle de qualite des radiances tovs.
3724    !
3725    implicit none
3726
3727    ! Arguments:
3728    type(struct_obs),      intent(inout) :: obsSpaceData       ! obspaceData Object
3729    integer,               intent(in)    :: headerIndex        ! current header Index 
3730    integer,               intent(in)    :: sensorIndex        ! numero de satellite (i.e. indice)
3731    real(8),               intent(in)    :: modelInterpTerrain ! topographie du modele as being over land/ice, cloudy, bad IWV
3732    logical,               intent(in)    :: RESETQC            ! reset du controle de qualite?
3733    logical,               intent(in)    :: modLSQ             ! If active, recalculate land/sea qualifier and terrain type based on LG/MG
3734    logical,               intent(in)    :: lastHeader         ! active if last header
3735    integer, allocatable,  intent(out)   :: qcIndicator(:)  ! indicateur controle de qualite tovs par canal (=0 ok, >0 rejet)
3736
3737    ! Locals:
3738    integer, parameter :: maxScanAngleAMSU = 98
3739    integer, parameter :: ilsmOpt = 2   ! OPTION for values of MG (land/sea mask) and LG (ice) 
3740                                        !   at each observation point using values on 5x5 mesh 
3741                                        !   centered at each point.
3742                                        !   ilsmOpt = 1 --> use MAX value from all 25 mesh points
3743                                        !   ilsmOpt = 2 --> use value at central mesh point (obs location)
3744                                        !   ilsmOpt = 3 --> use AVG value from all 25 mesh points
3745    integer :: calcLandQualifierIndice, calcTerrainTypeIndice, KCHKPRF
3746    integer :: iRej, iNumSeaIce, JI, actualNumChannel
3747    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsFlags
3748    integer :: ICH2OMPREJ(6), chanIgnoreInAllskyGenCoeff(6), ICHTOPO(3)
3749    integer :: ITEST(mwbg_maxNumTest)
3750    integer, allocatable :: B7CHCK(:)
3751    logical :: waterobs, grossrej, reportHasMissingTb 
3752    logical :: cloudobs, iwvreject, precipobs
3753    logical, allocatable :: qcRejectLogic(:)
3754    real(8) :: zdi, scatec, scatbg, SeaIce, riwv, ZCRIT(3)
3755    real(8), allocatable :: ROGUEFAC(:)
3756    logical, save :: LLFIRST = .true.
3757    integer, save :: numReportWithMissingTb
3758    integer, save :: allcnt                 ! Number of Tovs obs
3759    integer, save :: drycnt                 ! Number of pts flagged for AMSU-B Dryness Index
3760    integer, save :: landcnt                ! Number of obs pts found over land/ice
3761    integer, save :: rejcnt                 ! Number of problem obs pts (Tb err, QCfail)
3762    integer, save :: iwvcnt                 ! Number of pts with Mean 183 Ghz Tb < 240K
3763    integer, save :: pcpcnt                 ! Number of scatter/precip obs
3764    integer, save :: cldcnt                 ! Number of water point covered by cloud
3765    integer, save :: flgcnt                 ! Total number of filtered obs pts
3766    integer, save :: seaIcePointNum         ! Number of waterobs points converted to sea ice points
3767    integer, save :: clwMissingPointNum     ! Number of points where cloudLiquidWaterPath/SI missing
3768                                            !  over water due bad data
3769
3770    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3771    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
3772
3773    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
3774    allocate(ROGUEFAC(actualNumChannel+tvs_channelOffset(sensorIndex)))
3775    ROGUEFAC(:) = (/2.0d0, 9.9d0, 9.9d0, 9.9d0, 9.9d0, 9.9d0, 9.9d0, 9.9d0, &
3776                    9.9d0, 2.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0/)
3777    if ( tvs_mwAllskyAssim ) ROGUEFAC(1:3) = 9.9d0
3778
3779    ! Channel sets for rejection in test 9
3780    !   These AMSU-B channels are rejected if ch. 10 O-P fails rogue check over OPEN WATER only
3781    ICH2OMPREJ(:) = (/10, 11, 12, 13, 14, 15/)
3782
3783    !  Data for TOPOGRAPHY CHECK
3784    !   Channel AMSUB-3 (mwhs2 ch 11) is rejected for topography > 2500m.
3785    !                   (mwhs2 ch 12) is rejected for topography > 2250m.
3786    !   Channel AMSUB-4 (mwhs2 ch 13) is rejected for topography > 2000m.
3787    ICHTOPO(:) = (/11, 12, 13/)
3788    ZCRIT(:) = (/2500.0d0, 2250.0d0, 2000.0d0/)
3789
3790    !  Test selection (0=skip test, 1=do test)
3791    !              1  2  3  4  5
3792    ITEST(:) = 0
3793    ITEST(1:5) = (/1, 1, 1, 1, 1/)
3794
3795    ! Channels excluded from gen_bias_corr in all-sky mode
3796    chanIgnoreInAllskyGenCoeff(:) = (/ 10, 11, 12, 13, 14, 15/)
3797
3798    ! Initialisation, la premiere fois seulement!
3799    if (LLFIRST) then
3800      numReportWithMissingTb = 0
3801      allcnt = 0
3802      flgcnt = 0
3803      landcnt = 0
3804      rejcnt = 0
3805      cldcnt = 0
3806      iwvcnt = 0
3807      pcpcnt = 0
3808      drycnt = 0
3809      seaIcePointNum = 0
3810      clwMissingPointNum = 0
3811      rejectionCodArray(:,:,:)  = 0
3812      rejectionCodArray2(:,:,:) = 0
3813      LLFIRST = .FALSE.
3814    end if
3815
3816    ! PART 1 TESTS:
3817
3818    !###############################################################################
3819    ! STEP 1 ) Determine which obs pts are over open water (i.e NOT near coasts or
3820    !          over/near land/ice) using model MG and LG fields from glbhyb2 ANAL
3821    !###############################################################################
3822    call atmsMwhs2landIceMask(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, ilsmOpt, &
3823                              headerIndex, obsSpaceData)
3824
3825    !###############################################################################
3826    ! STEP 2 ) Check for values of TB that are missing or outside physical limits.
3827    !###############################################################################
3828    call mwbg_grossValueCheck(50.0d0, 380.0d0, grossrej, headerIndex, sensorIndex, obsSpaceData)
3829
3830    !###############################################################################
3831    ! STEP 3 ) Preliminary QC checks --> set qcRejectLogic(actualNumChannel)=.true.
3832    !          for data that fail QC
3833    !###############################################################################
3834    call mwbg_firstQcCheckMwhs2(qcRejectLogic, calcLandQualifierIndice, calcTerrainTypeIndice, &
3835                                reportHasMissingTb, modLSQ, headerIndex, sensorIndex, obsSpaceData)
3836
3837    if ( reportHasMissingTb ) numReportWithMissingTb = numReportWithMissingTb + 1
3838    !  Exclude problem points from further calculations
3839    if ( COUNT(qcRejectLogic(:)) == actualNumChannel ) grossrej = .true.
3840
3841    !###############################################################################
3842    ! STEP 4 ) mwbg_nrlFilterMwhs2 returns cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, scatec, scatbg and also does sea-ice
3843    !          detection missing value for cloudLiquidWaterPathObs, scatec, scatbg is mwbg_realMissing (e.g. over
3844    !          land or sea-ice).Sets calcTerrainTypeIndice=0 (sea ice) for points where retrieved SeaIce
3845    !          >=0.55. Does nothing if calcTerrainTypeIndice=0 (sea ice) and retrieved SeaIce<0.55.
3846    !###############################################################################
3847    call mwbg_nrlFilterMwhs2(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, grossrej, &
3848                             scatec, scatbg, iNumSeaIce, iRej, SeaIce, &
3849                             headerIndex, sensorIndex, obsSpaceData)
3850
3851    seaIcePointNum = seaIcePointNum + iNumSeaIce
3852    clwMissingPointNum = clwMissingPointNum + iRej
3853
3854    !###############################################################################
3855    ! STEP 5 ) Apply NRL cloud filter, scattering index and sea-ice detection algorithms
3856    !          to OPEN WATER (waterobs=true) points.
3857    ! Points with SeaIce>0.55 are set to sea-ice points (waterobs --> false)
3858    !###############################################################################
3859    call mwbg_flagDataUsingNrlCritMwhs2(scatec, SeaIce, grossrej, waterobs, mwbg_useUnbiasedObsForClw, &
3860                                        iwvreject, cloudobs, precipobs, cldcnt , riwv, zdi, &
3861                                        headerIndex, sensorIndex, obsSpaceData)
3862
3863    !###############################################################################
3864    ! STEP 6 ) ! Review all the checks previously made to determine which obs are to be
3865    !            accepted for assimilation and which are to be flagged for exclusion
3866    !            (obsFlags).
3867    !            grossrej()  = .true. if any channel had a gross error at the point
3868    !            cloudobs()  = .true. if CLW > clw_mwhs2_nrl_LTrej (0.175) or precipobs
3869    !            precipobs() = .true. if precip. detected through NRL scattering indices
3870    !            waterobs()  = .true. if open water point
3871    !            iwvreject() = .true. if Mean 183 Ghz [ch. 11-15] Tb < 240K (too dry
3872    !            for ch.11-13 over land)
3873    !###############################################################################
3874    call mwbg_reviewAllCritforFinalFlagsMwhs2(qcRejectLogic, grossrej, calcTerrainTypeIndice, waterobs, &
3875                                              precipobs, scatec, scatbg, &
3876                                              iwvreject, riwv, &
3877                                              zdi, allcnt, drycnt, landcnt, &
3878                                              rejcnt, iwvcnt, pcpcnt, flgcnt, &
3879                                              chanIgnoreInAllskyGenCoeff, &
3880                                              headerIndex, sensorIndex, obsSpaceData)
3881
3882    !###############################################################################
3883    ! PART 2 TESTS:
3884    !###############################################################################
3885
3886    ! allocations
3887    allocate(qcIndicator(actualNumChannel))
3888    allocate(B7CHCK(actualNumChannel))
3889    !  Initialisations
3890    qcIndicator(:) = 0
3891    B7CHCK(:) = 0
3892
3893    if ( RESETQC ) then
3894      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
3895        obsFlags = 0
3896        call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
3897      end do BODY
3898    end if
3899
3900    ! 1) test 1: Check flag bit 7 on from the first bgckMwhs2 program
3901    !  Includes observations flagged for cloud liquid water, scattering index,
3902    !  dryness index plus failure of several QC checks.
3903    call atmsMwhs2Test1Flagbit7Check (itest, sensorIndex, qcIndicator, &
3904                                      B7CHCK, headerIndex, obsSpaceData)
3905
3906    ! 2) test 2: Topography check (partial)
3907    call atmsMwhs2Test2TopographyCheck (itest, sensorIndex, &
3908                                        modelInterpTerrain, ICHTOPO, ZCRIT, B7CHCK, qcIndicator, &
3909                                        headerIndex, obsSpaceData)
3910
3911    ! 3) test 3: Uncorrected Tb check (single)
3912    !  Uncorrected datum (flag bit #6 off). In this case switch bit 11 ON.
3913    call atmsMwhs2Test3UncorrectedTbCheck (itest, sensorIndex, RESETQC, B7CHCK, qcIndicator, &
3914                                           headerIndex, obsSpaceData)
3915
3916    ! 4) test 4: "Rogue check" for (O-P) Tb residuals out of range. (single/full)
3917    !             Also, over WATER remove CH.10-15 if CH.10 |O-P|>5K (full)
3918    !  Les observations, dont le residu (O-P) depasse par un facteur (roguefac)
3919    !   l'erreur totale des TOVS.
3920    !  OVER OPEN WATER
3921    !    ch. 10 Abs(O-P) > 5K produces rejection of all ATMS amsub channels 10-15.
3922    call Mwhs2Test4RogueCheck (itest, sensorIndex, ROGUEFAC, waterobs, ICH2OMPREJ, &
3923                               B7CHCK, qcIndicator, headerIndex, obsSpaceData)
3924
3925    ! 5) test 5: Channel selection using array oer_tovutil(chan,sat)
3926    !  oer_tovutil = 0 (blacklisted)
3927    !                1 (assmilate)
3928    call atmsMwhs2Test5ChannelSelectionUsingTovutil(itest, sensorIndex, &
3929                                                    headerIndex, obsSpaceData)
3930
3931    !  Synthese de la controle de qualite au niveau de chaque point
3932    !  d'observation. Code:
3933    !            =0 aucun rejet, >0 au moins un canal rejete.
3934    KCHKPRF = 0
3935    do JI = 1, actualNumChannel
3936      KCHKPRF = MAX(KCHKPRF,qcIndicator(JI))
3937    end do
3938
3939    if ( mwbg_debug ) write(*,*)'KCHKPRF = ', KCHKPRF
3940
3941    ! reset global marker flag (55200) and mark it if observtions are rejected
3942    call resetQcCases(RESETQC, KCHKPRF, headerIndex, obsSpaceData)
3943
3944    if (lastHeader) then
3945      write(*,*) ' --------------------------------------------------------------- '
3946      write(*,*) ' Number of obs pts read from BURP file                         = ', allcnt
3947      write(*,*) ' Number of BURP file reports where Tb set to mwbg_realMissing  = ', numReportWithMissingTb
3948      write(*,*) ' --------------------------------------------------------------- '
3949      write(*,*) ' 1. Number of obs pts found over land/ice           = ', landcnt
3950      write(*,*) ' 2. Number of problem obs pts (Tb err, QCfail)      = ', rejcnt
3951      write(*,*) ' 3. Number of cloudy obs  (CLW > clw_min)           = ', cldcnt
3952      write(*,*) ' 4. Number of scatter/precip obs                    = ', pcpcnt
3953      write(*,*) ' 5. Number of pts with Mean 183 Ghz Tb < 240K       = ', iwvcnt
3954      write(*,*) ' 6. Number of pts flagged for AMSU-B Dryness Index  = ', drycnt
3955      write(*,*) ' --------------------------------------------------------------- '
3956      write(*,*) ' Total number of filtered obs pts                   = ', flgcnt
3957      write(*,*) ' ----------------------------------------------------------------'
3958      write(*,*) ' '
3959      write(*,*) ' Number of waterobs points converted to sea ice points         = ', seaIcePointNum
3960      write(*,*) ' Number of points where CLW/SI missing over water due bad data = ', clwMissingPointNum
3961      write(*,*) ' --------------------------------------------------------------- '
3962
3963      write(*,*) '   Meaning of newInformationFlag flag bits: '
3964      write(*,*) ' '
3965      write(*,*) '      BIT    Meaning'
3966      write(*,*) '       0     off=land or sea-ice, on=open water away from coast'
3967      write(*,*) '       1     Mean 183 Ghz [ch. 11-15] is missing'
3968      write(*,*) '       2     NRL CLW is missing (over water)'
3969      write(*,*) '       3     NRL > clw_mwhs2_nrl_LTrej (0.175 kg/m2) (cloudobs)'
3970      write(*,*) '       4     scatec > Lower Troposphere limit=9 (precipobs)'
3971      write(*,*) '       5     Mean 183 Ghz [ch. 11-15] Tb < 240K'
3972      write(*,*) '       6     CLW > clw_mwhs2_nrl_UTrej (0.200 kg/m2)'
3973      write(*,*) '       7     Dryness Index rejection (for ch. 11)'
3974      write(*,*) '       8     scatbg > CMC amsu-b limit (land=0,sea=15,ice=40)'
3975      write(*,*) '       9     Dryness Index rejection (for ch. 12)'
3976      write(*,*) '      10     Sea ice > 0.55 detected'
3977      write(*,*) '      11     Gross error in Tb (any chan.) or other QC problem (all channels rejected)'
3978      write(*,*) ' '
3979      write(*,*) '   New Element 13209 in BURP file = CLW (kg/m2)'
3980      write(*,*) '   New Element 13208 in BURP file = Bennartz-Grody Scattering Index'
3981      write(*,*) '   New Element 25174 in BURP file = newInformationFlag flag'
3982      write(*,*) ' '
3983    end if
3984
3985  end subroutine mwbg_tovCheckMwhs2
3986
3987  !--------------------------------------------------------------------------
3988  ! mwbg_readGeophysicFieldsAndInterpolate
3989  !--------------------------------------------------------------------------
3990  subroutine mwbg_readGeophysicFieldsAndInterpolate(instName, modelInterpTerrain, &
3991                                                    modelInterpLandFrac, modelInterpSeaIce, &
3992                                                    headerIndex, obsSpaceData)
3993    !
3994    !:Purpose: Reads Modele Geophysical variables and save for the first time
3995    !          TOPOGRAPHIE (MF ou MX):
3996    !            - MF est la topographie filtree avec unites en metres (filtered ME).
3997    !            - MX est la topographie filtree avec unites en m2/s2  (geopotential topography).
3998    !            - Glace de Mer (GL)
3999    !            - Masque Terre-Mer (MG)
4000    !          Then Interpolate Those variables to observation location
4001    !
4002    implicit none
4003
4004    ! Arguments:
4005    character(len=*), intent(in)    :: instName            ! Instrument Name
4006    real(8),          intent(out)   :: modelInterpLandFrac ! model interpolated land fraction.
4007    real(8),          intent(out)   :: modelInterpTerrain  ! topographie filtree (en metres) et interpolees
4008    real(8),          intent(out)   :: modelInterpSeaIce   ! Glace de mer interpolees au pt d'obs.
4009    type(struct_obs), intent(inout) :: obsSpaceData  ! obspaceData Object
4010    integer,          intent(in)    :: headerIndex   ! current header Index 
4011
4012    ! Locals:
4013    integer, parameter :: MXLON = 5, MXLAT = 5, MXELM = 40
4014    real(4), parameter :: DLAT = 0.4, DLON = 0.6
4015    real(4), allocatable, save  :: GL(:)   ! Modele Glace de Mer (GL)
4016    real(4), allocatable, save  :: MG(:)   ! Modele Masque Terre-Mer (MG)
4017    real(4), allocatable, save  :: MT(:)   ! Modele Topographie (MT)
4018    integer, save  ::  gdmt                ! topo interpolation param
4019    integer, save  ::  gdmg                ! mask terre-mer interpolation param
4020    integer, save  ::  gdgl                ! glace interpolation param
4021    real(4), save  :: TOPOFACT             ! Facteur x topo pour avoir des unites en metre
4022    logical, save  :: ifFirstCall = .True. ! If .True. we read GL, MT and MG
4023    integer :: gdllsval, IUNGEO 
4024    integer :: ier, irec, ezqkdef, ezsetopt, FSTINF,FSTPRM,FCLOS, FSTLIR,FSTFRM, FNOM, FSTOUV
4025    integer :: NI, NJ, NK, IG1, IG2, IG3, IG4, IDUM1, IDUM2, IDUM3, IDUM4, IDUM5, IDUM6, IDUM7, IDUM8
4026    integer :: IDUM9, IDUM10, IDUM11, IDUM12, IDUM13, IDUM14, IDUM15, IDUM16, IDUM17, IDUM18
4027    integer :: NLAT, NLON, boxPointIndex, latIndex, lonIndex, boxPointNum
4028    character(len=12) :: ETIKXX
4029    character(len=4)  :: CLNOMVAR
4030    character(len=4)  :: NOMVXX
4031    character(len=2)  :: TYPXX
4032    character(len=1)  :: GRTYP
4033    real(4) :: XLAT, XLON, obsLat, obsLon
4034    real(4), allocatable :: ZLATBOX(:), ZLONBOX(:), MGINTBOX(:), MTINTBOX(:), GLINTBOX(:)
4035    logical :: readGlaceMask
4036
4037    ! lat/lon
4038    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
4039    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
4040
4041    ! Convert lat/lon to degrees
4042    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
4043    if (obsLon > 180.) obsLon = obsLon - 360.
4044    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
4045
4046    ! STEP 1: READ MT, GL and MG from the FST FILE
4047    readGlaceMask = .True.
4048    if (instName == 'ATMS') readGlaceMask = .False.
4049    if (ifFirstCall) then
4050      IUNGEO = 0
4051      IER = FNOM(IUNGEO,fileMgLg,'STD+RND+R/O',0)
4052
4053      ! 3) Lecture des champs geophysiques (MF/MX) du modele
4054      IER = FSTOUV(IUNGEO,'RND')
4055
4056      ! TOPOGRAPHIE (MF ou MX).
4057      !     MX est la topographie filtree avec unites en m2/s2  (geopotential topography).
4058
4059      IREC = FSTINF(IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1,' ','MX')
4060      if (IREC .GE. 0) then
4061        TOPOFACT = 9.80616
4062        CLNOMVAR = 'MX'
4063        if(allocated(MT)) deallocate(MT)
4064        allocate ( MT(NI*NJ), STAT=ier)
4065        IER = FSTLIR(MT,IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1, &
4066           ' ',CLNOMVAR)
4067      else
4068        call utl_abort ('bgckMicrowave_mod: ERREUR: LA TOPOGRAPHIE (MF or MX) EST INEXISTANTE')
4069      end if
4070
4071      IER = FSTPRM(IREC, IDUM1, IDUM2, IDUM3, IDUM4, &
4072                   IDUM5, IDUM6, IDUM7, IDUM8, IDUM9, IDUM10,  &
4073                   IDUM11, TYPXX, NOMVXX, ETIKXX, GRTYP, IG1, &
4074                   IG2, IG3, IG4, IDUM12, IDUM13, IDUM14,  &
4075                   IDUM15, IDUM16, IDUM17, IDUM18)
4076       write (*,*) 'GRILLE MT : ',grtyp,ni,nj, ig1,ig2,ig3,ig4
4077      ier  = ezsetopt('INTERP_DEGREE','LINEAR')
4078      ier  = ezsetopt('EXTRAP_DEGREE','ABORT')
4079      gdmt = ezqkdef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iungeo)
4080
4081      if (readGlaceMask) then
4082        ! MG
4083        IREC = FSTINF(IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1,' ','MG')
4084        if (IREC < 0) then
4085          call utl_abort ('bgckMicrowave_mod: ERREUR: LE MASQUE TERRE-MER EST INEXISTANT')
4086        end if
4087
4088        if(allocated(MG)) deallocate(MG)
4089        allocate ( MG(NI*NJ), STAT=ier)
4090        IER = FSTLIR(MG,IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1,' ','MG')
4091
4092        IER = FSTPRM(IREC, IDUM1, IDUM2, IDUM3, IDUM4, &
4093                     IDUM5, IDUM6, IDUM7, IDUM8, IDUM9, IDUM10, &
4094                     IDUM11, TYPXX, NOMVXX, ETIKXX, GRTYP, IG1,&
4095                     IG2, IG3, IG4, IDUM12, IDUM13, IDUM14, &
4096                     IDUM15, IDUM16, IDUM17, IDUM18)
4097        write (*,*) ' GRILLE MG : ',grtyp,ni,nj, ig1,ig2,ig3,ig4
4098        ier  = ezsetopt('INTERP_DEGREE','LINEAR')
4099        ier  = ezsetopt('EXTRAP_DEGREE','ABORT')
4100        gdmg = ezqkdef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iungeo)
4101        ! GL
4102        IREC = FSTINF(IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1,' ','GL')
4103        if (IREC < 0) then
4104          call utl_abort ('bgckMicrowave_mod: ERREUR: LE CHAMP GLACE DE MER EST INEXISTANT')
4105        end if
4106
4107        if(allocated(GL)) deallocate(GL)
4108        allocate ( GL(NI*NJ), STAT=ier)
4109        IER = FSTLIR(GL,IUNGEO,NI,NJ,NK,-1,' ',-1,-1,-1, ' ','GL')
4110
4111        IER = FSTPRM(IREC, IDUM1, IDUM2, IDUM3, IDUM4, &
4112                     IDUM5, IDUM6, IDUM7, IDUM8, IDUM9, IDUM10, &
4113                     IDUM11, TYPXX, NOMVXX, ETIKXX, GRTYP, IG1, &
4114                     IG2, IG3, IG4, IDUM12, IDUM13, IDUM14, &
4115                     IDUM15, IDUM16, IDUM17, IDUM18)
4116        write (*,*) ' GRILLE GL : ',grtyp,ni,nj, ig1,ig2,ig3,ig4
4117        ier  = ezsetopt('INTERP_DEGREE','LINEAR')
4118        ier  = ezsetopt('EXTRAP_DEGREE','ABORT')
4119        gdgl = ezqkdef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iungeo)
4120      else
4121        gdgl = -1
4122        gdmg = -1
4123      end if
4124      IER = FSTFRM(IUNGEO)
4125      IER = FCLOS(IUNGEO)
4126      ifFirstCall = .False.
4127    end if ! if (ifFirstCall)
4128
4129    ! STEP 3:  Interpolation de la glace et le champ terre/mer du modele aux pts TOVS.
4130    ! N.B.: on examine ces champs sur une boite centree sur chaque obs.
4131    boxPointNum = MXLAT*MXLON
4132    if(allocated(ZLATBOX)) deallocate(ZLATBOX)
4133    allocate (ZLATBOX(boxPointNum) , STAT=ier)
4134    if(allocated(ZLONBOX)) deallocate(ZLONBOX)
4135    allocate (ZLONBOX(boxPointNum) , STAT=ier)
4136    if(allocated(MTINTBOX)) deallocate(MTINTBOX)
4137    allocate (MTINTBOX(boxPointNum) , STAT=ier)
4138    if(allocated(GLINTBOX)) deallocate(GLINTBOX)
4139    allocate (GLINTBOX(boxPointNum) , STAT=ier)
4140    if(allocated(MGINTBOX)) deallocate(MGINTBOX)
4141    allocate (MGINTBOX(boxPointNum) , STAT=ier)
4142    NLAT = (MXLAT-1) / 2
4143    NLON = (MXLON-1) / 2
4144    boxPointIndex = 0
4145    do latIndex = -NLAT, NLAT
4146      XLAT = obsLat + latIndex*DLAT
4147      XLAT = MAX(-90.0,MIN(90.0,XLAT))
4148      do lonIndex = -NLON, NLON
4149        boxPointIndex = boxPointIndex + 1
4150        XLON = obsLon + lonIndex*DLON
4151        if ( XLON < -180. ) XLON = XLON + 360.
4152        if ( XLON >  180. ) XLON = XLON - 360.
4153        if ( XLON <    0. ) XLON = XLON + 360.
4154          ZLATBOX(boxPointIndex) = XLAT
4155          ZLONBOX(boxPointIndex) = XLON
4156        end do
4157    end do
4158    ier = ezsetopt('INTERP_DEGREE','LINEAR')
4159    ier = gdllsval(gdmt,mtintbox,mt,ZLATBOX,ZLONBOX,boxPointNum)
4160    if (ier < 0) then
4161      call utl_abort ('bgckMicrowave_mod: ERROR in the interpolation of MT')
4162    end if
4163    if(readGlaceMask) then
4164      ier = gdllsval(gdmg,mgintbox,mg,ZLATBOX,ZLONBOX,boxPointNum)
4165      if (ier < 0) then
4166        call utl_abort ('bgckMicrowave_mod: ERROR in the interpolation of MG')
4167      end if
4168      ier = gdllsval(gdgl,glintbox,gl,ZLATBOX,ZLONBOX,boxPointNum)
4169      if (ier < 0) then
4170        call utl_abort ('bgckMicrowave_mod: ERROR in the interpolation of GL')
4171      end if
4172    end if
4173
4174    if (mwbg_debug) then
4175      print *, ' ------------------  '
4176      print *, ' obsLat,obsLon = ', obsLat, obsLon
4177      print *, '   '
4178      print *, ' ZLATBOX = '
4179      print *,  (ZLATBOX(boxPointIndex),boxPointIndex=1,boxPointNum)
4180      print *, ' ZLONBOX = '
4181      print *,  (ZLONBOX(boxPointIndex),boxPointIndex=1,boxPointNum)
4182      print *, ' MGINTBOX = '
4183      print *,  (MGINTBOX(boxPointIndex),boxPointIndex=1,boxPointNum)
4184      print *, ' MTINTBOX = '
4185      print *,  (MTINTBOX(boxPointIndex),boxPointIndex=1,boxPointNum)
4186      print *, ' GLINTBOX = '
4187      print *,  (GLINTBOX(boxPointIndex),boxPointIndex=1,boxPointNum)
4188    end if
4189    modelInterpLandFrac = 0.0d0
4190    modelInterpTerrain = 0.0d0
4191    modelInterpSeaIce = 0.0d0
4192    do boxPointIndex = 1, MXLAT*MXLON
4193      modelInterpTerrain = MAX(modelInterpTerrain, &
4194                               real(MTINTBOX(boxPointIndex)/TOPOFACT,8))
4195      if (readGlaceMask) then
4196        modelInterpLandFrac = MAX(modelInterpLandFrac,real(MGINTBOX(boxPointIndex),8))
4197        modelInterpSeaIce = MAX(modelInterpSeaIce,real(GLINTBOX(boxPointIndex),8))
4198      end if
4199    end do
4200    if (mwbg_debug) then
4201      print *, ' modelInterpLandFrac = ', modelInterpLandFrac
4202      print *, ' modelInterpTerrain = ', modelInterpTerrain
4203      print *, ' modelInterpSeaIce = ', modelInterpSeaIce
4204    end if
4205  end subroutine mwbg_readGeophysicFieldsAndInterpolate
4206
4207  !--------------------------------------------------------------------------
4208  ! atmsMwhs2landIceMask
4209  !--------------------------------------------------------------------------
4210  subroutine atmsMwhs2landIceMask(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, ilsmOpt, &
4211                                  headerIndex, obsSpaceData)
4212    !
4213    !:Purpose: This routine sets waterobs array by performing a land/ice proximity check using
4214    !          using analysis MG and LG (or GL) fields used by the model which produces the trial field.
4215    !          The purpose of this check is to remove obs that reside close to coasts or ice,
4216    !          and so whose TBs may be contaminated.
4217    !          The GEM Global (glbhyb2) analysis contains MG and LG fields (on different grids).
4218    !          Adapted from: land_ice_mask_ssmis.ftn90 of mwbg_ssmis (D. Anselmo, S. Macpherson)
4219    !
4220    !          NOTE: The 0.1 deg binary ice field check from land_ice_mask_ssmis.ftn90
4221    !          was removed. The land/sea qualifier (calcLandQualifierIndice) and terrain type (calcTerrainTypeIndice) 
4222    !          are modified to indicate proximity to land and sea-ice but are NOT changed in output BURP file.
4223    !
4224    !          In the application of this check, a 5x5 mesh, with spacing defined by rlat_km and
4225    !          rlon_km, is positioned with its center over an obs pt (2 grid pts on either side
4226    !          of the obs pt; size of mesh is equal to 4*rlat_km x 4*rlon_km). The values of MG
4227    !          and LG are evaluated at the grid points of this mesh. For ilsmOpt=1 (or 3), the maximum
4228    !          (or average) determines whether the obs pt is too close to ice or land to be retained.
4229    !          For ilsmOpt=2, the value at the central mesh point is used.
4230    !          **NOTE: the threshold value for MG has a very strong effect on the distance
4231    !                  from land that is permitted for an obs to be retained
4232    !
4233    !
4234    !          Maximum FOV             x---x---x---x---x     ^
4235    !             = 75km x 75km        |   |   |   |   |     |
4236    !             for Meso-sphere CHs  x---x---x---x---x     |
4237    !             = 74km x 47km        |   |   |   |   |     |
4238    !             for 19 GHz           x---x---o---x---x     | = 4*rlat_km
4239    !                                  |   |   |   |   |     | = 4*40 km
4240    !                               ^  x---x---x---x---x     | = 160 km = 80 km north & south
4241    !                       rlat_km |  |   |   |   |   |     |
4242    !                               v  x---x---x---x---x     v
4243    !                                              <--->
4244    !                                             rlon_km
4245    !     
4246    !                                  <--------------->
4247    !                                     = 4*rlon_km
4248    !                                     = 4*40 km
4249    !                                     = 160 km = 80 km east & west
4250    !     
4251    !     
4252    !          MG value = 1.0  ==>  LAND       MG value = 0.0  ==>  OCEAN
4253    !          LG value = 1.0  ==>  ICE        LG value = 0.0  ==>  NO ICE
4254    !
4255    !          Variable Definitions
4256    !          --------------------
4257    !          - mxlat      : internal-  number of grid pts in lat. direction for mesh
4258    !          - mxlon      : internal-  number of grid pts in lon. direction for mesh
4259    !          - rlat_km    : internal-  spacing desired between mesh grid points in km
4260    !                                    along lat. direction
4261    !          - rlon_km    : internal-  spacing desired between mesh grid points in km
4262    !                                    along lon. direction
4263    !          - dlat       : internal-  spacing between mesh grid points along lon. direction
4264    !                                    in degrees computed from rlat_km
4265    !          - dlon       : internal-  spacing between mesh grid points along lon. direction
4266    !                                    in degrees computed from rlon_km
4267    !          - rkm_per_deg : internal- distance in km per degree
4268    !                                    = Earth radius * PI/180.0
4269    !                                    = 6371.01 km * PI/180.0
4270    !                                    = 111.195 km
4271    !          - nlat,nlon  : internal-  used to define the lat/lon of the grid pts of mesh
4272    !          - zlatbox    : internal-  lat values at all grid pts of mesh for all obs pts
4273    !          - zlonbox    : internal-  lon values at all grid pts of mesh for all obs pts
4274    !          - latmesh    : internal-  lat values at all grid pts of mesh for 1 obs pt
4275    !          - lonmesh    : internal-  lon values at all grid pts of mesh for 1 obs pt
4276    !          - mgintob    : internal-  interpolated MG values at all grid pts of mesh for 1 obs pt
4277    !          - lgintob    : internal-  interpolated LG values at all grid pts of mesh for 1 obs pt
4278    !          - mgintrp    : internal-  max. interpolated MG value on mesh for all obs pts
4279    !          - lgintrp    : internal-  max. interpolated LG value on mesh for all obs pts
4280    !          - MGthresh   : internal-  maximum allowable land fraction for obs to be kept
4281    !          - LGthresh   : internal-  maximum allowable ice  fraction for obs to be kept
4282    !
4283    implicit none
4284
4285    ! Arguments::
4286    integer,          intent(in)    :: ilsmOpt                ! option for "interpolated" value of MG, LG at each location, 1 = use MAX value taken from all mesh grid points, 2 = use CENTRAL mesh point value (value at obs location), 3 = use AVG value of all mesh grid points
4287    integer,          intent(out)   :: calcLandQualifierIndice! land/sea qualifier (0 = land, 1 = sea)
4288    integer,          intent(out)   :: calcTerrainTypeIndice  ! terrain-type (-1 land/open water, 0 = ice)
4289    logical,          intent(out)   :: waterobs               ! if obs over open water, far from coast/ice
4290    type(struct_obs), intent(inout) :: obsSpaceData           ! obspaceData Object
4291    integer,          intent(in)    :: headerIndex            ! current header Index 
4292
4293    ! Locals:
4294    logical, save :: firstCall = .true.
4295    integer, parameter :: mxlat = 5, mxlon = 5
4296    integer :: iungeo, ier, key
4297    integer, save :: ni, nj, nk, nilg, njlg
4298    integer, save :: ig1, ig2, ig3, ig4, ig1lg, ig2lg, ig3lg, ig4lg
4299    integer :: idum4, idum5, idum6, idum7, idum8, idum9, idum10, idum11
4300    integer :: idum12, idum13, idum14, idum15, idum16, idum17, idum18
4301    integer :: indx, ii, jj, nlat, nlon
4302    integer, parameter :: ii_obsloc = ((mxlat * mxlon) / 2) + 1  ! 1D-index of central mesh-point (obs location)
4303    real(4), parameter :: pi = 3.141592654
4304    real(4), parameter :: MGthresh = 0.01, LGthresh = 0.01
4305    real(4), parameter :: rlat_km = 40.0, rlon_km = 40.0
4306    real(4), parameter :: rkm_per_deg = 111.195
4307    real(4) :: xlat, xlatrad, xlon, rii, rjj
4308    real(4) :: dlat, dlon, obsLat, obsLon
4309    character(len=12) :: etikxx
4310    character(len=4)  :: nomvxx
4311    character(len=2)  :: typxx
4312    character(len=1), save :: grtyp, grtyplg
4313    logical  :: llg
4314    real(4), allocatable, save :: mg(:),lg(:)
4315    real(4), allocatable       :: latmesh(:), lonmesh(:)
4316    real(4), allocatable       :: mgintob(:), lgintob(:)
4317    real(4), allocatable       :: zlatbox(:), zlonbox(:)
4318    real(4) :: mgintrp, lgintrp
4319    ! RMNLIB interpolating functions:
4320    integer :: ezsetopt, ezqkdef
4321    integer :: gdllsval, gdid, gdidlg
4322    ! Define FORTRAN FST functions:
4323    integer, external :: fstinf, fstprm, fstlir, fnom, fclos
4324    integer, external :: fstouv, fstfrm
4325    integer :: idum1, idum2, idum3
4326
4327    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
4328    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
4329
4330    ! Convert lat/lon to degrees
4331    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
4332    if (obsLon > 180.) obsLon = obsLon - 360.
4333    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
4334
4335    ! Allocate space for arrays holding values on mesh grid pts.
4336    allocate(latmesh(mxlat*mxlon))
4337    allocate(lonmesh(mxlat*mxlon))
4338    allocate(mgintob(mxlat*mxlon))
4339    allocate(lgintob(mxlat*mxlon))
4340    allocate(zlatbox(mxlat*mxlon))
4341    allocate(zlonbox(mxlat*mxlon))
4342    latmesh(:) = 0.0
4343    lonmesh(:) = 0.0
4344    mgintob(:) = 0.0
4345    lgintob(:) = 0.0
4346    zlatbox(:) = 0.0
4347    zlonbox(:) = 0.0
4348
4349    ! Open FST file.
4350    iungeo = 0
4351    ier = fnom( iungeo,fileMgLg,'STD+RND+R/O',0 )
4352    ier = fstouv( iungeo,'RND' )
4353
4354    if (firstCall) then
4355      firstCall = .false.
4356
4357      ! Read MG field.
4358      key = fstinf(iungeo,ni,nj,nk,-1,' ',-1,-1,-1,' ' ,'MG')
4359      if ( key <  0 ) then
4360        call utl_abort('atmsMwhs2landIceMask: The MG field is MISSING')
4361      end if
4362
4363      call utl_reAllocate(mg, ni*nj)
4364
4365      ier = fstlir(mg,iungeo,ni,nj,nk,-1,' ',-1,-1,-1,' ','MG')
4366
4367      ier = fstprm(key,idum1,idum2,idum3,idum4,idum5,idum6,idum7,idum8,    &
4368                   idum9,idum10,idum11,typxx,nomvxx,etikxx,grtyp,ig1,ig2,  &
4369                   ig3,ig4,idum12,idum13,idum14,idum15,idum16,idum17,      &
4370                   idum18)
4371
4372      ! Read LG field. Use GL field as backup.
4373      ! **CAUTION**: Discontinuities in GL field may cause interpolation problems! LG field is preferable.
4374      llg = .false.
4375      key = fstinf(iungeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ' ,'LG')
4376      if ( key <  0 ) then
4377        key = fstinf(iungeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ' ,'GL')
4378        if ( key <  0 ) then
4379          call utl_abort('atmsMwhs2landIceMask: The LG or GL field is MISSING')
4380        end if
4381      else
4382        llg = .true.
4383      end if
4384
4385      call utl_reAllocate(lg, nilg*njlg)
4386
4387      if ( llg ) then
4388        ier = fstlir(lg,iungeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ','LG')
4389      else
4390        ier = fstlir(lg,iungeo,nilg,njlg,nk,-1,' ',-1,-1,-1,' ','GL')
4391      end if
4392
4393      ier = fstprm(key,idum1,idum2,idum3,idum4,idum5,idum6,idum7,idum8,          &
4394                   idum9,idum10,idum11,typxx,nomvxx,etikxx,grtyplg,ig1lg,ig2lg,  &
4395                   ig3lg,ig4lg,idum12,idum13,idum14,idum15,idum16,idum17,        &
4396                   idum18)
4397
4398    end if ! firstCall
4399
4400    ! For each obs pt, define a grid of artificial pts surrounding it.
4401    nlat = ( mxlat - 1 ) / 2
4402    nlon = ( mxlon - 1 ) / 2
4403
4404    dlat = rlat_km / rkm_per_deg
4405    indx = 0
4406
4407    do ii = -nlat, nlat
4408      rii = float(ii)
4409      xlat = obsLat + rii*dlat
4410      xlat = max( -90.0, min(90.0,xlat) )
4411      xlatrad = xlat * pi / 180.0
4412
4413      do jj = -nlon, nlon
4414        dlon = rlon_km / ( rkm_per_deg*cos(xlatrad) )
4415        rjj = float(jj)
4416        indx = indx + 1
4417        xlon = obsLon + rjj*dlon
4418        if ( xlon < -180. ) xlon = xlon + 360.
4419        if ( xlon >  180. ) xlon = xlon - 360.
4420        if ( xlon <    0. ) xlon = xlon + 360.
4421        zlatbox(indx) = xlat
4422        zlonbox(indx) = xlon
4423      end do
4424
4425    end do
4426
4427    ! Interpolate values from MG and LG field to grid pts of mesh centred over each obs pt.
4428    ! Determine for each obs pt, the max interpolated MG and LG value within the box
4429    ! surrounding it.
4430    ier    = ezsetopt('INTERP_DEGREE','LINEAR')
4431    gdid   = ezqkdef(ni,nj,grtyp,ig1,ig2,ig3,ig4,iungeo)
4432    gdidlg = ezqkdef(nilg,njlg,grtyplg,ig1lg,ig2lg,ig3lg,ig4lg,iungeo)
4433
4434    mgintrp = 0.0
4435    lgintrp = 0.0
4436
4437    latmesh = zlatbox(:)
4438    lonmesh = zlonbox(:)
4439
4440    ier  = gdllsval(gdid,mgintob,mg,latmesh,lonmesh,mxlat*mxlon)
4441    ier  = gdllsval(gdidlg,lgintob,lg,latmesh,lonmesh,mxlat*mxlon)
4442
4443    if ( ilsmOpt == 1 ) then
4444      mgintrp = maxval(mgintob(:))
4445      lgintrp = maxval(lgintob(:))
4446    elseif ( ilsmOpt == 2) then
4447      mgintrp = mgintob(ii_obsloc)
4448      lgintrp = lgintob(ii_obsloc)
4449    else
4450      mgintrp = sum(mgintob(:))/real((mxlat*mxlon))
4451      lgintrp = sum(lgintob(:))/real((mxlat*mxlon))
4452    end if      
4453
4454    !  Initialize all obs as being over land and free of ice or snow.
4455    !  Determine which obs are over open water.
4456    waterobs = .false.   ! not over open water
4457    calcTerrainTypeIndice = -1             ! no ice (reset terain type)
4458    calcLandQualifierIndice = 0 ! land   (reset land/sea qualifier)
4459
4460    if ( mgintrp < MGthresh ) calcLandQualifierIndice = 1  ! ocean point away from coast
4461    if ( lgintrp >= LGthresh .and. calcLandQualifierIndice == 1 ) calcTerrainTypeIndice = 0  ! sea-ice affected point
4462    if ( lgintrp  < LGthresh .and. calcLandQualifierIndice == 1 ) then
4463      waterobs = .true.  ! water point not in close proximity to land or sea-ice
4464    end if
4465
4466    ier = fstfrm(iungeo)
4467    ier = fclos(iungeo)
4468
4469  end subroutine atmsMwhs2landIceMask
4470
4471  !--------------------------------------------------------------------------
4472  ! mwbg_computeMwhs2SurfaceType
4473  !--------------------------------------------------------------------------
4474  subroutine mwbg_computeMwhs2SurfaceType(obsSpaceData)
4475    !
4476    !:Purpose: Compute surface type element and update obsSpaceData.
4477    !
4478    implicit none
4479
4480    ! Arguments:
4481    type(struct_obs), intent(inout) :: obsSpaceData  ! ObsSpaceData object
4482
4483    ! Locals:
4484    integer, parameter :: ilsmOpt = 2   ! OPTION for values of MG (land/sea mask) and LG (ice) 
4485                                        ! at each observation point using values on 5x5 mesh 
4486                                        ! centered at each point.
4487                                        ! ilsmOpt = 1 --> use MAX value from all 25 mesh points
4488                                        ! ilsmOpt = 2 --> use value at central mesh point (obs location)
4489                                        ! ilsmOpt = 3 --> use AVG value from all 25 mesh points
4490    integer :: calcLandQualifierIndice, calcTerrainTypeIndice, codtyp, headerIndex
4491    logical :: waterobs, mwhs2DataPresent
4492
4493    write(*,*) 'ssbg_computeMwhs2SurfaceType: Starting'
4494
4495    mwhs2DataPresent = .false.
4496    call obs_set_current_header_list(obsSpaceData,'TO')
4497
4498    HEADER0: do
4499      headerIndex = obs_getHeaderIndex(obsSpaceData)
4500      if (headerIndex < 0) exit HEADER0
4501      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
4502      if ( tvs_isIdBurpInst(codtyp,'mwhs2') ) then
4503        mwhs2DataPresent = .true.
4504        exit HEADER0
4505      end if
4506    end do HEADER0
4507
4508    if ( .not. mwhs2DataPresent ) then
4509      write(*,*) 'WARNING: WILL NOT RUN ssbg_computeMwhs2SurfaceType since no MWHS2 DATA is found'
4510      return
4511    end if
4512
4513    call mwbg_init()
4514
4515    if ( .not. modLSQ ) then
4516      write(*,*) 'WARNING: WILL NOT RUN ssbg_computeMwhs2SurfaceType since MODLSQ is not activated'
4517      return
4518    end if
4519
4520    write(*,*) 'MWHS2 data found and modLSQ option activated!'
4521    write(*,*) '-->  Output file will contain recomputed values for land/sea qualifier and terrain type based on LG/MG.'
4522
4523    call obs_set_current_header_list(obsSpaceData,'TO')
4524    HEADER1: do
4525      headerIndex = obs_getHeaderIndex(obsSpaceData)
4526      if (headerIndex < 0) exit HEADER1
4527
4528      call atmsMwhs2landIceMask(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, ilsmOpt, &
4529                                headerIndex, obsSpaceData)
4530
4531      call obs_headSet_i(obsSpaceData, OBS_STYP, headerIndex, calcLandQualifierIndice)
4532      call obs_headSet_i(obsSpaceData, OBS_TTYP, headerIndex, calcTerrainTypeIndice)
4533
4534    end do HEADER1
4535
4536    write(*,*) 'ssbg_computeMwhs2SurfaceType: Finished'
4537
4538  end subroutine mwbg_computeMwhs2SurfaceType
4539
4540  !--------------------------------------------------------------------------
4541  ! mwbg_grossValueCheck  
4542  !--------------------------------------------------------------------------
4543
4544  subroutine mwbg_grossValueCheck(ztbThresholdMin, ztbThresholdMax, grossrej, headerIndex, sensorIndex, obsSpaceData)
4545    !
4546    !:Purpose: Check Tbs for values that are missing or outside physical limits.
4547    !          REJECT ALL CHANNELS OF ONE IS FOUND TO BE BAD.
4548    !
4549    implicit none
4550
4551    ! Arguments:
4552    real(8),          intent(in)    :: ztbThresholdMin         ! ztb threshold for rejection
4553    real(8),          intent(in)    :: ztbThresholdMax         ! ztb threshold for rejection
4554    logical,          intent(out)   :: grossrej                ! logical array defining which obs are to be rejected
4555    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
4556    integer,          intent(in)    :: headerIndex  ! current header Index 
4557    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice)
4558
4559    ! Locals:
4560    integer :: actualNumChannel, bodyIndex, bodyIndexBeg, bodyIndexEnd
4561    integer :: obsChanNum, obsChanNumWithOffset
4562    real(8) :: obsTb, obsTbBiasCorr
4563    real(8), allocatable :: ztb(:) ! biased or unbiased radiances
4564
4565    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
4566    allocate(ztb(actualNumChannel))
4567    ztb(:) = mwbg_realMissing
4568
4569    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
4570    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
4571    
4572    grossrej = .true.
4573    if ( mwbg_useUnbiasedObsForClw ) then
4574      BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
4575        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
4576        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)      
4577        obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
4578
4579        ztb(obsChanNum) = obsTb
4580      end do BODY
4581    else
4582      BODY2: do bodyIndex = bodyIndexBeg, bodyIndexEnd
4583        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
4584        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)  
4585        obsTb = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
4586        obsTbBiasCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
4587
4588        if (obsTbBiasCorr /= mwbg_realMissing) then
4589          ztb(obsChanNum) = obsTb - obsTbBiasCorr
4590        else
4591          ztb(obsChanNum) = obsTb
4592        end if
4593      end do BODY2
4594    end if
4595
4596    if (all(ztb(:) > ztbThresholdMin) .and. all(ztb(:) < ztbThresholdMax)) grossrej = .false.
4597
4598  end subroutine mwbg_grossValueCheck
4599
4600  !--------------------------------------------------------------------------
4601  ! mwbg_firstQcCheckAtms
4602  !--------------------------------------------------------------------------
4603  subroutine mwbg_firstQcCheckAtms(qcRejectLogic, grossrej, calcLandQualifierIndice, calcTerrainTypeIndice, &
4604                                   reportHasMissingTb, headerIndex, sensorIndex, obsSpaceData)
4605    !
4606    !:Purpose: This routine performs basic quality control checks on the data. It sets array
4607    !          qcRejectLogic(actualNumChannel) elements to .true. to flag data with failed checks.
4608    !
4609    !          The 6 QC checks are:
4610    !            - 1) Invalid land/sea qualifier or terrain type,
4611    !            - 2) Invalid field of view number,
4612    !            - 3) Satellite zenith angle missing or out of range, (> 75 deg),
4613    !            - 4) lat,lon check (lat,lon = O(-90.), 0(-180.))
4614    !            - 5) Change in (computed) calcLandQualifierIndice,calcTerrainTypeIndice from (input) 
4615    !            - 6) ATMS quality flag check (qual. flag elements 33078,33079,33080,33081)
4616    !
4617    !          landQualifierIndice,terrainTypeIndice (from MG,LG fields).
4618    !
4619    !          landQualifierIndice= 0,1 (from hi-res land/sea mask interpolated to obs point [CMDA])
4620    !
4621    !          terrainTypeIndice=-1,0 (from hi-res ice analysis  interpolated to obs point [CMDA])
4622    !
4623    !          calcLandQualifierIndice= 0,1 (from max interp MG (0.0 to 1.0) in box surrounding obs point)
4624    !
4625    !          calcTerrainTypeIndice=-1,0 (from max interp LG (0.0 to 1.0) in box surrounding obs point)
4626    !
4627    !          In most cases, qcRejectLogic(actualNumChannel) is set to .true. for all channels at point ii
4628    !          if the check detects a problem. In addition, Tb (obsTb) is set to missing_value
4629    !          for checks 3 and 4 fails.
4630    !
4631    implicit none
4632
4633    ! Arguments:
4634    integer,              intent(in)    :: calcLandQualifierIndice ! land/sea qualifier (0 = land, 1 = sea)
4635    integer,              intent(in)    :: calcTerrainTypeIndice   ! terrain-type (-1 land/open water, 0 = ice)
4636    logical,              intent(in)    :: grossrej                ! .true. if 1 or more Tb fail gross error check
4637    logical,              intent(out)   :: reportHasMissingTb      ! .true. if Tb(obsTb) are set to missing_value
4638    logical, allocatable, intent(out)   :: qcRejectLogic(:)        ! qcRejectLogic = .false. on input
4639    type(struct_obs),     intent(inout) :: obsSpaceData            ! obspaceData Object
4640    integer,              intent(in)    :: headerIndex             ! current header Index 
4641    integer,              intent(in)    :: sensorIndex             ! numero de satellite (i.e. indice)
4642
4643    ! Locals:
4644    integer :: indx1, icount, landQualifierIndice, terrainTypeIndice, satScanPosition, actualNumChannel
4645    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset, channelIndex
4646    integer :: obsQcFlag1(3), obsQcFlag2
4647    integer, allocatable :: obsChannels(:)
4648    real(8) :: obsLat, obsLon, satZenithAngle, obsTb
4649    logical :: fail, fail1, fail2
4650
4651    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
4652    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
4653
4654    reportHasMissingTb = .false.
4655    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
4656    allocate(qcRejectLogic(actualNumChannel))
4657    qcRejectLogic(:) = .false.  ! Flag for preliminary QC checks
4658
4659    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
4660    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
4661    satScanPosition = obs_headElem_i(obsSpaceData, OBS_FOV , headerIndex)
4662
4663    ! lat/lon
4664    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
4665    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
4666
4667    ! Convert lat/lon to degrees
4668    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
4669    if (obsLon > 180.0d0) obsLon = obsLon - 360.0d0
4670    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
4671    
4672    ! terrain type
4673    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
4674    
4675    ! If terrain type is missing, set it to -1 for the QC programs
4676    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
4677
4678    obsQcFlag1(:) = 0
4679    if (instName == 'ATMS') then  
4680      obsQcFlag1(1) = obs_headElem_i(obsSpaceData, OBS_AQF1, headerIndex) 
4681      obsQcFlag1(2) = obs_headElem_i(obsSpaceData, OBS_AQF2, headerIndex) 
4682      obsQcFlag1(3) = obs_headElem_i(obsSpaceData, OBS_AQF3, headerIndex) 
4683    end if
4684
4685    allocate(obsChannels(actualNumChannel))
4686    do channelIndex = 1, actualNumChannel
4687      obsChannels(channelIndex) = channelIndex + tvs_channelOffset(sensorIndex)
4688    end do
4689
4690    ! Global rejection checks
4691
4692    ! Check if number of channels is correct
4693    !if ( actualNumChannel /= mwbg_maxNumChan ) then
4694    !  write(*,*) 'WARNING: Number of channels (',actualNumChannel, ') is not equal to mwbg_maxNumChan (', mwbg_maxNumChan,')'
4695    !  write(*,*) '         All data flagged as bad and returning to calling routine!'
4696    !  qcRejectLogic(:) = .true.  ! flag all data in report as bad
4697    !  return
4698    !end if
4699
4700    ! Check for errors in channel numbers (should be 1-22 for each location ii)
4701    ! For this, tvs_channelOffset(sensorIndex)=0 and total number of channels 
4702    ! in obsSpaceData should be equal to 22     
4703    fail = .false.
4704    if (tvs_channelOffset(sensorIndex) /= 0 .or. &
4705        obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) /= actualNumChannel) then
4706      fail = .true.
4707    end if
4708    if ( fail ) then
4709      write(*,*) 'WARNING: Bad channel number(s) detected!'
4710      write(*,*) '         All data flagged as bad and returning to calling routine!'
4711      write(*,*) '  obsChannels(actualNumChannel) array = ', obsChannels(:)
4712      qcRejectLogic(:) = .true.  ! flag all data in report as bad
4713      return
4714    end if
4715
4716    ! 1) invalid land/sea qualifier or terrain type
4717    !  landQualifierIndice = 0 (land),     1 (sea)
4718    !  terrainTypeIndice = 0 (sea-ice), -1 otherwise
4719    !  calcLandQualifierIndice = 1 (sea, away from land/coast [MG]),      0 otherwise
4720    !  calcTerrainTypeIndice = 0 (over or near analyzed sea-ice [LG]), -1 otherwise
4721    fail = .false.
4722    if ( landQualifierIndice < 0  .or. landQualifierIndice > 2 ) fail = .true.
4723    if ( terrainTypeIndice < -1 .or. terrainTypeIndice > 1 ) fail = .true.
4724    if ( fail ) then
4725      write(*,*) 'WARNING: Invalid land/sea qualifier or terrain type!'
4726      write(*,*) '  landQualifierIndice, terrainTypeIndice, (lat, lon) = ', landQualifierIndice, terrainTypeIndice, '(',obsLat, obsLon,')'
4727    end if
4728
4729    if ( landQualifierIndice == 0 .and. terrainTypeIndice == 0 ) then
4730      fail = .true.
4731      write(*,*) 'WARNING: Sea ice point (terrainTypeIndice=0) at land point (landQualifierIndice=0)!'
4732      write(*,*) ' lat, lon =  ', obsLat, obsLon
4733    end if
4734    if ( fail ) qcRejectLogic(:) = .true.
4735
4736    fail = .false.
4737    if ( calcLandQualifierIndice < 0 .or. calcLandQualifierIndice > 2 ) fail = .true.
4738    if ( calcTerrainTypeIndice < -1 .or. calcTerrainTypeIndice > 1 ) fail = .true.
4739    if ( fail ) then
4740      write(*,*) 'WARNING: Invalid model-based (MG/LG) land/sea qualifier or terrain type!'
4741      write(*,*) '  calcLandQualifierIndice, calcTerrainTypeIndice, (lat, lon) = ', &
4742                  calcLandQualifierIndice, calcTerrainTypeIndice, '(',obsLat, obsLon,')'
4743    end if
4744    if ( fail ) qcRejectLogic(:) = .true.
4745
4746    ! 2) invalid field of view number
4747    fail = .false.
4748    if ( satScanPosition < 1 .or. satScanPosition > mwbg_maxScanAngle ) then
4749      fail = .true.
4750      write(*,*) 'WARNING: Invalid field of view! satScanPosition, lat, lon = ', satScanPosition, obsLat, obsLon
4751    end if
4752    if ( fail ) qcRejectLogic(:) = .true.
4753
4754    ! 3) satellite zenith angle missing or out of range (> 75 deg)
4755    !  If bad zenith, then set Tb (and zenith) = missing value
4756    indx1 = 1
4757    fail = .false.
4758    if ( satZenithAngle > 75.0d0 .or. satZenithAngle < 0.0d0 ) then
4759      fail = .true.
4760      write(*,*) 'WARNING: Bad or missing zenith angle! zenith, lat, lon = ', satZenithAngle, obsLat, obsLon
4761      satZenithAngle = mwbg_realMissing
4762      reportHasMissingTb = .true.
4763    end if
4764    if ( fail ) then
4765      qcRejectLogic(:) = .true.
4766      do bodyIndex = bodyIndexBeg, bodyIndexEnd
4767        obsTb = mwbg_realMissing
4768        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
4769      end do
4770    end if
4771
4772    ! 4) Lat,lon check
4773    ! Check for undecoded BURP file integer values of lat,lon = 0,0
4774    ! (usually associated with missing zenith angle and erroneous Tb=330K)
4775
4776    icount = 0
4777    fail = .false.
4778    if ( obsLat == -90.0d0  .and. obsLon == -180.0d0 ) then
4779      fail = .true.
4780      icount =  icount + 1
4781      reportHasMissingTb = .true.
4782    end if
4783    if ( fail ) then
4784      qcRejectLogic(:) = .true.
4785      do bodyIndex = bodyIndexBeg, bodyIndexEnd
4786        obsTb = mwbg_realMissing
4787        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
4788      end do
4789    end if
4790    if ( icount > 0 ) write(*,*) 'WARNING: Bad lat,lon pair(s) detected. Number of locations = ', icount
4791
4792    icount = 0
4793    fail = .false.
4794    if ( abs(obsLat) > 90.0d0  .or. abs(obsLon) > 180.0d0 ) then
4795      fail = .true.
4796      icount =  icount + 1
4797      reportHasMissingTb = .true.
4798    end if
4799    if ( fail ) then
4800      qcRejectLogic(:) = .true.
4801      do bodyIndex = bodyIndexBeg, bodyIndexEnd
4802        obsTb = mwbg_realMissing
4803        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
4804      end do
4805    end if
4806    if ( icount > 0 ) write(*,*) 'WARNING: Lat or lon out of range! Number of locations = ', icount
4807
4808    !  5) Change in land/sea qualifier or terrain-type based on MG,LG fields
4809    icount = 0
4810    fail = .false.
4811    if ( (landQualifierIndice /= calcLandQualifierIndice) .or. &
4812         (terrainTypeIndice /= calcTerrainTypeIndice) ) then
4813      fail = .true.
4814    end if
4815    if ( fail ) icount =  icount + 1
4816
4817    ! 6) ATMS quality flag check (qual. flag elements 33078,33079,33080,33081)
4818
4819    !  33078 Geolocation quality code     obsQcFlag1(1)  code value = 0-15 (0= OK, 15=misg)
4820    !  33079 Granule level quality flags  obsQcFlag1(2)  16 bit flag  (start bit 6(2^5)=32) (misg=2^16-1 = 65535)
4821    !  33080 Scan level quality flags     obsQcFlag1(3)  20 bit flag  (start bit 7(2^6)=64) (misg=2^20-1) 
4822    !  33081 Channel data quality flags   obsQcFlag2        12 bit flag  (start bit 3(2^2)=4)  (misg=2^12-1)
4823    !
4824    !  See http://www.wmo.int/pages/prog/www/WMOCodes/WMO306_vI2/2010edition/BUFRver16/BUFR_16_0_0_TableD.pdf
4825
4826    fail1 = .false.
4827    fail = .false.
4828    if ( (obsQcFlag1(1) > 0) .or. (obsQcFlag1(2) >= 32) .or. (obsQcFlag1(3) >= 64) ) then
4829      write(*,*) 'WARNING: INFO BLOCK QC flag(s) indicate problem with data'
4830      write(*,*) ' ele33078 = ',obsQcFlag1(1),' ele33079 = ',obsQcFlag1(2),' ele33080 = ', obsQcFlag1(3)
4831      write(*,*) ' lat, lon = ', obsLat, obsLon
4832      fail1 = .true.
4833      if ( grossrej ) write(*,*) ' NOTE: grossrej is also true for this point!'
4834    end if
4835
4836    do bodyIndex = bodyIndexBeg, bodyIndexEnd
4837      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
4838      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
4839      obsQcFlag2 = obs_bodyElem_i(obsSpaceData, OBS_QCF2, bodyIndex)
4840
4841      fail2 = .false.
4842      if ( obsQcFlag2 >= 4 ) then
4843        !write(*,*) 'WARNING: DATA BLOCK QC flag ele33081 = ', obsQcFlag2
4844        !write(*,*) '    Lat, lon, channel = ', obsLat, obsLon, obsChanNum
4845        fail2 = .true.
4846        fail = .true.
4847        !if ( (.not. fail1) .and. grossrej ) write(*,*) ' NOTE: grossrej is also true for this point!'
4848      end if
4849      if ( fail2 .or. fail1 ) qcRejectLogic(obsChanNum) = .true.
4850    end do
4851    if ( fail ) write(*,*) 'WARNING: DATA BLOCK QC flag ele33081 >= 4 for one or more channels! lat, lon = ', obsLat, obsLon
4852
4853    !write(*,*) 'mwbg_firstQcCheckAtms: Number of data processed and flagged = ', &
4854    !           actualNumChannel, count(qcRejectLogic)
4855
4856    call obs_headSet_r(obsSpaceData, OBS_SZA, headerIndex, satZenithAngle)
4857
4858  end subroutine mwbg_firstQcCheckAtms
4859
4860  !--------------------------------------------------------------------------
4861  ! mwbg_firstQcCheckMwhs2
4862  !--------------------------------------------------------------------------
4863  subroutine mwbg_firstQcCheckMwhs2(qcRejectLogic, calcLandQualifierIndice, calcTerrainTypeIndice, &
4864                                    reportHasMissingTb, modLSQ, headerIndex, sensorIndex, obsSpaceData)
4865    !
4866    !:Purpose: This routine performs basic quality control checks on the data. It sets array
4867    !          qcRejectLogic(actualNumChannel) elements to .true. to flag data with failed checks. Check 1
4868    !          (for landQualifierIndice,terrainTypeIndice) and check 5 are skipped if modlsqtt=.true., 
4869    !          as the original values will be replaced in output file by calcLandQualifierIndice,calcTerrainTypeIndice.
4870    !
4871    !          The 5 QC checks are:
4872    !            - 1) Invalid land/sea qualifier or terrain type,
4873    !            - 2) Invalid field of view number,
4874    !            - 3) Satellite zenith angle missing or out of range, (> 75 deg),
4875    !            - 4) lat,lon check (lat,lon = O(-90.), 0(-180.))
4876    !            - 5) Change in (computed) calcLandQualifierIndice,calcTerrainTypeIndice 
4877    !          from (input) landQualifierIndice,terrainTypeIndice (from MG,LG fields).
4878    !
4879    !          landQualifierIndice= 0,1 (from hi-res land/sea mask interpolated to obs point [CMDA])
4880    !
4881    !          terrainTypeIndice=-1,0 (from hi-res ice analysis  interpolated to obs point [CMDA])
4882    !
4883    !          calcLandQualifierIndice= 0,1 (from max interp MG (0.0 to 1.0) in box surrounding obs point)
4884    !
4885    !          calcTerrainTypeIndice=-1,0 (from max interp LG (0.0 to 1.0) in box surrounding obs point)
4886    !
4887    !          In most cases, qcRejectLogic(ii,actualNumChannel) is set to .true. for all channels at point ii
4888    !          if the check detects a problem. In addition, Tb (obsTb) is set to missing_value
4889    !          for checks 3 and 4 fails.
4890    !
4891    implicit none
4892
4893    ! Arguments:
4894    integer,              intent(in)    :: calcLandQualifierIndice! land/sea qualifier (0 = land, 1 = sea)
4895    integer,              intent(in)    :: calcTerrainTypeIndice  ! terrain-type (-1 land/open water, 0 = ice)
4896    logical,              intent(out)   :: reportHasMissingTb     ! true if Tb(obsTb) are set to missing_value
4897    logical,              intent(in)    :: modLSQ                 ! If active, recalculate land/sea qualifier and terrain type based on LG/MG
4898    logical, allocatable, intent(inout) :: qcRejectLogic(:)       ! qcRejectLogic = .false. on input
4899    type(struct_obs),     intent(inout) :: obsSpaceData           ! obspaceData Object
4900    integer,              intent(in)    :: headerIndex            ! current header Index 
4901    integer,              intent(in)    :: sensorIndex            ! numero de satellite (i.e. indice)
4902
4903    ! Locals:
4904    integer :: icount, landQualifierIndice, terrainTypeIndice, satScanPosition, actualNumChannel
4905    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, channelIndex
4906    integer, allocatable :: obsChannels(:)
4907    real(8) :: obsLat, obsLon, satZenithAngle
4908    real(8) :: obsTb
4909    logical :: fail
4910
4911    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
4912    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
4913
4914    reportHasMissingTb = .false.
4915    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
4916    allocate(qcRejectLogic(actualNumChannel))
4917    qcRejectLogic(:) = .false.  ! Flag for preliminary QC checks
4918
4919    landQualifierIndice = obs_headElem_i(obsSpaceData, OBS_STYP, headerIndex) 
4920    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
4921    satScanPosition = obs_headElem_i(obsSpaceData, OBS_FOV , headerIndex) 
4922
4923    ! lat/lon
4924    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
4925    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
4926
4927    ! Convert lat/lon to degrees
4928    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
4929    if (obsLon > 180.0d0) obsLon = obsLon - 360.0d0
4930    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
4931
4932    ! terrain type
4933    terrainTypeIndice = obs_headElem_i(obsSpaceData, OBS_TTYP, headerIndex) 
4934    
4935    ! If terrain type is missing, set it to -1 for the QC programs
4936    if (terrainTypeIndice == 99) terrainTypeIndice = mwbg_intMissing
4937
4938    allocate(obsChannels(actualNumChannel))
4939    do channelIndex = 1, actualNumChannel
4940      obsChannels(channelIndex) = channelIndex + tvs_channelOffset(sensorIndex)
4941    end do
4942
4943    ! Global rejection checks
4944
4945    ! Check if number of channels is correct
4946    !if ( actualNumChannel /= mwbg_maxNumChan ) then
4947    !  write(*,*) 'WARNING: Number of channels (',actualNumChannel, ') is not equal to mwbg_maxNumChan (', mwbg_maxNumChan,')'
4948    !  write(*,*) '         All data flagged as bad and returning to calling routine!'
4949    !  qcRejectLogic(:) = .true.  ! flag all data in report as bad
4950    !  return
4951    !end if
4952
4953    ! Check for errors in channel numbers (should be 1-15 for each location ii)
4954    ! For this, tvs_channelOffset(sensorIndex)=0 and total number of channels 
4955    ! in obsSpaceData should be equal to 15 
4956    fail = .false.
4957    if (tvs_channelOffset(sensorIndex) /= 0 .or. &
4958        obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) /= actualNumChannel) then
4959      fail = .true.
4960    end if
4961    if ( fail ) then
4962      write(*,*) 'WARNING: Bad channel number(s) detected!'
4963      write(*,*) '         All data flagged as bad and returning to calling routine!'
4964      write(*,*) '  obsChannels(actualNumChannel) array = ', obsChannels(:)
4965      qcRejectLogic(:) = .true.  ! flag all data in report as bad
4966      return
4967    end if
4968
4969    ! 1) invalid land/sea qualifier or terrain type
4970    !  landQualifierIndice = 0 (land),     1 (sea)
4971    !  terrainTypeIndice = 0 (sea-ice), -1 otherwise
4972    !  calcLandQualifierIndice = 1 (sea, away from land/coast [MG]),      0 otherwise
4973    !  calcTerrainTypeIndice = 0 (over or near analyzed sea-ice [LG]), -1 otherwise
4974
4975    ! Checks on landQualifierIndice,terrainTypeIndice are not done if values are to be replaced in output file.
4976
4977    if ( .not. modLSQ ) then
4978      fail = .false.
4979      if ( landQualifierIndice < 0 .or. landQualifierIndice > 2 ) fail = .true.
4980      if ( terrainTypeIndice < -1 .or. terrainTypeIndice > 1 ) fail = .true.
4981      if ( fail ) then
4982        write(*,*) 'WARNING: Invalid land/sea qualifier or terrain type!'
4983        write(*,*) '  landQualifierIndice, terrainTypeIndice, (lat, lon) = ', &
4984                    landQualifierIndice, terrainTypeIndice, '(',obsLat, obsLon,')'
4985      end if
4986
4987      if ( landQualifierIndice == 0 .and. terrainTypeIndice == 0 ) then
4988        fail = .true.
4989        write(*,*) 'WARNING: Sea ice point (terrainTypeIndice=0) at land point (landQualifierIndice=0)!'
4990        write(*,*) ' lat, lon =  ', obsLat, obsLon
4991      end if
4992      if ( fail ) qcRejectLogic(:) = .true.
4993    end if
4994
4995    fail = .false.
4996    if ( calcLandQualifierIndice < 0 .or. calcLandQualifierIndice > 2 ) fail = .true.
4997    if ( calcTerrainTypeIndice < -1 .or. calcTerrainTypeIndice > 1 ) fail = .true.
4998    if ( fail ) then
4999      write(*,*) 'WARNING: Invalid model-based (MG/LG) land/sea qualifier or terrain type!'
5000      write(*,*) '  calcLandQualifierIndice, calcTerrainTypeIndice, (lat, lon) = ', &
5001                  calcLandQualifierIndice, calcTerrainTypeIndice, '(',obsLat, obsLon,')'
5002    end if
5003    if ( fail ) qcRejectLogic(:) = .true.
5004
5005    ! 2) invalid field of view number
5006    fail = .false.
5007    if ( satScanPosition < 1 .or. satScanPosition > mwbg_maxScanAngle ) then
5008      fail = .true.
5009      write(*,*) 'WARNING: Invalid field of view! satScanPosition, lat, lon = ', &
5010                  satScanPosition, obsLat, obsLon
5011    end if
5012    if ( fail ) qcRejectLogic(:) = .true.
5013
5014    ! 3) satellite zenith angle missing or out of range (> 75 deg)
5015    !  If bad zenith, then set Tb (and zenith) = missing value
5016    fail = .false.
5017    if ( satZenithAngle > 75.0d0 .or. satZenithAngle < 0.0d0 ) then
5018      fail = .true.
5019      write(*,*) 'WARNING: Bad or missing zenith angle! zenith, lat, lon = ', &
5020                  satZenithAngle, obsLat, obsLon
5021      satZenithAngle = mwbg_realMissing
5022      reportHasMissingTb = .true.
5023    end if
5024    if ( fail ) then
5025      qcRejectLogic(:) = .true.
5026      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5027        obsTb = mwbg_realMissing
5028        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
5029      end do
5030    end if
5031
5032    ! 4) Lat,lon check
5033    ! Check for undecoded BURP file integer values of lat,lon = 0,0
5034    ! (usually associated with missing zenith angle and erroneous Tb=330K)
5035
5036    icount = 0
5037    fail = .false.
5038    if ( obsLat == -90.0d0 .and. obsLon == -180.0d0 ) then
5039      fail = .true.
5040      icount =  icount + 1
5041      reportHasMissingTb = .true.
5042    end if
5043    if ( fail ) then
5044      qcRejectLogic(:) = .true.
5045      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5046        obsTb = mwbg_realMissing
5047        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
5048      end do
5049    end if
5050    if ( icount > 0 ) write(*,*) 'WARNING: Bad lat,lon pair(s) detected. Number of locations = ', icount
5051
5052    icount = 0
5053    fail = .false.
5054    if ( abs(obsLat) > 90.0d0 .or. abs(obsLon) > 180.0d0 ) then
5055      fail = .true.
5056      icount =  icount + 1
5057      reportHasMissingTb = .true.
5058    end if
5059    if ( fail ) then
5060      qcRejectLogic(:) = .true.
5061      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5062        obsTb = mwbg_realMissing
5063        call obs_bodySet_r(obsSpaceData, OBS_VAR, bodyIndex, obsTb)
5064      end do
5065    end if
5066    if ( icount > 0 ) write(*,*) 'WARNING: Lat or lon out of range! Number of locations = ', icount
5067
5068    !  5) Change in land/sea qualifier or terrain-type based on MG,LG fields
5069    if ( .not. modLSQ ) then
5070      icount = 0
5071      fail = .false.
5072      if (landQualifierIndice /= calcLandQualifierIndice .or. terrainTypeIndice /= calcTerrainTypeIndice) then
5073        fail = .true.
5074      end if
5075      if ( fail ) then
5076        icount =  icount + 1
5077      end if
5078    end if
5079
5080    call obs_headSet_r(obsSpaceData, OBS_SZA, headerIndex, satZenithAngle)
5081    
5082  end subroutine mwbg_firstQcCheckMwhs2
5083
5084  !--------------------------------------------------------------------------
5085  ! mwbg_nrlFilterAtms
5086  !--------------------------------------------------------------------------
5087  subroutine mwbg_nrlFilterAtms(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, grossrej, &
5088                                si_ecmwf, si_bg, iNumSeaIce, iRej, SeaIce, &
5089                                headerIndex, sensorIndex, obsSpaceData)
5090    !
5091    !:Purpose: Compute the following parameters using 5 ATMS channels:
5092    !            - sea ice,
5093    !            - cloud liquid water from observation (cloudLiquidWaterPathObs),
5094    !            - cloud liquid water from first guess (cloudLiquidWaterPathFG),
5095    !            - 2 scattering indices (si) (ECMWF, Bennartz-Grody)
5096    !          The five channels used are: 23Ghz, 31Ghz, 50Ghz, 89Ghz, and 165Ghz.
5097    !
5098    !          NOTES:
5099    !            - open water points are converted to sea-ice points if sea ice concentration >= 0.55
5100    !              and <calcTerrainTypeIndice> (terrainTypeIndice or terrain type) is changed accordingly
5101    !            - <cloudLiquidWaterPathObs> are missing when out-of-range parameters/Tb detected or grossrej = .true.
5102    !            - <cloudLiquidWaterPathObs> and si only computed over open water away from coasts and sea-ice
5103    !            - <cloudLiquidWaterPathObs> and si = mwbg_realMissing where value cannot be computed.
5104    !          REFERENCES: Ben Ruston, NRL Monterey
5105    !          JCSDA Seminar 12/12/12: Impact of NPP Satellite Assimilation in the U.S. Navy Global Modeling System
5106    !
5107    !          Notes: In the case where an output parameter cannot be calculated, the
5108    !          value of this parameter is set to mwbg_realMissing
5109    !
5110    implicit none
5111
5112    ! Arguments:
5113    integer,          intent(out)   :: iNumSeaIce     ! counter for number open water points with sea-ice detected (from algorithm)
5114    integer,          intent(in)    :: calcLandQualifierIndice ! land/sea indicator (0=land, 1=ocean)
5115    integer,          intent(inout) :: calcTerrainTypeIndice   ! terrain type (0=ice, -1 otherwise)
5116    integer,          intent(out)   :: iRej           ! counter for number locations with bad satZenithAngle, obsLat, calcLandQualifierIndice, or with grossrej=true
5117    logical,          intent(in)    :: grossrej       ! .true. if any channel had a gross error from mwbg_grossValueCheck
5118    logical,          intent(inout) :: waterobs       ! .true. if open water point (away from coasts and sea-ice)
5119    real(8),          intent(out)   :: si_ecmwf       ! ECMWF scattering index from tb89 & tb165
5120    real(8),          intent(out)   :: si_bg          ! Bennartz-Grody scattering index from tb89 & tb165
5121    real(8),          intent(out)   :: SeaIce         ! computed sea-ice fraction from tb23 & tb50
5122    type(struct_obs), intent(inout) :: obsSpaceData   ! obspaceData Object
5123    integer,          intent(in)    :: headerIndex    ! current header Index 
5124    integer,          intent(in)    :: sensorIndex    ! numero de satellite (i.e. indice)
5125
5126    ! Locals:
5127    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
5128    integer :: ier, actualNumChannel
5129    real(8) :: ice, tb23, tb23FG, tb31, tb31FG, tb50, tb89, tb165
5130    real(8) :: bcor23, bcor31, bcor50, bcor89, bcor165
5131    real(8) :: aa, deltb, cosz, t23, t23FG, t31, t31FG, t50, t89, t165
5132    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
5133    real(8) :: obsLat, obsLon, satZenithAngle
5134    real(8), allocatable :: obsTb(:), ompTb(:), obsTbBiasCorr(:)
5135
5136    iNumSeaIce = 0
5137    iRej = 0
5138
5139    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
5140    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
5141    actualNumChannel = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex)
5142    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
5143    if (tvs_coefs(sensorIndex)%coef%fmv_ori_nchn /= actualNumChannel) then
5144      write(*,*) 'mwbg_nrlFilterAtms: tvs_coefs(sensorIndex)%coef%fmv_ori_nchn /= actualNumChannel'
5145    end if
5146
5147    ! lat/lon
5148    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
5149    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
5150
5151    ! Convert lat/lon to degrees
5152    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
5153    if (obsLon > 180.0d0) obsLon = obsLon - 360.0d0
5154    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
5155
5156    if (.not. grossrej) then
5157      allocate(ompTb(actualNumChannel))
5158      allocate(obsTb(actualNumChannel))
5159      allocate(obsTbBiasCorr(actualNumChannel))
5160      ompTb(:) = mwbg_realMissing
5161      obsTb(:) = mwbg_realMissing
5162      obsTbBiasCorr(:) = mwbg_realMissing
5163      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5164        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
5165        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
5166
5167        ompTb(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_OMP, bodyIndex)
5168        obsTb(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
5169        obsTbBiasCorr(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
5170      end do
5171    end if
5172
5173    ! 1) Initialise parameters:
5174    ice = mwbg_realMissing
5175    cloudLiquidWaterPathObs = mwbg_realMissing
5176    cloudLiquidWaterPathFG  = mwbg_realMissing
5177    si_ecmwf = mwbg_realMissing
5178    si_bg = mwbg_realMissing
5179    SeaIce = 0.0d0
5180
5181    tb23    = mwbg_realMissing
5182    tb23FG  = mwbg_realMissing
5183    bcor23  = mwbg_realMissing
5184    tb31    = mwbg_realMissing
5185    tb31FG  = mwbg_realMissing
5186    bcor31  = mwbg_realMissing
5187    tb50    = mwbg_realMissing
5188    bcor50  = mwbg_realMissing
5189    tb89    = mwbg_realMissing
5190    bcor89  = mwbg_realMissing
5191    tb165   = mwbg_realMissing
5192    bcor165 = mwbg_realMissing   
5193
5194    ! 2) Validate input parameters:
5195    if (satZenithAngle < 0.0d0 .or. satZenithAngle > 70.0d0 .or. &
5196        obsLat < -90.0d0 .or. obsLat > 90.0d0 .or. &
5197        calcLandQualifierIndice < 0 .or. calcLandQualifierIndice > 1) then
5198      ier = 1
5199    end if
5200
5201    ! Skip computations for points where all data are rejected  (bad Tb ANY channel)
5202    if ( grossrej ) then
5203      ier = 1
5204    else
5205      ier = 0
5206
5207      ! extract required channels:
5208      !  23 Ghz = AMSU-A 1 = ATMS channel 1
5209      !  31 Ghz = AMSU-A 2 = ATMS channel 2
5210      !  50 Ghz = AMSU-A 3 = ATMS channel 3
5211      !  53 Ghz = AMSU-A 5 = ATMS channel 6
5212      !  89 Ghz = AMSU-A15 = ATMS channel 16
5213      ! 150 Ghz = AMSU-B 2 = ATMS channel 17
5214      tb23    = obsTb(1)
5215      tb23FG  = obsTb(1) - ompTb(1)
5216      bcor23  = obsTbBiasCorr(1)
5217      tb31    = obsTb(2)
5218      tb31FG  = obsTb(2) - ompTb(2)
5219      bcor31  = obsTbBiasCorr(2)
5220      tb50    = obsTb(3)
5221      bcor50  = obsTbBiasCorr(3)
5222      tb89    = obsTb(16)
5223      bcor89  = obsTbBiasCorr(16)
5224      tb165   = obsTb(17)
5225      bcor165 = obsTbBiasCorr(17)
5226    end if
5227
5228    ! 3) Compute parameters:
5229    if ( ier == 0 ) then
5230      cosz   = cosd(satZenithAngle)
5231
5232      if (bcor23 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5233        t23 = tb23
5234      else
5235        t23 = tb23 - bcor23
5236      end if
5237      if (bcor31 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5238        t31 = tb31
5239      else
5240        t31 = tb31 - bcor31
5241      end if
5242      if (bcor50 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5243        t50 = tb50
5244      else
5245        t50 = tb50 - bcor50
5246      end if
5247      if (bcor89 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5248        t89 = tb89
5249      else
5250        t89 = tb89 - bcor89
5251      end if
5252      if (bcor165 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5253        t165 = tb165
5254      else
5255        t165 = tb165 - bcor165
5256      end if
5257
5258      deltb = t89 - t165
5259      t23FG = tb23FG
5260      t31FG = tb31FG
5261
5262      ! Check for sea-ice over water points. Set terrain type to 0 if ice>=0.55 detected.
5263      if ( calcLandQualifierIndice == 1 ) then  ! water point
5264
5265        if ( abs(obsLat) < 50.0d0 ) then
5266          ice = 0.0d0
5267        else
5268          ice = 2.85d0 + 0.020d0 * t23 - 0.028d0 * t50
5269        end if
5270
5271        SeaIce = ice
5272
5273        if ( ice >= 0.55d0 .and. waterobs ) then
5274          iNumSeaIce = iNumSeaIce + 1
5275          waterobs = .false.
5276          calcTerrainTypeIndice = 0
5277        end if
5278
5279      end if
5280
5281      ! Compute cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, and Scattering Indices (over open water only)
5282      if ( waterobs ) then
5283        if ( t23 < 284.0d0 .and. t31 < 284.0d0 ) then
5284          aa = 8.24d0 - (2.622d0 - 1.846d0 * cosz) * cosz
5285          cloudLiquidWaterPathObs = aa + 0.754d0 * dlog(285.0d0 - t23) - 2.265d0 * dlog(285.0d0 - t31)
5286          cloudLiquidWaterPathObs = cloudLiquidWaterPathObs * cosz
5287          if ( cloudLiquidWaterPathObs < 0.0d0 ) cloudLiquidWaterPathObs = 0.0d0
5288
5289          cloudLiquidWaterPathFG = aa + 0.754d0 * dlog(285.0d0 - t23FG) - 2.265d0 * dlog(285.0d0 - t31FG)
5290          cloudLiquidWaterPathFG = cloudLiquidWaterPathFG * cosz
5291          if ( cloudLiquidWaterPathFG < 0.0d0 ) cloudLiquidWaterPathFG = 0.0d0
5292        end if
5293        si_ecmwf = deltb - (-46.94d0 + 0.248d0 * satZenithAngle)
5294        si_bg    = deltb - (-39.201d0 + 0.1104d0 * satZenithAngle)
5295      end if
5296
5297    else  ! ier == 1 case
5298        iRej = iRej + 1
5299
5300    end if ! if ( ier == 0 )
5301
5302    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
5303    call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
5304
5305    if ( mwbg_debug ) then
5306      write(*,*) ' '
5307      write(*,*) ' tb23,tb23FG,tb31,tb31FG,tb50,tb89,tb165,satZenithAngle,obsLat, calcLandQualifierIndice = ', &
5308                 tb23,tb23FG,tb31,tb31FG,tb50,tb89,tb165,satZenithAngle,obsLat, calcLandQualifierIndice
5309      write(*,*) ' ier,ice,cloudLiquidWaterPathObs,cloudLiquidWaterPathFG,si_ecmwf,si_bg,calcTerrainTypeIndice,waterobs =', &
5310                 ier,ice,cloudLiquidWaterPathObs,cloudLiquidWaterPathFG,si_ecmwf,si_bg,calcTerrainTypeIndice,waterobs
5311    end if
5312
5313  end subroutine mwbg_nrlFilterAtms
5314
5315  !--------------------------------------------------------------------------
5316  ! mwbg_nrlFilterMwhs2
5317  !--------------------------------------------------------------------------
5318  subroutine mwbg_nrlFilterMwhs2(calcLandQualifierIndice, calcTerrainTypeIndice, waterobs, grossrej, &
5319                                 si_ecmwf, si_bg, iNumSeaIce, iRej, SeaIce, &
5320                                 headerIndex, sensorIndex, obsSpaceData)
5321    !
5322    !:Purpose: Compute the following parameters using 2 MWHS2 channels:
5323    !            - sea ice,
5324    !            - cloud liquid water from observation (cloudLiquidWaterPathObs),
5325    !            - cloud liquid water from first guess (cloudLiquidWaterPathFG),
5326    !            - 2 scattering indices (si) (ECMWF, Bennartz-Grody)
5327    !          The two channels used are: 89Ghz, and 165Ghz.
5328    !
5329    !          NOTES:
5330    !            - open water points are converted to sea-ice points if sea ice concentration >= 0.55
5331    !              and calcTerrainTypeIndice (terrainTypeIndice or terrain type) is changed accordingly
5332    !            - <cloudLiquidWaterPathObs> are missing when out-of-range parameters/Tb detected or grossrej = .true.
5333    !            - <cloudLiquidWaterPathObs> and <si_ecmwf> only computed over open water away from coasts and sea-ice
5334    !            - <si_bg> is computed for all points
5335    !            - <cloudLiquidWaterPathObs> and si = mwbg_realMissing where value cannot be computed.
5336    !          REFERENCES: Ben Ruston, NRL Monterey
5337    !          JCSDA Seminar 12/12/12: Impact of NPP Satellite Assimilation in the U.S. Navy Global Modeling System
5338    !
5339    !          Notes: In the case where an output parameter cannot be calculated, the
5340    !          value of this parameter is set to mwbg_realMissing
5341    !
5342    implicit none
5343
5344    ! Arguments:
5345    integer,          intent(out)   :: iNumSeaIce   ! running counter for number of open water points with sea-ice detected (from algorithm)
5346    integer,          intent(in)    :: calcLandQualifierIndice ! land/sea indicator (0=land, 1=ocean)
5347    integer,          intent(inout) :: calcTerrainTypeIndice   ! terrain type (0=ice, -1 otherwise)
5348    integer,          intent(out)   :: iRej         ! running counter for number of locations with bad satZenithAngle, obsLat, calcLandQualifierIndice, or with grossrej=true
5349    logical,          intent(in)    :: grossrej     ! .true. if any channel had a gross error from mwbg_grossValueCheck
5350    logical,          intent(inout) :: waterobs     ! .true. if open water point (away from coasts and sea-ice)
5351    real(8),          intent(out)   :: si_ecmwf     ! ECMWF scattering index from tb89 & tb165
5352    real(8),          intent(out)   :: si_bg        ! Bennartz-Grody scattering index from tb89 & tb165
5353    real(8),          intent(out)   :: SeaIce       ! computed sea-ice fraction from tb23 & tb50
5354    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
5355    integer,          intent(in)    :: headerIndex  ! current header Index 
5356    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice)
5357
5358    ! Locals:
5359    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
5360    integer :: ier, actualNumChannel
5361    real(8) :: ice, tb23, tb23FG, tb31, tb31FG, tb50, tb89, tb165
5362    real(8) :: bcor23, bcor31, bcor50, bcor89, bcor165
5363    real(8) :: aa, deltb, cosz, t23, t23FG, t31, t31FG, t50, t89, t165
5364    real(8) :: cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
5365    real(8) :: obsLat, obsLon, satZenithAngle
5366    real(8), allocatable :: obsTb(:), obsTbBiasCorr(:)
5367
5368    iNumSeaIce = 0
5369    iRej = 0
5370
5371    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
5372    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
5373    actualNumChannel = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex)
5374    satZenithAngle = obs_headElem_r(obsSpaceData, OBS_SZA, headerIndex) 
5375
5376    ! lat/lon
5377    obsLat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) 
5378    obsLon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) 
5379
5380    ! Convert lat/lon to degrees
5381    obsLon = obsLon * MPC_DEGREES_PER_RADIAN_R8
5382    if (obsLon > 180.0d0) obsLon = obsLon - 360.0d0
5383    obsLat = obsLat * MPC_DEGREES_PER_RADIAN_R8
5384
5385    if (.not. grossrej) then
5386      allocate(obsTb(actualNumChannel))
5387      allocate(obsTbBiasCorr(actualNumChannel))
5388      obsTb(:) = mwbg_realMissing
5389      obsTbBiasCorr(:) = mwbg_realMissing
5390      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5391        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
5392        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
5393
5394        obsTb(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
5395        obsTbBiasCorr(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
5396      end do
5397    end if
5398
5399    !! extract required channels:  ATMS ch.   MWHS-2 ch.
5400    !!   23 Ghz = AMSU-A 1 =        1          n/a
5401    !!   31 Ghz = AMSU-A 2 =        2          n/a
5402    !!   50 Ghz = AMSU-A 3 =        3          n/a
5403    !!   53 Ghz = AMSU-A 5 =        6          n/a
5404    !!   89 Ghz = AMSU-A 15/ B 1 = 16           1
5405    !!  150 Ghz = AMSU-B 2 =       17          10
5406    !!  183 Ghz = AMSU-B 3 =       22          11
5407    !!  183 Ghz = AMSU-B 5 =       18          15
5408    !
5409    !   Extract Tb for channels 1 (AMSU-B 1) and 10 (AMSU-B 2) for Bennartz SI
5410    !   Extract Tb for channels 22 (AMSU-B 3) and 18 (AMSU-B 5) for Dryness Index (DI)
5411
5412    tb23    = mwbg_realMissing
5413    tb23FG  = mwbg_realMissing
5414    bcor23  = mwbg_realMissing
5415    tb31    = mwbg_realMissing
5416    tb31FG  = mwbg_realMissing
5417    bcor31  = mwbg_realMissing
5418    tb50    = mwbg_realMissing
5419    bcor50  = mwbg_realMissing
5420    tb89    = mwbg_realMissing
5421    bcor89  = mwbg_realMissing
5422    tb165   = mwbg_realMissing
5423    bcor165 = mwbg_realMissing   
5424
5425    ! 1) Initialise parameters:
5426    ice      = mwbg_realMissing
5427    cloudLiquidWaterPathObs = mwbg_realMissing
5428    cloudLiquidWaterPathFG  = mwbg_realMissing
5429    si_ecmwf = mwbg_realMissing
5430    si_bg    = mwbg_realMissing
5431    SeaIce   = 0.0d0
5432
5433    ! 2) Validate input parameters:
5434    if (satZenithAngle < 0.0d0 .or. satZenithAngle > 70.0d0 .or. &
5435        obsLat < -90.0d0 .or. obsLat > 90.0d0 .or. &
5436        calcLandQualifierIndice < 0 .or. calcLandQualifierIndice > 1) then
5437      ier = 1
5438    end if
5439
5440    ! Skip computations for points where all data are rejected  (bad Tb ANY channel)
5441    if ( grossrej ) then
5442      ier = 1
5443    else
5444      ier = 0
5445
5446      tb89    = obsTb(1)
5447      bcor89  = obsTbBiasCorr(1)
5448      tb165   = obsTb(10)
5449      bcor165 = obsTbBiasCorr(10)
5450    end if
5451
5452    ! 3) Compute parameters:
5453    if ( ier == 0 ) then
5454      cosz = cosd(satZenithAngle)
5455
5456      if (bcor23 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5457        t23 = tb23
5458      else
5459        t23 = tb23 - bcor23
5460      end if
5461      if (bcor31 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5462        t31 = tb31
5463      else
5464        t31 = tb31 - bcor31
5465      end if
5466      if (bcor50 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5467        t50 = tb50
5468      else
5469        t50 = tb50 - bcor50
5470      end if
5471      if (bcor89 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5472        t89 = tb89
5473      else
5474        t89 = tb89 - bcor89
5475      end if
5476      if (bcor165 == mwbg_realMissing .or. mwbg_useUnbiasedObsForClw) then
5477        t165 = tb165
5478      else
5479        t165 = tb165 - bcor165
5480      end if
5481
5482      deltb = t89 - t165
5483      t23FG = tb23FG
5484      t31FG = tb31FG
5485
5486      ! Check for sea-ice over water points. Set terrain type to 0 if ice>=0.55 detected.
5487      if ( calcLandQualifierIndice == 1 .and. t23 /= mwbg_realMissing ) then  ! water point
5488
5489        if ( abs(obsLat) < 50.0d0 ) then
5490          ice = 0.0d0
5491        else
5492          ice = 2.85d0 + 0.020d0 * t23 - 0.028d0 * t50
5493        end if
5494
5495        SeaIce = ice
5496        if ( ice >= 0.55d0 .and. waterobs ) then
5497          iNumSeaIce = iNumSeaIce + 1
5498          waterobs = .false.
5499          calcTerrainTypeIndice = 0
5500        end if
5501
5502      end if
5503
5504      ! Compute cloudLiquidWaterPathObs, cloudLiquidWaterPathFG, and Scattering Indices (over open water only)
5505      if ( waterobs ) then
5506        if ( t23 /= mwbg_realMissing ) then
5507          if ( t23 < 284.0d0 .and. t31 < 284.0d0 ) then
5508            aa = 8.24d0 - (2.622d0 - 1.846d0 * cosz) * cosz
5509            cloudLiquidWaterPathObs = aa + 0.754d0 * dlog(285.0d0 - t23) - 2.265d0 * dlog(285.0d0 - t31)
5510            cloudLiquidWaterPathObs = cloudLiquidWaterPathObs * cosz
5511            if ( cloudLiquidWaterPathObs < 0.0d0 ) cloudLiquidWaterPathObs = 0.0d0
5512
5513            cloudLiquidWaterPathFG = aa + 0.754d0 * dlog(285.0d0 - t23FG) - 2.265d0 * dlog(285.0d0 - t31FG)
5514            cloudLiquidWaterPathFG = cloudLiquidWaterPathFG * cosz
5515            if ( cloudLiquidWaterPathFG < 0.0d0 ) cloudLiquidWaterPathFG = 0.0d0
5516          end if
5517        end if
5518        si_ecmwf = deltb - (-46.94d0 + 0.248d0 * satZenithAngle)
5519        si_bg    = deltb - (-39.201d0 + 0.1104d0 * satZenithAngle)
5520      else
5521        si_bg    = deltb - (0.158d0 + 0.0163d0 * satZenithAngle)
5522      end if
5523
5524    else  ! ier == 1 case
5525        iRej = iRej + 1
5526
5527    end if ! if ( ier == 0 )
5528
5529    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
5530    call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
5531
5532    if ( mwbg_debug ) then
5533      write(*,*) ' '
5534      write(*,*) ' tb23,tb23FG,tb31,tb31FG,tb50,tb89,tb165,satZenithAngle,obsLat, calcLandQualifierIndice = ', &
5535                 tb23,tb23FG,tb31,tb31FG,tb50,tb89,tb165,satZenithAngle,obsLat, calcLandQualifierIndice
5536      write(*,*) ' ier,ice,cloudLiquidWaterPathObs,cloudLiquidWaterPathFG,si_ecmwf,si_bg,calcTerrainTypeIndice,waterobs =', &
5537                 ier,ice,cloudLiquidWaterPathObs,cloudLiquidWaterPathFG,si_ecmwf,si_bg,calcTerrainTypeIndice,waterobs
5538    end if
5539
5540  end subroutine mwbg_nrlFilterMwhs2
5541
5542  !--------------------------------------------------------------------------
5543  ! mwbg_flagDataUsingNrlCritAtms
5544  !--------------------------------------------------------------------------
5545  subroutine mwbg_flagDataUsingNrlCritAtms(scatec, scatbg, SeaIce, grossrej, waterobs, useUnbiasedObsForClw, &
5546                                           iwvreject, cloudobs, precipobs,  cldcnt, riwv, zdi, &
5547                                           headerIndex, sensorIndex, obsSpaceData)
5548    ! 
5549    !:Purpose: Set the Information flag (newInformationFlag) values (new BURP element 025174 in header).
5550    !          BIT Meaning:
5551    !            -  0     off=land or sea-ice, on=open water away from coast
5552    !            -  1     Mean 183 Ghz [ch. 18-22] is missing
5553    !            -  2     CLW is missing (over water)
5554    !            -  3     CLW > clw_atms_nrl_LTrej (0.175 kg/m2) (cloudobs)
5555    !            -  4     scatec/scatbg > Lower Troposphere limit 9/10 (precipobs)
5556    !            -  5     Mean 183 Ghz [ch. 18-22] Tb < 240K
5557    !            -  6     CLW > clw_atms_nrl_UTrej (0.200 kg/m2)
5558    !            -  7     Dryness Index rejection (for ch. 22)
5559    !            -  8     scatec/scatbg > Upper Troposphere limit 18/15
5560    !            -  9     Dryness Index rejection (for ch. 21)
5561    !            - 10     Sea ice > 0.55 detected
5562    !            - 11     Gross error in Tb (any chan.) (all channels rejected)
5563    !
5564    implicit none
5565
5566    ! Arguments:
5567    real(8),          intent(in)    :: scatec       ! ECMWF scattering index from tb89 & tb165
5568    real(8),          intent(in)    :: scatbg       ! Bennartz-Grody scattering index from tb89 & tb165
5569    real(8),          intent(in)    :: SeaIce       ! computed sea-ice fraction from tb23 & tb50
5570    logical,          intent(in)    :: useUnbiasedObsForClw  ! use unbiased Tb for CLW calculation
5571    logical,          intent(in)    :: grossrej     ! .true. if any channel had a gross error from mwbg_grossValueCheck
5572    logical,          intent(in)    :: waterobs     ! if obs over open-water
5573    integer,          intent(inout) :: cldcnt       ! Number of water point covered by cloud
5574    logical,          intent(out)   :: cloudobs     ! .true. if CLW > clw_atms_nrl_LTrej (0.175) or precipobs
5575    logical,          intent(out)   :: iwvreject    ! .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
5576    logical,          intent(out)   :: precipobs    ! .true. if precip. detected through NRL scattering indices
5577    real(8),          intent(out)   :: zdi          ! simple AMSU-B Dryness Index Tb(ch.3)-Tb(ch.5)
5578    real(8),          intent(out)   :: riwv         ! Mean 183 Ghz [ch. 18-22] Tb
5579    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
5580    integer,          intent(in)    :: headerIndex  ! current header Index 
5581    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice)
5582
5583    ! Locals:
5584    integer :: indx, n_cld, newInformationFlag, actualNumChannel
5585    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
5586    real(8) :: ztb_amsub3, bcor_amsub3, ztb_amsub5, bcor_amsub5, ztb183(5)
5587    real(8) :: cloudLiquidWaterPathObs
5588    real(8), allocatable :: obsTb(:), obsTbBiasCorr(:)
5589
5590    ! To begin, assume that all obs are good.
5591    newInformationFlag = 0
5592    n_cld = 0
5593    cloudobs  = .false.
5594    iwvreject = .false.
5595    precipobs = .false.
5596
5597    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
5598    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
5599    actualNumChannel = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex)
5600    if (tvs_coefs(sensorIndex)%coef%fmv_ori_nchn /= actualNumChannel) then
5601      write(*,*) 'mwbg_flagDataUsingNrlCritAtms: tvs_coefs(sensorIndex)%coef%fmv_ori_nchn /= actualNumChannel'
5602    end if
5603
5604    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
5605
5606    if (.not. grossrej) then
5607      allocate(obsTb(actualNumChannel))
5608      allocate(obsTbBiasCorr(actualNumChannel))
5609      obsTb(:) = mwbg_realMissing
5610      obsTbBiasCorr(:) = mwbg_realMissing
5611      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5612        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
5613        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
5614
5615        obsTb(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
5616        obsTbBiasCorr(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
5617      end do
5618
5619      ! Extract Tb for channels 16 (AMSU-B 1) and 17 (AMSU-B 2) for Bennartz SI
5620      ! Extract Tb for channels 22 (AMSU-B 3) and 18 (AMSU-B 5) for Dryness Index (DI)      
5621      ztb_amsub3 = obsTb(22)
5622      bcor_amsub3 = obsTbBiasCorr(22)
5623      ztb_amsub5 = obsTb(18)
5624      bcor_amsub5 = obsTbBiasCorr(18)
5625    end if
5626
5627    ! Flag data using NRL criteria
5628
5629    ! Compute Mean 183 Ghz [ch. 18-22] Tb (riwv)
5630    riwv = mwbg_realMissing
5631    if (.not. grossrej) then
5632      do indx = 1, 5
5633        if (obsTbBiasCorr(indx+10) == mwbg_realMissing .or. useUnbiasedObsForClw) then
5634          ztb183(indx) = obsTb(indx+17)
5635        else
5636          ztb183(indx) = obsTb(indx+17) - obsTbBiasCorr(indx+17)
5637        end if
5638      end do
5639      riwv  = sum(ztb183) / 5.0d0
5640      if ( riwv < mean_Tb_183Ghz_min ) iwvreject = .true.
5641    else
5642      iwvreject = .true.
5643    end if
5644
5645    !  Set bits in newInformationFlag flag to identify where various data selection criteria are met
5646    !     precipobs = .true  where ECMWF or BG scattering index > min_threshold (LT)
5647    !     cloudobs  = .true. where CLW > min_threshold (LT) or if precipobs = .true
5648
5649    if ( grossrej ) newInformationFlag = IBSET(newInformationFlag,11)
5650    if ( scatec > scatec_atms_nrl_LTrej .or. scatbg > scatbg_atms_nrl_LTrej ) precipobs = .true.
5651    if (cloudLiquidWaterPathObs > clw_atms_nrl_LTrej) n_cld = 1
5652    cldcnt  = cldcnt  + n_cld
5653    if ( (cloudLiquidWaterPathObs > clw_atms_nrl_LTrej) .or. precipobs ) cloudobs = .true.
5654    if ( waterobs )  newInformationFlag = IBSET(newInformationFlag,0)
5655    if ( iwvreject ) newInformationFlag = IBSET(newInformationFlag,5)
5656    if ( precipobs ) newInformationFlag = IBSET(newInformationFlag,4)
5657    if ( cloudLiquidWaterPathObs > clw_atms_nrl_LTrej) newInformationFlag = IBSET(newInformationFlag,3)
5658    if ( cloudLiquidWaterPathObs > clw_atms_nrl_UTrej) newInformationFlag = IBSET(newInformationFlag,6)
5659    if ( scatec > scatec_atms_nrl_UTrej .or. scatbg > scatbg_atms_nrl_UTrej ) newInformationFlag = IBSET(newInformationFlag,8)
5660    if ( SeaIce >= 0.55d0 ) newInformationFlag = IBSET(newInformationFlag,10)
5661
5662    if (waterobs .and. cloudLiquidWaterPathObs == mwbg_realMissing) then
5663      newInformationFlag = IBSET(newInformationFlag,2)
5664    end if
5665    if (riwv == mwbg_realMissing) newInformationFlag = IBSET(newInformationFlag,1)
5666
5667    ! Compute the simple AMSU-B Dryness Index zdi for all points = Tb(ch.3)-Tb(ch.5)
5668    if ( useUnbiasedObsForClw ) then
5669      if (.not. grossrej) then
5670        zdi = ztb_amsub3 - ztb_amsub5
5671      else
5672        zdi = mwbg_realMissing
5673      end if
5674    else
5675      if (.not. grossrej) then
5676        zdi = (ztb_amsub3 - bcor_amsub3) - (ztb_amsub5 - bcor_amsub5)
5677      else
5678        zdi = mwbg_realMissing
5679      end if
5680    end if
5681
5682    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
5683
5684  end subroutine mwbg_flagDataUsingNrlCritAtms
5685
5686  !--------------------------------------------------------------------------
5687  ! mwbg_flagDataUsingNrlCritMwhs2
5688  !--------------------------------------------------------------------------
5689  subroutine mwbg_flagDataUsingNrlCritMwhs2(scatec, SeaIce, grossrej, waterobs, useUnbiasedObsForClw, &
5690                                            iwvreject, cloudobs, precipobs,  cldcnt, riwv, zdi, &
5691                                            headerIndex, sensorIndex, obsSpaceData)
5692    !
5693    !:Purpose: Set the  Information flag (newInformationFlag) values (new BURP element 025174 in header).
5694    !          BIT Meaning:
5695    !            - 0     off=land or sea-ice, on=open water away from coast
5696    !            - 1     Mean 183 Ghz [ch. 18-22] is missing
5697    !            - 2     CLW is missing (over water)
5698    !            - 3     CLW > clw_mwhs2_nrl_LTrej (0.175 kg/m2) (cloudobs)
5699    !            - 4     scatec > Lower Troposphere limit 9/10 (precipobs)
5700    !            - 5     Mean 183 Ghz [ch. 18-22] Tb < 240K
5701    !            - 6     CLW > clw_mwhs2_nrl_UTrej (0.200 kg/m2)
5702    !            - 10    Sea ice > 0.55 detected
5703    !            - 11    Gross error in Tb (any chan.)  (all channels rejected)
5704    !
5705    implicit none
5706
5707    ! Arguments:
5708    real(8),          intent(in)    :: scatec               ! ECMWF scattering index from tb89 & tb165
5709    real(8),          intent(in)    :: SeaIce               ! computed sea-ice fraction from tb23 & tb50
5710    logical,          intent(in)    :: useUnbiasedObsForClw ! use unbiased Tb for CLW calculation
5711    logical,          intent(in)    :: grossrej             ! .true. if any channel had a gross error from mwbg_grossValueCheck
5712    logical,          intent(in)    :: waterobs             ! if obs over open-water
5713    integer,          intent(inout) :: cldcnt               ! Number of water point covered by cloud
5714    logical,          intent(out)   :: cloudobs             ! .true. if CLW > clw_atms_nrl_LTrej (0.175) or precipobs
5715    logical,          intent(out)   :: iwvreject            ! .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
5716    logical,          intent(out)   :: precipobs            ! .true. if precip. detected through NRL scattering indices
5717    real(8),          intent(out)   :: zdi                  ! simple AMSU-B Dryness Index Tb(ch.3)-Tb(ch.5)
5718    real(8),          intent(out)   :: riwv                 ! Mean 183 Ghz [ch. 18-22] Tb
5719    type(struct_obs), intent(inout) :: obsSpaceData         ! obspaceData Object
5720    integer,          intent(in)    :: headerIndex          ! current header Index 
5721    integer,          intent(in)    :: sensorIndex          ! numero de satellite (i.e. indice)
5722
5723    ! Locals:
5724    integer :: indx, n_cld, newInformationFlag, actualNumChannel
5725    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
5726    real(8) :: ztb_amsub3, bcor_amsub3, ztb_amsub5, bcor_amsub5,  ztb183(5)
5727    real(8) :: cloudLiquidWaterPathObs
5728    real(8), allocatable :: obsTb(:), obsTbBiasCorr(:)
5729
5730    ! To begin, assume that all obs are good.
5731    newInformationFlag = 0
5732    n_cld = 0
5733    cloudobs  = .false.
5734    iwvreject = .false.
5735    precipobs = .false.
5736
5737    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
5738    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
5739    actualNumChannel = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex)
5740    
5741    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
5742
5743    if (.not. grossrej) then
5744      allocate(obsTb(actualNumChannel))
5745      allocate(obsTbBiasCorr(actualNumChannel))
5746      obsTb(:) = mwbg_realMissing
5747      obsTbBiasCorr(:) = mwbg_realMissing
5748      do bodyIndex = bodyIndexBeg, bodyIndexEnd
5749        obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
5750        obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
5751
5752        obsTb(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
5753        obsTbBiasCorr(obsChanNum) = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex)
5754      end do
5755
5756      ! Extract Tb for channels 1 (AMSU-B 1) and 10 (AMSU-B 2) for Bennartz SI
5757      ! Extract Tb for channels 11 (AMSU-B 3) and 15 (AMSU-B 5) for Dryness Index (DI)
5758      ztb_amsub3 = obsTb(11)
5759      bcor_amsub3 = obsTbBiasCorr(11)
5760      ztb_amsub5 = obsTb(15)
5761      bcor_amsub5 = obsTbBiasCorr(15)
5762    end if
5763
5764    ! Flag data using NRL criteria
5765
5766    ! Compute Mean 183 Ghz [ch. 11-15] Tb (riwv)
5767    riwv = mwbg_realMissing
5768    if (.not. grossrej) then
5769      do indx = 1, 5
5770        if (obsTbBiasCorr(indx+10) == mwbg_realMissing .or. useUnbiasedObsForClw) then
5771          ztb183(indx) = obsTb(indx+10)
5772        else
5773          ztb183(indx) = obsTb(indx+10) - obsTbBiasCorr(indx+10)
5774        end if
5775      end do
5776      riwv  = sum(ztb183) / 5.0d0
5777      if ( riwv < mean_Tb_183Ghz_min ) iwvreject = .true.
5778    else
5779      iwvreject = .true.
5780    end if
5781
5782    !  Set bits in newInformationFlag flag to identify where various data selection criteria are met
5783    !     precipobs = .true  where ECMWF or BG scattering index > min_threshold (LT)
5784    !     cloudobs  = .true. where CLW > min_threshold (LT) or if precipobs = .true
5785
5786    if ( grossrej ) newInformationFlag = IBSET(newInformationFlag,11)
5787    if ( scatec > scatec_mwhs2_nrl_LTrej ) precipobs = .true.
5788    if (cloudLiquidWaterPathObs > clw_mwhs2_nrl_LTrej) n_cld = 1
5789    cldcnt  = cldcnt  + n_cld
5790    if ( (cloudLiquidWaterPathObs > clw_mwhs2_nrl_LTrej) .or. precipobs ) cloudobs = .true.
5791    if ( waterobs )  newInformationFlag = IBSET(newInformationFlag,0)
5792    if ( iwvreject ) newInformationFlag = IBSET(newInformationFlag,5)
5793    if ( precipobs ) newInformationFlag = IBSET(newInformationFlag,4)
5794    if ( cloudLiquidWaterPathObs > clw_mwhs2_nrl_LTrej) newInformationFlag = IBSET(newInformationFlag,3)
5795    if ( cloudLiquidWaterPathObs > clw_mwhs2_nrl_UTrej) newInformationFlag = IBSET(newInformationFlag,6)
5796    if ( SeaIce >= 0.55d0 ) newInformationFlag = IBSET(newInformationFlag,10)
5797
5798    if (waterobs .and. cloudLiquidWaterPathObs == mwbg_realMissing) then
5799      newInformationFlag = IBSET(newInformationFlag,2)
5800    end if
5801    if (riwv == mwbg_realMissing) newInformationFlag = IBSET(newInformationFlag,1)
5802
5803    ! Compute the simple AMSU-B Dryness Index zdi for all points = Tb(ch.3)-Tb(ch.5)
5804    if ( useUnbiasedObsForClw ) then
5805      if ( .not. grossrej ) then
5806        zdi = ztb_amsub3 - ztb_amsub5
5807      else
5808        zdi = mwbg_realMissing
5809      end if
5810    else
5811      if ( .not. grossrej ) then
5812        zdi = (ztb_amsub3 - bcor_amsub3) - (ztb_amsub5 - bcor_amsub5)
5813      else
5814        zdi = mwbg_realMissing
5815      end if
5816    end if
5817
5818    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
5819
5820  end subroutine mwbg_flagDataUsingNrlCritMwhs2
5821
5822  !--------------------------------------------------------------------------
5823  ! mwbg_reviewAllCritforFinalFlagsAtms
5824  !--------------------------------------------------------------------------
5825  subroutine mwbg_reviewAllCritforFinalFlagsAtms(qcRejectLogic, grossrej, waterobs, &
5826                                                 precipobs, scatec, scatbg, &
5827                                                 iwvreject, riwv, &
5828                                                 zdi, drycnt, landcnt, &
5829                                                 rejcnt, iwvcnt, pcpcnt, flgcnt, &
5830                                                 chanIgnoreInAllskyGenCoeff, &
5831                                                 headerIndex, sensorIndex, obsSpaceData)
5832    !
5833    !:Purpose: Review all the checks previously made to determine which obs are to be accepted
5834    !          for assimilation and which are to be flagged for exclusion (lflagchn).
5835    !            - <grossrej>  = .true. if any channel had a gross error at the point
5836    !            - <cloudobs>  = .true. if CLW > clw_atms_nrl_LTrej (0.175) or precipobs
5837    !            - <precipobs> = .true. if precip. detected through NRL scattering indices
5838    !            - <waterobs>  = .true. if open water point
5839    !            - <iwvreject> = .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
5840    !
5841    implicit none
5842
5843    ! Arguments:
5844    logical,          intent(in)    :: qcRejectLogic(:)              ! .true. if channel is rejected
5845    real(8),          intent(in)    :: scatbg    ! Bennartz-Grody scattering index from tb89 & tb165
5846    real(8),          intent(in)    :: scatec    ! ECMWF scattering index from tb89 & tb165
5847    logical,          intent(in)    :: grossrej  ! .true. if any channel had a gross error from mwbg_grossValueCheck
5848    logical,          intent(in)    :: waterobs  ! if obs over open-water
5849    logical,          intent(in)    :: iwvreject ! .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
5850    logical,          intent(in)    :: precipobs ! .true. if precip. detected through NRL scattering indices
5851    real(8),          intent(in)    :: zdi       ! simple AMSU-B Dryness Index Tb(ch.3)-Tb(ch.5)
5852    real(8),          intent(in)    :: riwv      ! Mean 183 Ghz [ch. 18-22] Tb
5853    integer,          intent(inout) :: landcnt   ! Number of obs pts found over land/ice
5854    integer,          intent(inout) :: drycnt    ! Number of pts flagged for AMSU-B Dryness Index
5855    integer,          intent(inout) :: rejcnt    ! Number of rejected obs (Tb err, QCfail)
5856    integer,          intent(inout) :: iwvcnt    ! Number of pts with Mean 183 Ghz Tb < 240K
5857    integer,          intent(inout) :: pcpcnt    ! Number of scatter/precip obs
5858    integer,          intent(inout) :: flgcnt    ! Total number of filtered obs
5859    integer,          intent(in)    :: chanIgnoreInAllskyGenCoeff(:) ! Channels excluded from gen_bias_corr in all-sky mode
5860    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
5861    integer,          intent(in)    :: headerIndex  ! current header Index 
5862    integer,          intent(in)    :: sensorIndex  ! numero de satellite (i.e. indice) 
5863
5864    ! Locals:
5865    integer :: INDXCAN, codtyp, obsGlobalMarker, newInformationFlag, actualNumChannel
5866    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
5867    integer :: obsFlags
5868    real(8) :: clwObsFGaveraged, cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
5869    real(8) :: scatIndexOverWaterObs, scatIndexOverWaterFG
5870    logical, allocatable :: lflagchn(:)
5871
5872    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
5873    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
5874    newInformationFlag = obs_headElem_i(obsSpaceData, OBS_INFG, headerIndex)
5875    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
5876
5877    ! Allocation
5878    allocate(lflagchn(actualNumChannel))
5879
5880    lflagchn(:) = qcRejectLogic(:)  ! initialize with flags set in mwbg_firstQcCheckAtms
5881    ! Reject all channels if gross Tb error detected in any channel or other problems
5882    if ( grossrej ) then
5883      lflagchn(:) = .true.
5884    else
5885
5886      ! OVER LAND OR SEA-ICE,
5887      !    -- CLW/SI not determined over land
5888      !    -- surface emissivity effects lower tropospheric and window channels
5889      !    -- reject window & lower tropospheric channels 1-6, 16-19
5890      !    -- reject ch. 20-22 if iwvreject = .true.  [ Mean 183 Ghz [ch. 18-22] Tb < 240K ]
5891      !    -- check DI for AMSU-B like channels
5892
5893      if  ( .not. waterobs ) then
5894        lflagchn(1:mwbg_atmsNumSfcSensitiveChannel) = .true.      ! AMSU-A 1-6
5895        lflagchn(16:19) = .true.                                  ! AMSU-B (like 1,2,5)
5896        if ( iwvreject ) lflagchn(20:22) = .true.                 ! AMSU-B (like 4,3)
5897
5898        ! Dryness index (for AMSU-B channels 19-22 assimilated over land/sea-ice)
5899        ! Channel AMSUB-3 (ATMS channel 22) is rejected for a dryness index >    0.
5900        !                (ATMS channel 21) is rejected for a dryness index >   -5.
5901        ! Channel AMSUB-4 (ATMS channel 20) is rejected for a dryness index >   -8.
5902        if ( zdi > 0.0d0 ) then
5903          lflagchn(22) = .true.
5904          newInformationFlag = IBSET(newInformationFlag,7)
5905        end if
5906        if ( zdi > -5.0d0 ) then
5907          lflagchn(21) = .true.
5908          newInformationFlag = IBSET(newInformationFlag,9)
5909          drycnt = drycnt + 1
5910        end if
5911        if ( zdi > -8.0d0 ) then
5912          lflagchn(20) = .true.
5913        end if
5914
5915      else  ! if waterobs
5916
5917      ! OVER WATER,
5918      !    in clear-sky mode:
5919      !    -- reject ch. 5-6, if CLW > clw_atms_nrl_LTrej or CLW = mwbg_realMissing
5920      !    in all-sky mode:
5921      !    -- reject ch. 5-6, if CLW > mwbg_clwQcThreshold or CLW = mwbg_realMissing
5922      !
5923      !    -- reject ch. 1-4, if CLW > clw_atms_nrl_LTrej or CLW = mwbg_realMissing
5924      !    -- reject ch. 16-20 if CLW > clw_atms_nrl_LTrej or CLW = mwbg_realMissing
5925      !    -- reject ch. 7-9, 21-22 if CLW > clw_atms_nrl_UTrej or CLW = mwbg_realMissing
5926      !    -- reject ch. 1-6, 16-22 if scatec > 9  or scatec = mwbg_realMissing
5927      !    -- reject ch. 7-9        if scatec > 18 or scatec = mwbg_realMissing
5928      !    -- reject ch. 1-6        if scatbg > 10 or scatbg = mwbg_realMissing
5929      !    -- reject ch. 7-9        if scatbg > 15 or scatbg = mwbg_realMissing
5930      !    -- reject ch. 16-22      if iwvreject = .true.   [ Mean 183 Ghz [ch. 18-22] Tb < 240K ]
5931
5932        if ( cloudLiquidWaterPathObs > clw_atms_nrl_LTrej )  then
5933          if ( tvs_mwAllskyAssim ) then
5934            lflagchn(1:4) = .true.
5935            clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
5936            if ( clwObsFGaveraged > mwbg_clwQcThreshold ) lflagchn(5:6) = .true.
5937          else
5938            lflagchn(1:mwbg_atmsNumSfcSensitiveChannel) = .true.
5939          end if
5940          lflagchn(16:20) = .true.
5941        end if
5942        if ( cloudLiquidWaterPathObs > clw_atms_nrl_UTrej )  then
5943          lflagchn(7:9)   = .true.
5944          lflagchn(21:22) = .true.
5945        end if
5946        if ( scatec >  scatec_atms_nrl_LTrej ) then
5947          lflagchn(1:mwbg_atmsNumSfcSensitiveChannel) = .true.
5948          lflagchn(16:22) = .true.
5949        end if
5950        if ( scatec > scatec_atms_nrl_UTrej ) lflagchn(7:9) = .true.
5951        if ( scatbg > scatbg_atms_nrl_LTrej ) lflagchn(1:mwbg_atmsNumSfcSensitiveChannel) = .true.
5952        if ( scatbg > scatbg_atms_nrl_UTrej ) lflagchn(7:9) = .true.
5953        if ( iwvreject ) lflagchn(16:22) = .true.
5954        if ( cloudLiquidWaterPathObs == mwbg_realMissing ) then
5955          newInformationFlag = IBSET(newInformationFlag,2)
5956          lflagchn(1:9)   = .true.
5957          lflagchn(16:22) = .true.
5958        end if
5959        if ( riwv == mwbg_realMissing ) then     ! riwv = mean_Tb_183Ghz
5960          newInformationFlag = IBSET(newInformationFlag,1)
5961          lflagchn(16:22) = .true.
5962        end if
5963      end if  ! if waterobs
5964
5965    end if  ! if .not. grossrej
5966
5967    if ( .not. waterobs ) landcnt  = landcnt  + 1
5968    if ( grossrej )  rejcnt = rejcnt + 1
5969    if ( iwvreject)  iwvcnt = iwvcnt + 1
5970    if ( precipobs .and. waterobs ) then
5971      pcpcnt = pcpcnt + 1
5972    end if
5973
5974    if ( ANY(lflagchn(:)) ) flgcnt = flgcnt + 1
5975
5976    ! RESET scatIndexOverWaterObs array to ECMWF scattering index for output to BURP file
5977    scatIndexOverWaterObs = scatec
5978    ! Set missing cloudLiquidWaterPathFG and scatIndexOverWaterFG to BURP missing value (mwbg_realMissing)
5979    if (cloudLiquidWaterPathObs == mwbg_realMissing) cloudLiquidWaterPathFG = mwbg_realMissing
5980    scatIndexOverWaterFG = mwbg_realMissing
5981
5982    ! Modify data flag values (set bit 7) for rejected data
5983    ! In all-sky mode, turn on bit=23 for channels in chanIgnoreInAllskyGenCoeff(:)
5984    ! as cloud-affected radiances over sea when there is mismatch between 
5985    ! cloudLiquidWaterPathObs and cloudLiquidWaterPathFG (to be used in gen_bias_corr)
5986    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
5987    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
5988    clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
5989    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
5990      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
5991      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
5992      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
5993
5994      if (lflagchn(obsChanNum)) obsFlags = IBSET(obsFlags,7)
5995
5996      INDXCAN = utl_findloc(chanIgnoreInAllskyGenCoeff(:),obsChanNumWithOffset)
5997      if (tvs_mwAllskyAssim .and. waterobs .and. INDXCAN /= 0 .and. &
5998          (clwObsFGaveraged > mwbg_cloudyClwThresholdBcorr .or. &
5999           cloudLiquidWaterPathObs == mwbg_realMissing .or. &
6000           cloudLiquidWaterPathFG == mwbg_realMissing)) then
6001        obsFlags = IBSET(obsFlags,23)
6002      end if
6003
6004      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
6005    end do BODY
6006
6007    ! Set bit 6 in 24-bit global flags if any data rejected
6008    if ( ANY(lflagchn(:)) ) then
6009      obsGlobalMarker = obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex)
6010      obsGlobalMarker = IBSET(obsGlobalMarker,6)
6011      call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex, obsGlobalMarker)
6012    end if
6013
6014    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
6015    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
6016    call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
6017    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
6018
6019    if (scatIndexOverWaterObs /= mwbg_realMissing) then
6020      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, scatIndexOverWaterObs)
6021    else
6022      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, MPC_missingValue_R8)
6023    end if
6024
6025    if (tvs_isInstrumAllskyHuAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
6026      if (scatIndexOverWaterFG /= mwbg_realMissing) then
6027        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, scatIndexOverWaterFG)
6028      else
6029        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, MPC_missingValue_R8)
6030      end if
6031    end if
6032
6033  end subroutine mwbg_reviewAllCritforFinalFlagsAtms
6034
6035  !--------------------------------------------------------------------------
6036  ! mwbg_reviewAllCritforFinalFlagsMwhs2
6037  !--------------------------------------------------------------------------
6038  subroutine mwbg_reviewAllCritforFinalFlagsMwhs2(qcRejectLogic, grossrej, calcTerrainTypeIndice, waterobs, &
6039                                                  precipobs, scatec, scatbg, &
6040                                                  iwvreject, riwv, &
6041                                                  zdi, allcnt, drycnt, landcnt, &
6042                                                  rejcnt, iwvcnt, pcpcnt, flgcnt, &
6043                                                  chanIgnoreInAllskyGenCoeff, &
6044                                                  headerIndex, sensorIndex, obsSpaceData)
6045    !
6046    !:Purpose: Review all the checks previously made to determine which obs are to be accepted
6047    !          for assimilation and which are to be flagged for exclusion (lflagchn).
6048    !            - <grossrej>  = .true. if any channel had a gross error at the point
6049    !            - <cloudobs>  = .true. if CLW > clw_mwhs2_nrl_LTrej (0.175) or precipobs
6050    !            - <precipobs> = .true. if precip. detected through NRL scattering indices
6051    !            - <waterobs>  = .true. if open water point
6052    !            - <iwvreject> = .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
6053    !
6054    implicit none
6055
6056    ! Arguments:
6057    logical,          intent(in)    :: qcRejectLogic(:) ! .true. if channel is rejected
6058    real(8),          intent(in)    :: scatec           ! ECMWF scattering index from tb89 & tb165
6059    real(8),          intent(in)    :: scatbg           ! Bennartz-Grody scattering index from tb89 & tb165
6060    logical,          intent(in)    :: grossrej         ! .true. if any channel had a gross error from mwbg_grossValueCheck
6061    logical,          intent(in)    :: waterobs         ! if obs over open-water
6062    logical,          intent(in)    :: iwvreject        ! .true. if Mean 183 Ghz [ch. 18-22] Tb < 240K (too dry for ch.20-22 over land)
6063    logical,          intent(in)    :: precipobs        ! .true. if precip. detected through NRL scattering indices
6064    real(8),          intent(in)    :: zdi              ! simple AMSU-B Dryness Index Tb(ch.3)-Tb(ch.5)
6065    real(8),          intent(in)    :: riwv             ! Mean 183 Ghz [ch. 18-22] Tb
6066    integer,          intent(inout) :: allcnt           ! total number of obs
6067    integer,          intent(inout) :: drycnt           ! Number of pts flagged for AMSU-B Dryness Index
6068    integer,          intent(inout) :: landcnt          ! Number of obs pts found over land/ice
6069    integer,          intent(inout) :: rejcnt           ! Number of rejected obs (Tb err, QCfail)
6070    integer,          intent(inout) :: iwvcnt           ! Number of pts with Mean 183 Ghz Tb < 240K
6071    integer,          intent(inout) :: pcpcnt           ! Number of scatter/precip obs
6072    integer,          intent(inout) :: flgcnt           ! Total number of filtered obs
6073    integer,          intent(inout) :: calcTerrainTypeIndice         ! terrain type (0=ice, -1 otherwise)
6074    integer,          intent(in)    :: chanIgnoreInAllskyGenCoeff(:) ! Channels excluded from gen_bias_corr in all-sky mode
6075    type(struct_obs), intent(inout) :: obsSpaceData     ! obspaceData Object
6076    integer,          intent(in)    :: headerIndex      ! current header Index 
6077    integer,          intent(in)    :: sensorIndex      ! numero de satellite (i.e. indice) 
6078
6079    ! Locals:
6080    integer :: INDXCAN, codtyp, obsGlobalMarker, newInformationFlag, actualNumChannel
6081    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, obsChanNum, obsChanNumWithOffset
6082    integer :: obsFlags
6083    real(8) :: clwObsFGaveraged, cloudLiquidWaterPathObs, cloudLiquidWaterPathFG
6084    real(8) :: scatbg_rej, scatIndexOverWaterObs, scatIndexOverWaterFG
6085    logical, allocatable :: lflagchn(:)
6086
6087    cloudLiquidWaterPathObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
6088    cloudLiquidWaterPathFG = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
6089    newInformationFlag = obs_headElem_i(obsSpaceData, OBS_INFG, headerIndex)
6090    actualNumChannel = tvs_coefs(sensorIndex)%coef%fmv_ori_nchn
6091
6092    ! Allocation
6093    allocate(lflagchn(actualNumChannel))
6094
6095    lflagchn(:) = qcRejectLogic(:)  ! initialize with flags set in mwbg_firstQcCheckMwhs2
6096    allcnt = allcnt + 1  ! Counting total number of observations
6097    ! Reject all channels if gross Tb error detected in any channel or other problems
6098    if ( grossrej ) then
6099      lflagchn(:) = .true.
6100    else
6101
6102      ! OVER LAND OR SEA-ICE,
6103      !    -- CLW/SI not determined over land
6104      !    -- surface emissivity effects lower tropospheric and window channels
6105      !    -- reject window & lower tropospheric channels 1,10,14,15
6106      !    -- reject ch. 11-13 if iwvreject = .true.  [ Mean 183 Ghz [ch. 18-22] Tb < 240K ]
6107      !    -- check DI for AMSU-B like channels
6108      !    -- reject all channels if scatbg exceeds CMC thresholds for AMSU-B
6109
6110      if  ( .not. waterobs ) then
6111        lflagchn((/ 1,10,14,15 /)) = .true.       ! AMSU-B (like 1,2,5)
6112        if ( iwvreject ) lflagchn(11:13) = .true. ! AMSU-B (like 4,3)
6113
6114        ! Dryness index (for AMSU-B channels 11-14 assimilated over land/sea-ice)
6115        ! Channel AMSUB-3 (MWHS-2 channel 11) is rejected for a dryness index >    0.
6116        !                 (MWHS-2 channel 12) is rejected for a dryness index >   -5.
6117        ! Channel AMSUB-4 (MWHS-2 channel 13) is rejected for a dryness index >   -8.
6118        if ( zdi > 0.0d0 ) then
6119          lflagchn(11) = .true.
6120          newInformationFlag = IBSET(newInformationFlag,7)
6121        end if
6122        if ( zdi > -5.0d0 ) then
6123          lflagchn(12) = .true.
6124          newInformationFlag = IBSET(newInformationFlag,9)
6125          drycnt = drycnt + 1
6126        end if
6127        if ( zdi > -8.0d0 ) then
6128          lflagchn(13) = .true.
6129        end if
6130
6131        ! Bennartz -Grody SI check thresholds (same as for QC of AMSU-B/MHS)
6132        if ( calcTerrainTypeIndice == 0 ) then ! sea-ice
6133          scatbg_rej = scatbg_mwhs2_cmc_ICErej
6134        else                     ! land
6135          scatbg_rej = scatbg_mwhs2_cmc_LANDrej
6136        end if
6137        if ( scatbg > scatbg_rej ) then
6138          lflagchn(:) = .true.
6139          newInformationFlag = IBSET(newInformationFlag,8)
6140        end if
6141
6142      else  ! if waterobs
6143
6144      ! OVER WATER,
6145      !-----------------------------------------------------------------
6146      ! PLACEHOLDER VALUES FOR ALLSKY ASSIM, SINCE NOT IMPLEMENTED YET
6147      !    in clear-sky mode:
6148      !    -- reject ch. 1, if CLW > clw_mwhs2_nrl_LTrej or CLW = mwbg_realMissing
6149      !    in all-sky mode:
6150      !    -- reject ch. 1, if CLW > mwbg_clwQcThreshold or CLW = mwbg_realMissing
6151      !-----------------------------------------------------------------
6152      !    -- reject ch. 1, 10, 13-15 if CLW > clw_mwhs2_nrl_LTrej
6153      !    -- reject ch. 11-12 if CLW > clw_mwhs2_nrl_UTrej
6154      !    -- reject ch. 1, 10-15 if scatec > 9  or scatec = mwbg_realMissing
6155      !    -- reject ch. 1, 10-15 if iwvreject = .true.   [ Mean 183 Ghz [ch. 11-15] Tb < 240K ]
6156      !    -- reject all channels if scatbg exceeds CMC SEA threshold for AMSU-B
6157
6158        if ( cloudLiquidWaterPathObs > clw_mwhs2_nrl_LTrej )  then
6159          if ( tvs_mwAllskyAssim ) then ! NEVER TRUE SINCE NOT IMPLEMENTED YET
6160            clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
6161            if ( clwObsFGaveraged > mwbg_clwQcThreshold ) lflagchn(1) = .true.
6162          else
6163            lflagchn(1) = .true.
6164          end if
6165          lflagchn((/ 10,13,14,15 /)) = .true.
6166        end if
6167        if ( cloudLiquidWaterPathObs > clw_mwhs2_nrl_UTrej )  then
6168          lflagchn(11:12) = .true.
6169        end if
6170        if ( scatec >  scatec_mwhs2_nrl_LTrej ) then
6171          lflagchn(1) = .true.
6172          lflagchn(10:15) = .true.
6173        end if
6174        if ( iwvreject ) then
6175          lflagchn(1) = .true.
6176          lflagchn(10:15) = .true.
6177        end if
6178        if ( riwv == mwbg_realMissing ) then     ! riwv = mean_Tb_183Ghz
6179          newInformationFlag = IBSET(newInformationFlag,1)
6180          lflagchn(1) = .true.
6181          lflagchn(10:15) = .true.
6182        end if
6183        ! Bennartz-Grody SI check thresholds (same as for QC of AMSU-B/MHS)
6184        if ( scatbg > scatbg_mwhs2_cmc_SEA ) then
6185          lflagchn(:) = .true.
6186          newInformationFlag = IBSET(newInformationFlag,8)
6187        end if
6188      end if  ! if waterobs
6189
6190    end if  ! if .not. grossrej
6191
6192    if ( .not. waterobs ) landcnt  = landcnt  + 1
6193    if ( grossrej )  rejcnt = rejcnt + 1
6194    if ( iwvreject)  iwvcnt = iwvcnt + 1
6195    if ( precipobs .and. waterobs ) then
6196      pcpcnt = pcpcnt + 1
6197    end if
6198
6199    if ( ANY(lflagchn(:)) ) flgcnt = flgcnt + 1
6200
6201    ! RESET scatIndexOverWaterObs array to Bennartz-Grody scattering index for output to BURP file
6202    scatIndexOverWaterObs = scatbg
6203    ! Set missing cloudLiquidWaterPathFG and scatIndexOverWaterFG to BURP missing value (mwbg_realMissing)
6204    if (cloudLiquidWaterPathObs == mwbg_realMissing) cloudLiquidWaterPathFG = mwbg_realMissing
6205    scatIndexOverWaterFG = mwbg_realMissing
6206
6207    ! Modify data flag values (set bit 7) for rejected data
6208    ! In all-sky mode, turn on bit=23 for channels in chanIgnoreInAllskyGenCoeff(:)
6209    ! as cloud-affected radiances over sea when there is mismatch between 
6210    ! cloudLiquidWaterPathObs and cloudLiquidWaterPathFG (to be used in gen_bias_corr)
6211    bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
6212    bodyIndexEnd = bodyIndexBeg + obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) - 1
6213    clwObsFGaveraged = 0.5d0 * (cloudLiquidWaterPathObs + cloudLiquidWaterPathFG)
6214    BODY: do bodyIndex = bodyIndexBeg, bodyIndexEnd
6215      obsChanNumWithOffset = nint(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
6216      obsChanNum = obsChanNumWithOffset - tvs_channelOffset(sensorIndex)
6217      obsFlags = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex)
6218
6219      if (lflagchn(obsChanNum)) obsFlags = IBSET(obsFlags,7)
6220
6221      INDXCAN = utl_findloc(chanIgnoreInAllskyGenCoeff(:),obsChanNumWithOffset)
6222      if (tvs_mwAllskyAssim .and. waterobs .and. INDXCAN /= 0 .and. &
6223          (clwObsFGaveraged > mwbg_cloudyClwThresholdBcorr .or. &
6224           cloudLiquidWaterPathObs == mwbg_realMissing .or. &
6225           cloudLiquidWaterPathFG == mwbg_realMissing)) then
6226        obsFlags = IBSET(obsFlags,23)
6227      end if
6228
6229      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, obsFlags)
6230    end do BODY
6231
6232    ! Set bit 6 in 24-bit global flags if any data rejected
6233    if ( ANY(lflagchn(:)) ) then
6234      obsGlobalMarker = obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex)
6235      obsGlobalMarker = IBSET(obsGlobalMarker,6)
6236      call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex, obsGlobalMarker)
6237    end if
6238
6239    codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
6240    call obs_headSet_r(obsSpaceData, OBS_CLWO, headerIndex, cloudLiquidWaterPathObs)
6241    call obs_headSet_r(obsSpaceData, OBS_CLWB, headerIndex, cloudLiquidWaterPathFG)
6242    call obs_headSet_i(obsSpaceData, OBS_INFG, headerIndex, newInformationFlag)
6243
6244    if (scatIndexOverWaterObs /= mwbg_realMissing) then
6245      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, scatIndexOverWaterObs)
6246    else
6247      call obs_headSet_r(obsSpaceData, OBS_SIO, headerIndex, MPC_missingValue_R8)
6248    end if
6249
6250    if (tvs_isInstrumAllskyHuAssim(tvs_getInstrumentId(codtyp_get_name(codtyp)))) then
6251      if (scatIndexOverWaterFG /= mwbg_realMissing) then
6252        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, scatIndexOverWaterFG)
6253      else
6254        call obs_headSet_r(obsSpaceData, OBS_SIB, headerIndex, MPC_missingValue_R8)
6255      end if
6256    end if
6257
6258  end subroutine mwbg_reviewAllCritforFinalFlagsMwhs2
6259
6260  !--------------------------------------------------------------------------
6261  ! calcStateDepObsErr
6262  !--------------------------------------------------------------------------
6263  function calcStateDepObsErr(cldPredThresh1, cldPredThresh2, &
6264                                 errThresh1, errThresh2, cldPredUsed) result(sigmaObsErrUsed)
6265    !
6266    !:Purpose: Calculate single-precision state-dependent observation error.
6267    !                                 
6268    implicit none
6269
6270    ! Arguments:
6271    real(8), intent(in) :: cldPredThresh1  ! first cloud predictor threshold
6272    real(8), intent(in) :: cldPredThresh2  ! second cloud predictor threshold
6273    real(8), intent(in) :: errThresh1      ! sigmaO corresponding to first cloud predictor threshold
6274    real(8), intent(in) :: errThresh2      ! sigmaO corresponding to second cloud predictor threshold
6275    real(8), intent(in) :: cldPredUsed     ! cloud predictor for the obs
6276    ! Result:
6277    real(8)             :: sigmaObsErrUsed ! estimated sigmaO for the obs
6278
6279    if (cldPredUsed <= cldPredThresh1) then
6280      sigmaObsErrUsed = errThresh1
6281    else if (cldPredUsed >  cldPredThresh1 .and. & 
6282             cldPredUsed <= cldPredThresh2) then
6283      sigmaObsErrUsed = errThresh1 + &
6284                        (errThresh2 - errThresh1) / &
6285                        (cldPredThresh2 - cldPredThresh1) * &
6286                        (cldPredUsed - cldPredThresh1) 
6287    else
6288      sigmaObsErrUsed = errThresh2
6289    end if
6290
6291  end function calcStateDepObsErr
6292   
6293  !--------------------------------------------------------------------------
6294  !  ifTovsExist
6295  !--------------------------------------------------------------------------
6296  function ifTovsExist(headerIndex, sensorIndex, obsSpaceData) result(sensorIndexFound)
6297    !
6298    !:Purpose: Check obs is among the sensors.
6299    !
6300    implicit None
6301
6302    ! Arguments:
6303    integer,          intent(in)    :: headerIndex  ! current header Index 
6304    integer,          intent(out)   :: sensorIndex  ! find tvs_sensor index corresponding to current obs
6305    type(struct_obs), intent(inout) :: obsSpaceData ! obspaceData Object
6306    ! Result:
6307    logical :: sensorIndexFound
6308
6309    ! Locals:
6310    integer :: iplatform, instrum, isat, iplatf, instr
6311
6312    ! find tvs_sensor index corresponding to current obs
6313    iplatf      = obs_headElem_i( obsSpaceData, OBS_SAT, headerIndex )
6314    instr       = obs_headElem_i( obsSpaceData, OBS_INS, headerIndex )
6315
6316    call tvs_mapSat( iplatf, iplatform, isat )
6317    call tvs_mapInstrum( instr, instrum )
6318    
6319    sensorIndexFound = .false.
6320    do sensorIndex =1, tvs_nsensors
6321      if ( iplatform ==  tvs_platforms(sensorIndex)  .and. &
6322           isat      ==  tvs_satellites(sensorIndex) .and. &
6323           instrum   == tvs_instruments(sensorIndex)       ) then
6324          sensorIndexFound = .true. 
6325         exit
6326      end if
6327    end do
6328
6329  end function ifTovsExist 
6330
6331  !--------------------------------------------------------------------------
6332  ! mwbg_mwbg_bgCheckMW
6333  !--------------------------------------------------------------------------
6334  subroutine mwbg_bgCheckMW( obsSpaceData )
6335    !
6336    !:Purpose: Do the quality control for ATMS, AMSUA, AMSUB and MWHS2
6337    !
6338    implicit None
6339
6340    ! Arguments:
6341    type(struct_obs), intent(inout) :: obsSpaceData   ! obspaceData Object
6342
6343    ! Locals:
6344    integer               :: headerIndex              ! header Index
6345    integer               :: sensorIndex              ! satellite index in obserror file
6346    integer               :: codtyp                   ! codetype
6347    real(8)               :: modelInterpTerrain       ! topo in standard file interpolated to obs point
6348    real(8)               :: modelInterpSeaIce        ! Glace de mer " "
6349    real(8)               :: modelInterpLandFrac      ! model interpolated land fraction
6350    integer, allocatable  :: qcIndicator(:)           ! indicateur controle de qualite tovs par canal 
6351                                                      !  =0 ok, >0 rejet,
6352    integer, external     :: exdb, exfin, fnom, fclos
6353    logical               :: mwDataPresent, sensorIndexFound
6354    logical               :: lastHeader               ! active while reading last report
6355
6356    call utl_tmg_start(118,'--BgckMicrowave')
6357    mwDataPresent = .false.
6358    call obs_set_current_header_list(obsSpaceData,'TO')
6359    HEADER0: do
6360      headerIndex = obs_getHeaderIndex(obsSpaceData)
6361    if (headerIndex < 0) exit HEADER0
6362      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
6363      if ( tvs_isIdBurpInst(codtyp,'atms' ) .or. &
6364           tvs_isIdBurpInst(codtyp,'amsua') .or. &
6365           tvs_isIdBurpInst(codtyp,'amsub') .or. & 
6366           tvs_isIdBurpInst(codtyp,'mwhs2') .or. &
6367           tvs_isIdBurpInst(codtyp,'mhs'  ) ) then
6368        mwDataPresent = .true.
6369      end if
6370    end do HEADER0
6371
6372    if ( .not. mwDataPresent ) then 
6373      write(*,*) 'WARNING: WILL NOT RUN mwbg_bgCheckMW since no ATMS or AMSUA or MWHS2'
6374      return
6375    end if 
6376
6377    write(*,*) ' MWBG QC PROGRAM STARTS ....'
6378    ! read nambgck
6379    call mwbg_init()
6380
6381    !Quality Control loop over all observations
6382    !
6383    ! loop over all header indices of the specified family with surface obs
6384    lastHeader = .false.
6385
6386    call obs_set_current_header_list(obsSpaceData,'TO')
6387    HEADER: do
6388      headerIndex = obs_getHeaderIndex(obsSpaceData)
6389      if (headerIndex < 0) exit HEADER
6390      if (headerIndex == obs_numHeader(obsSpaceData)) lastHeader = .true.
6391      codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
6392      if (instName== 'AMSUB') then
6393        if ( .not. tvs_isIdBurpInst(codtyp,'amsub') .and. &
6394             .not. tvs_isIdBurpInst(codtyp,'mhs'  )  .and. &
6395             .not. tvs_isIdBurpInst(codtyp,'mwhs2') ) then
6396          write(*,*) 'WARNING: Observation with codtyp = ', codtyp, ' is not ', instName
6397          cycle HEADER
6398        end if 
6399      else
6400        if ( .not. (tvs_isIdBurpInst(codtyp,instName)) ) then
6401          write(*,*) 'WARNING: Observation with codtyp = ', codtyp, ' is not ', instName
6402          cycle HEADER
6403        end if
6404      end if
6405 
6406      sensorIndexFound = ifTovsExist(headerIndex, sensorIndex, obsSpaceData)
6407      if ( .not. sensorIndexFound ) call utl_abort('midas-bgckMW: sensor Index not found') 
6408
6409      ! STEP 1: Interpolation de le champ MX(topogrpahy), MG et GL aux pts TOVS.
6410      call mwbg_readGeophysicFieldsAndInterpolate(instName, modelInterpTerrain, &
6411                                                  modelInterpLandFrac, modelInterpSeaIce, &
6412                                                  headerIndex, obsSpaceData)
6413
6414      ! STEP 2: Controle de qualite des TOVS. Data QC flags (obsFlags) are modified here!
6415      if (instName == 'AMSUA') then
6416        call mwbg_tovCheckAmsua(qcIndicator, sensorIndex, modelInterpLandFrac, modelInterpTerrain, &
6417                                modelInterpSeaIce, RESETQC, headerIndex, obsSpaceData)
6418
6419      else if (instName == 'AMSUB') then
6420        call mwbg_tovCheckAmsub(qcIndicator, sensorIndex, modelInterpLandFrac, modelInterpTerrain, &
6421                                modelInterpSeaIce, RESETQC, headerIndex, obsSpaceData)
6422
6423      else if (instName == 'ATMS') then
6424        call mwbg_tovCheckAtms(qcIndicator, sensorIndex, modelInterpTerrain, &
6425                               RESETQC, headerIndex, obsSpaceData)
6426
6427      else if (instName == 'MWHS2') then
6428        call mwbg_tovCheckMwhs2(qcIndicator, sensorIndex, modelInterpTerrain, &
6429                                RESETQC, modLSQ, lastHeader, headerIndex, obsSpaceData)
6430
6431      else
6432        write(*,*) 'midas-bgckMW: instName = ', instName
6433        call utl_abort('midas-bgckMW: unknown instName')
6434      end if
6435
6436      ! STEP 3: Accumuler Les statistiques sur les rejets
6437      call mwbg_qcStats(instName, qcIndicator, sensorIndex, &
6438                        tvs_satelliteName(1:tvs_nsensors), .FALSE.)
6439    end do HEADER
6440
6441    ! STEP 4: Print the statistics in listing file 
6442    call mwbg_qcStats(instName, qcIndicator, sensorIndex, &
6443                      tvs_satelliteName(1:tvs_nsensors), .TRUE.)
6444
6445    call utl_tmg_stop(118)
6446
6447  end subroutine mwbg_bgCheckMW 
6448
6449end module bgckMicrowave_mod