subroutine da_qc_ahi (it, i, nchan, ob, iv) 1,17

   !---------------------------------------------------------------------------
   ! Purpose: perform quality control for ahi  data.
   !  Method: Assume cloud flag coming from GEOCAT processing
   ! To be developed: built in cloud_detection method 
   ! HISTORY: 2020/03/01 - Add clear sky cloud detection procedures   Dongmei Xu, NUIST, NCAR/MMM   
   !---------------------------------------------------------------------------

   implicit none

   integer, intent(in)             :: it         ! outer loop count
   integer, intent(in)             :: i          ! sensor index.
   integer, intent(in)             :: nchan      ! number of channel
   type (y_type),  intent(in)      :: ob         ! Observation structure.
   type (iv_type), intent(inout)   :: iv         ! O-B structure.
   ! local variables
   logical   :: lmix, cloud_detection 
   integer   :: n,k,isflg,ios,fgat_rad_unit
   integer   :: ngood(nchan),nrej(nchan),nrej_omb_abs(nchan), &
                nrej_omb_std(nchan), &
                nrej_clw(nchan),num_proc_domain, &
				nrej_mixsurface,nrej_land

   real      :: inv_grosscheck, lowBTcheck					
   ! isflg: SEA(0),ICE(1),LAND(2),SNOW(3),MSEA(4),MICE(5),MLND(6),MSNO(7)
   integer, parameter :: sea_flag = 0
   integer, parameter :: ice_flag = 1
   integer, parameter :: land_flag = 2
   integer, parameter :: snow_flag = 3
   integer, parameter :: msea_flag = 4
   integer, parameter :: mice_flag = 5
   integer, parameter :: mland_flag = 6
   integer, parameter :: msnow_flag = 7
   character(len=30)  :: filename
   logical           :: print_cld_debug

   !! Additional variables used by Zhuge and Zou (2017)
   integer            :: itest
   logical            :: reject_clddet
   real               :: crit_clddet
   real               :: rad_O14, rad_M14, rad_tropt
   real               :: rad_o_ch7, rad_b_ch7, rad_o_ch14, rad_b_ch14   
   real               :: Relaz, Glintzen, tb_temp1 
   real               :: wave_num(10)
   real               :: plbc1(10), plbc2(10)
   real               :: plfk1(10), plfk2(10)
   integer, parameter :: num_clddet_tests = 10
   integer, parameter :: num_clddet_cats  = 4
   real               :: eps_clddet(num_clddet_tests+2,num_clddet_cats)
   integer            :: index_clddet(num_clddet_tests), offset_clddet
   integer            :: isflgs_clddet(num_clddet_cats)
   logical            :: qual_clddet(num_clddet_cats)
   character(len=10)  :: crit_names_clddet(num_clddet_tests)
   integer            :: nrej_clddet(nchan,num_clddet_tests+1)
   integer*2          :: clddet_tests(iv%instid(i)%superob_width, &
                                      iv%instid(i)%superob_width, &
                                      num_clddet_tests)
   integer            :: superob_center
   integer            :: isuper, jsuper

   real, pointer      :: tb_ob(:,:), tb_xb(:,:), tb_inv(:,:), tb_xb_clr(:,:), ca_mean(:,:)
   integer            :: tb_qc(nchan), tb_qc_clddet(nchan)

   real               :: big_num   
   real, parameter :: C1=1.19104276e-5     ! = 2 * h * c**2 mWm-2sr-1(cm-1)-4
   real, parameter :: C2=1.43877516        ! = h * c / b = 1.43877 K(cm-1)-1

   ! h = Planck's constant
   ! b = Boltzmann constant
   ! c = velocity of light

   integer, parameter :: ch7  = 1
   integer, parameter :: ch10 = 4
   integer, parameter :: ch13 = 7
   integer, parameter :: ch14 = 8
   integer, parameter :: ch15 = 9
   ! mmr or pf Cloud Detection Variables
   integer              :: kts_100hPa(1), kte_surf, ndim
   integer              :: numrad_local(nchan), numrad_global(nchan)
   real                 :: tstore
   real                 :: bias_local(nchan), bias_global(nchan)
   integer              :: kmin, kmax
   integer, allocatable  :: k_cloud_flag(:) ! cloud flags  
   if (trace_use) call da_trace_entry("da_qc_ahi")
   ! These values can change as SRF (spectral response function) is updated
   ! It is recommended to acquire these from L1B files, not copy them from GOES R PUG L1b Vol. 3
   wave_num(1:10)   = (/2570.373, 1620.528, 1443.554, 1363.228, 1184.220, & 
                        1040.891,  968.001,  894.000,  815.294,  753.790/)
   plbc1(1:10)      = (/0.43361, 1.55228, 0.34427, 0.05651, 0.18733, & 
                        0.09102, 0.07550, 0.22516, 0.21702, 0.06266/)
   plbc2(1:10)      = (/0.99939, 0.99667, 0.99918, 0.99986, 0.99948, &
                        0.99971, 0.99975, 0.99920, 0.99916, 0.99974/)

   plfk1 = C1 * wave_num**3
   plfk2 = C2 * wave_num

   crit_names_clddet(1)  = "rtct"  !here
   crit_names_clddet(2)  = "etrop"  
   crit_names_clddet(3)  = "pfmft"
   crit_names_clddet(4)  = "nfmft"
   crit_names_clddet(5)  = "rfmft"  !here
   crit_names_clddet(6)  = "cirh2o"  !here
   crit_names_clddet(7)  = "emiss4"
   crit_names_clddet(8)  = "ulst"  !here
   crit_names_clddet(9)  = "notc"
   crit_names_clddet(10) = "tempir" !here

   big_num = huge(big_num)
   !!  Table 4 from Zhuge X. and Zou X. JAMC, 2016. [modified from ABI Cloud Mask Algorithm]
                 !ocean       land      snow      ice (assume same as snow)
   eps_clddet = transpose( reshape( (/ &
                    3.2,     4.1, big_num, big_num &
                 ,  0.1,     0.3,     0.4,     0.4 &
                 ,  0.8,     2.5, big_num, big_num &
                 ,  1.0,     2.0,     5.0,     5.0 &
                 ,  0.7,     1.0, big_num, big_num &
                 ,  0.7,     0.7,     0.7,     0.7 &
                 ,  0.1,    0.2,     0.3,     0.3 & ! Land values: 0.46 in ABI CM; 0.2 in ZZ16
                 , 2.86, big_num, big_num, big_num &
                 , 0.05,     0.1,    0.12,    0.12 &
                 ,  15.,     21.,     10.,     10. &
                 ,  11.,     15.,     4.5,     4.5 &
                 ,  2.0,     2.0,     2.0,     2.0 &
                /), (/ size(eps_clddet, 2), size(eps_clddet, 1) /)) )
   index_clddet  = (/1, 2, 3, 4, 5, 6, 7, 9, 10, 12/)
   isflgs_clddet = (/sea_flag, land_flag, snow_flag, ice_flag/)
   print_cld_debug = .false.
   ngood(:)        = 0
   nrej(:)         = 0
   nrej_omb_abs(:) = 0
   nrej_omb_std(:) = 0
   nrej_clw(:)     = 0
   nrej_mixsurface = 0
   nrej_land       = 0
   nrej_clddet(:,:)= 0  
   num_proc_domain = 0

   superob_center = ahi_superob_halfwidth + 1

   tb_xb => iv%instid(i)%tb_xb
   tb_inv => iv%instid(i)%tb_inv
   AHIPixelQCLoop: do n= iv%instid(i)%info%n1,iv%instid(i)%info%n2

      tb_ob => ob%instid(i)%tb

      if (iv%instid(i)%info%proc_domain(1,n)) &
            num_proc_domain = num_proc_domain + 1

      !  0.0  initialise QC by flags assuming good obs
      !-----------------------------------------------------------------
      tb_qc = qc_good	

      !  1.0  reject all channels over mixture surface type
      !------------------------------------------------------	  
      isflg = iv%instid(i)%isflg(n)
      lmix  = (isflg==4) .or. (isflg==5) .or. (isflg==6) .or. (isflg==7)
      if (lmix) then
         tb_qc = qc_bad
         if (iv%instid(i)%info%proc_domain(1,n)) &
            nrej_mixsurface = nrej_mixsurface + 1
      end if

      if ( isflg > 0 ) then         
         do k = 1, nchan                
            if ( k /= 2 .and. k /= 3 .and. k /= 4 ) then
            if (only_sea_rad) then
               tb_qc(k) = qc_bad
               nrej_land = nrej_land + 1
            end if
            end if
         end do         
      end if
      !  2.0 check iuse
      !-----------------------------------------------------------------
      do k = 1, nchan
         if (satinfo(i)%iuse(k) .eq. -1) &
             tb_qc(k) = qc_bad
      end do

      !  3.0 check clw in  fg
      !-----------------------------------------------------------------
      if (.not. crtm_cloud ) then

         do k = 1, nchan		 
         if (iv%instid(i)%clwp(n) >= 0.2) then
            tb_qc = qc_bad
            if (iv%instid(i)%info%proc_domain(1,n)) &
               nrej_clw(k) = nrej_clw(k) + 1
         end if
		end do 
      end if
	  ! METHOD,Zhuge, X. and Zou, X., Test of a Modified Infrared-Only ABI Cloud Mask Algorithm for AHI Radiance Observations. J. Appl. Meteor. Climatol., 2016, 55: 2529–2546.
      ahi_clddet: if ( use_clddet_zz &
                     .and. all(tb_inv( (/ch7,ch14,ch15/), n ) .gt. missing_r) &
                     .and. all(tb_ob(  (/ch7,ch14,ch15/), n ) .gt. missing_r) &
                     .and. all(tb_xb(  (/ch7,ch14,ch15/), n ) .gt. missing_r) &
                     ) then
         !!===============================================================================
         !!===============================================================================
         !!
         !!  4.0 AHI IR-only Cloud Mask Algorithm, combines:
         !!     (*) Heidinger A. and Straka W., ABI Cloud Mask, version 3.0, 11 JUN, 2013.
         !!     (*) Zhuge X. and Zou X. JAMC, 2016.
         !!
         !!===============================================================================
         !!===============================================================================

!JJGDEBUG
         if (print_cld_debug) write(stdout,'(A,I8,*(2x,F10.4:))') 'PIXEL_DEBUG1: ', n, &
            tb_inv(:,n)
         if (print_cld_debug) write(stdout,'(A,I8,*(2x,F10.4:))') 'PIXEL_DEBUG2: ', n, &
            tb_xb(:,n)
         if (print_cld_debug) write(stdout,'(A,I8,*(2x,F10.4:))') 'PIXEL_DEBUG3: ', n, &
            tb_ob(:,n)
         if (crtm_cloud ) then
            if (print_cld_debug) write(stdout,'(A,I8,*(2x,F10.4:))') 'PIXEL_DEBUG4: ', n, &
               tb_xb_clr(:,n)
         end if

         if (print_cld_debug) write(stdout,'(A,I8,8F12.4,2x,A)') 'PIXEL_DEBUG5: ', n, &
            iv%instid(i)%info%lat(1,n), iv%instid(i)%info%lon(1,n), &
            iv%instid(i)%satzen(n), iv%instid(i)%satazi(n), &
            iv%instid(i)%solzen(n), iv%instid(i)%solazi(n), &
            iv%instid(i)%tropt(n), iv%instid(i)%superob(superob_center,superob_center)%cld_qc(n)%terr_hgt, &
            iv%instid(i)%info%date_char(n)
!JJGDEBUG

         clddet_tests = 0

         do jsuper = 1, iv%instid(i)%superob_width
         do isuper = 1, iv%instid(i)%superob_width

         tb_ob => iv%instid(i)%superob(isuper,jsuper)%tb_obs

         if ( tb_xb(ch14,n) .gt. 0. .and. iv%instid(i)%tropt(n) .gt. 0. ) then
            tb_temp1  = tb_ob(ch14,n)
            rad_O14   = plfk1(ch14) / &
                        ( exp( plfk2(ch14) / (plbc1(ch14) + plbc2(ch14) * tb_temp1 ) ) -1 )
            tb_temp1  = tb_xb(ch14,n)
            rad_M14   = plfk1(ch14) / &
                        ( exp( plfk2(ch14) / (plbc1(ch14) + plbc2(ch14) * tb_temp1) ) -1 )
            tb_temp1  = iv%instid(i)%tropt(n)
            rad_tropt = plfk1(ch14) / &
                        ( exp( plfk2(ch14) / (plbc1(ch14) + plbc2(ch14) * tb_temp1) ) -1 )
         else
            rad_O14 = missing_r
            rad_M14 = missing_r
            rad_tropt = missing_r
         end if

         if (tb_ob(ch7,n) .gt. 0. .and. tb_ob(ch14,n) .gt. 0.) then
            tb_temp1   = tb_ob(ch7,n)
            rad_o_ch7  = plfk1(ch7) / & 
                          ( exp( plfk2(ch7) / ( plbc1(ch7) + plbc2(ch7) * tb_temp1 ) ) - 1. )
            tb_temp1   = tb_xb(ch7,n)
            rad_b_ch7  = plfk1(ch7) / & 
                          ( exp( plfk2(ch7) / ( plbc1(ch7) + plbc2(ch7) * tb_temp1 ) ) - 1. )
            tb_temp1   = tb_ob(ch14,n)
            rad_o_ch14 = plfk1(ch7) / & 
                          ( exp( plfk2(ch7) / ( plbc1(ch7) + plbc2(ch7) * tb_temp1 ) ) - 1. )
            tb_temp1   = tb_xb(ch14,n)
            rad_b_ch14 = plfk1(ch7) / & 
                          ( exp( plfk2(ch7) / ( plbc1(ch7) + plbc2(ch7) * tb_temp1 ) ) - 1. )
         else
            rad_o_ch7 = missing_r
            rad_b_ch7 = missing_r
            rad_o_ch14 = missing_r
            rad_b_ch14 = missing_r
         end if

         tb_qc_clddet = tb_qc

         AHICloudTestLoop: do itest = 1, num_clddet_tests
            qual_clddet = .true.
            offset_clddet = 0
            crit_clddet = missing_r
            select case (itest)
               case (1)
                  !--------------------------------------------------------------------------
                  ! 4.1 Relative Thermal Contrast Test (RTCT)
                  !--------------------------------------------------------------------------
                  crit_clddet = iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%RTCT
                  qual_clddet(3:4) = .false.			  
               case (2)
                  !--------------------------------------------------------------------------
                  ! 4.2 Cloud check: step 1  
                  ! Emissivity at Tropopause Test (ETROP)
                  !--------------------------------------------------------------------------
                  if ( tb_xb(ch14,n) .gt. 0. .and. iv%instid(i)%tropt(n) .gt. 0. ) &
                     crit_clddet = (rad_O14 - rad_M14) / (rad_tropt - rad_M14)
               case (3)
                  !--------------------------------------------------------------------------
                  ! 4.3 Cloud check: step 2  
                  ! Positive Fourteen Minus Fifteen Test (PFMFT)
                  !--------------------------------------------------------------------------
                  ! See ABI Cloud Mask Description for qual_clddet
                  qual_clddet = (tb_xb(ch14,n).ge.tb_xb(ch15,n))
                  if ( (tb_inv(ch14,n) + tb_xb(ch14,n)).le.310. .and. &
                       iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%tb_stddev_14.ge.0.3 .and. &
                       tb_ob(ch14,n).gt.0. .and. tb_ob(ch15,n).gt.0. ) &
                        crit_clddet = ( tb_ob(ch14,n) - tb_ob(ch15,n) )
! above using ob without VarBC 
! -------------------------------
!                        crit_clddet = (tb_inv(ch14,n) + tb_xb(ch14,n) - &
!                                (tb_inv(ch15,n) + tb_xb(ch15,n)) )-&
!                                (tb_xb(ch14,n) - tb_xb(ch15,n)) * &
!                                (tb_ob(ch14,n) - 260.) / (tb_xb(ch14,n) - 260.) 
! above using ob with VarBC
! -------------------------------
                  if ( crit_clddet.gt.missing_r .and. &
                       (tb_inv(ch14,n) + tb_xb(ch14,n)).gt.270. .and. &
                       tb_xb(ch14,n).gt.270. ) &
                        crit_clddet = crit_clddet       - &
                                (tb_xb(ch14,n) - tb_xb(ch15,n)) * &
                                (tb_ob(ch14,n) - 260.) / (tb_xb(ch14,n) - 260.)
! above 1 line using ob without VarBC
!                               (tb_inv(ch14,n) + tb_xb(ch14,n) - 260.)/ & 
!                               (tb_xb(ch14,n) - 260.) 
! above 2 lines using ob with VarBC  

               case (4)
                  !--------------------------------------------------------------------------
                  ! 4.4 Negative Fourteen Minus Fifteen Test (NFMFT)
                  !--------------------------------------------------------------------------
                  if (tb_ob(ch14,n) .gt. 0. .and. tb_ob(ch15,n) .gt. 0.) &
                     crit_clddet = tb_inv(ch15,n) - tb_inv(ch14,n)
               case (5)
                  !--------------------------------------------------------------------------
                  ! 4.5 Relative Fourteen Minus Fifteen Test (RFMFT)
                  !--------------------------------------------------------------------------
                  ! See ABI Cloud Mask Description for qual_clddet
                  qual_clddet      = ( tb_ob(ch14,n) - tb_ob(ch15,n) ) .lt. 1.0
                  qual_clddet(2)   = qual_clddet(2) .and. tb_ob(ch14,n) .le. 300.
                  qual_clddet(3:4) = .false.

                  crit_clddet = iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%RFMFT
               case (6)
                  !--------------------------------------------------------------------------
                  ! 4.6 Cirrus Water Vapor Test (CIRH2O)
                  !--------------------------------------------------------------------------
                  ! See ABI Cloud Mask Description for qual_clddet
                  qual_clddet = &
                          iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%terr_hgt .le. 2000.  &
                     .and. iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%terr_hgt .ge. 0.  &
                     .and. iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%tb_stddev_10 .gt. 0.5 &
                     .and. iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%tb_stddev_14 .gt. 0.5
                     crit_clddet = iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%CIRH2O
               case (7)
                  !--------------------------------------------------------------------------
                  ! 4.7 Modified 4um Emissivity Test (M-4EMISS)
                  !--------------------------------------------------------------------------
                  ! Modify EMISS for sun glint area may be  not work, because we are at north land
                  ! - compute relative azimuth
                  Relaz = RELATIVE_AZIMUTH(iv%instid(i)%solazi(n),iv%instid(i)%satazi(n))

                  ! - compute glint angle
                  Glintzen = GLINT_ANGLE(iv%instid(i)%solzen(n),iv%instid(i)%satzen(n),Relaz )

                  if ( Glintzen.lt.40.0 .and. isflg==sea_flag) then
                     crit_clddet = - tb_inv(ch7,n) ! (B_ch7 - O_ch7)
                     offset_clddet = 1
                  else
                     if (tb_ob(ch7,n) .gt. 0. .and. tb_ob(ch14,n) .gt. 0.) &
                        crit_clddet = (rad_o_ch7/rad_o_ch14 - rad_b_ch7/rad_b_ch14)/ & 
                                  (rad_b_ch7 / rad_b_ch14)
                  end if
               case (8)
                  !--------------------------------------------------------------------------
                  ! 4.8 Uniform low stratus Test (ULST)
                  !--------------------------------------------------------------------------
!JJG, AHI error: Changed this to solzen instead of solazi for night/day test
                  qual_clddet = iv%instid(i)%solzen(n) >= 85.0
                  if (tb_ob(ch7,n) .gt. 0. .and. tb_ob(ch14,n) .gt. 0.) &
                     crit_clddet = rad_b_ch7/rad_b_ch14 - rad_o_ch7/rad_o_ch14
               case (9)
                  !--------------------------------------------------------------------------
                  ! 4.9 New Optically Thin Cloud Test (N-OTC)
                  !--------------------------------------------------------------------------
!JJG, AHI error: Changed this to solzen instead of solazi for night/day test
                  if ( iv%instid(i)%solzen(n) .ge. 85.0 ) &
                     offset_clddet = 1 ! night time

                  if (tb_ob(ch7,n) .gt. 0. .and. tb_ob(ch15,n) .gt. 0.) &
! using ob without VarBC
! -------------------------------
                       crit_clddet = tb_ob(ch7,n) - tb_ob(ch15,n)  

! using ob with VarBC
! -------------------------------
!                       crit_clddet = tb_inv(ch7,n) + tb_xb(ch7,n) - & 
!                             (tb_inv(ch15,n) + tb_xb(ch15,n))
               case (10)
                  !--------------------------------------------------------------------------
                  ! 4.10 Temporal Infrared Test (TEMPIR)
                  !--------------------------------------------------------------------------
                  crit_clddet = iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%TEMPIR
               case default
                  cycle
            end select

!            call evaluate_clddet_test ( &
!                  isflg, isflgs_clddet, crit_clddet, eps_clddet(index_clddet(itest)+offset_clddet,:), qual_clddet, &
!                  iv%instid(i)%info%lat(1,n), iv%instid(i)%info%lon(1,n), &
!                  reject_clddet )

            reject_clddet = crit_clddet.gt.missing_r .and. &
                        any( isflg.eq.isflgs_clddet .and. &
                             crit_clddet.gt.eps_clddet(index_clddet(itest)+offset_clddet,:) .and. &
                             qual_clddet )

            if (reject_clddet) then
               tb_qc_clddet = qc_bad
               if (iv%instid(i)%info%proc_domain(1,n)) then
                  nrej_clddet(:,itest) = nrej_clddet(:,itest) + 1
        !          write(stdout,"(A,F14.6,A,I4,2D12.4)") trim(crit_names_clddet(itest)), crit_clddet, " isflg", isflg, iv%instid(i)%info%lat(1,n), iv%instid(i)%info%lon(1,n)
               end if

               clddet_tests(isuper, jsuper, itest) = 1
            end if
         end do AHICloudTestLoop
         end do ! isuper
         end do ! jsuper

         if ( iv%instid(i)%superob_width > 1 ) then
            iv%instid(i)%cloud_frac(n) = &
               real( count(sum(clddet_tests,3) > 0),8) / real( iv%instid(i)%superob_width**2,8)
         end if

         if (.not. crtm_cloud ) tb_qc = tb_qc_clddet

!JJGDEBUG
         if (print_cld_debug) write(stdout,'(A,I8,*(2x,I1:))') 'PIXEL_DEBUG6: ', n, clddet_tests
!JJGDEBUG
      else ! not clddet_zz
         ! 4. Cloud detection scheme MMR in Auligné (2014).or. PF in Xu et al., (2016)
         !---------------------------------------------
      if ((use_clddet==1 .or. use_clddet==2) .and. (.not.use_satcv(2))) then
            iv%instid(i)%cloud_flag(:,n) = qc_good

            if (rtm_option == rtm_option_rttov) then
#ifdef RTTOV
               kte_surf   = iv%instid(i)%nlevels
               kts_100hPa = MAXLOC(coefs(i)%coef%ref_prfl_p(1:kte_surf), &
                            MASK = coefs(i)%coef%ref_prfl_p(1:kte_surf) < 100.0)
               do k=1,nchan
                  tstore = coefs(i)%coef%ff_bco(k) + coefs(i)%coef%ff_bcs(k) * &
                           (ob%instid(i)%tb(k,n) - bias_global(k))
                  iv%instid(i)%rad_obs(k,n) = coefs(i)%coef%planck1(k) / &
                           (EXP(coefs(i)%coef%planck2(k)/tstore) - 1.0)
               end do
#endif
            elseif (rtm_option == rtm_option_crtm) then
               kte_surf   = kte
               kts_100hPa = MAXLOC(iv%instid(i)%pm(kts:kte,n), &
                            MASK = iv%instid(i)%pm(kts:kte,n) < 100.0)

               do k = 1, nchan
                  CALL CRTM_Planck_Radiance(i,k,ob%instid(i)%tb(k,n) - bias_global(k), &
                                            iv%instid(i)%rad_obs(k,n))
               end do

            end if

            ndim = kte_surf - kts_100hPa(1) + 1

            call da_cloud_detect(i,nchan,ndim,kts_100hPa(1),kte_surf,n,iv)
         end if

         do k = 1, nchan
            if (iv%instid(i)%cloud_flag(k,n) == qc_bad) tb_qc(k) = qc_bad
         end do
      end if ahi_clddet  

      tb_ob => ob%instid(i)%tb

      ! ---------------------------calculate and save ca_mean for crtm_cloud and crtm_clr
      ! 5.0 assigning obs errors
	     tb_xb_clr => iv%instid(i)%tb_xb_clr  	 
      if (.not. crtm_cloud ) then
         do k = 1, nchan
            if (use_error_factor_rad) then
               iv%instid(i)%tb_error(k,n) = &
                   satinfo(i)%error_std(k)*satinfo(i)%error_factor(k)
            else
               iv%instid(i)%tb_error(k,n) = satinfo(i)%error_std(k)
            end if
         end do ! nchan
      end if

      !  6.0 gross 8k check -clr,sdobs-clr
      !-----------------------------------------------------------------
      if (.not. crtm_cloud ) then
         ! absolute departure check
         do k = 1, nchan
            inv_grosscheck = 8.0
            if (use_satcv(2)) inv_grosscheck = 100.0
            if (abs(iv%instid(i)%tb_inv(k,n)) > inv_grosscheck) then
                tb_qc(k)  = qc_bad
                if (iv%instid(i)%info%proc_domain(1,n)) &
                        nrej_omb_abs(k) = nrej_omb_abs(k) + 1
            end if			
         end do ! nchan
         if (use_clddet_zz) then
            ! SDob cloud inhomogeneous check
            do isuper = 1, iv%instid(i)%superob_width
            do jsuper = 1, iv%instid(i)%superob_width
               if (iv%instid(i)%superob(isuper,jsuper)%cld_qc(n)%tb_stddev_13 >= 2) then ! only use abs clear pixel
                  tb_qc = qc_bad
                  if (iv%instid(i)%info%proc_domain(1,n)) &
                     nrej_clddet(:,11)= nrej_clddet(:,11)+1
               end if
            end do
            end do
         end if		  
      end if

      !  7.0 3std check 
      !-----------------------------------------------------------------	  
      do k = 1, nchan
         ! relative departure check
           if (abs(iv%instid(i)%tb_inv(k,n)) > 3.0*iv%instid(i)%tb_error(k,n)) then
               tb_qc(k)  = qc_bad
               if (iv%instid(i)%info%proc_domain(1,n)) &
                   nrej_omb_std(k) = nrej_omb_std(k) + 1
          
           end if
      end do	 ! nchan
 	  
      !final QC decsion	 	
      if (crtm_cloud ) tb_qc(2:4) = qc_good  ! no qc  for crtm_cloud
      iv%instid(i)%tb_qc(:,n) = tb_qc
      do k = 1, nchan     		 
           if (iv%instid(i)%tb_qc(k,n) == qc_bad) then
              iv%instid(i)%tb_error(k,n) = 500.0
              if (iv%instid(i)%info%proc_domain(1,n)) &
                  nrej(k) = nrej(k) + 1
           else
              if (iv%instid(i)%info%proc_domain(1,n)) &
                 ngood(k) = ngood(k) + 1
           end if
      end do      ! nchan	  
   end do AHIPixelQCLoop
 
   ! Do inter-processor communication to gather statistics.
   call da_proc_sum_int  (num_proc_domain)
   call da_proc_sum_int  (nrej_mixsurface)
   call da_proc_sum_int  (nrej_land)
   call da_proc_sum_ints (nrej_omb_abs)
   call da_proc_sum_ints (nrej_omb_std)
   call da_proc_sum_ints (nrej_clw)
   do itest = 1, num_clddet_tests+1
     call da_proc_sum_ints (nrej_clddet(:,itest))  
   end do   
   call da_proc_sum_ints (nrej)
   call da_proc_sum_ints (ngood)

   if (rootproc) then
      if (num_fgat_time > 1) then
         write(filename,'(i2.2,a,i2.2)') it,'_qcstat_'//trim(iv%instid(i)%rttovid_string)//'_',iv%time
      else
         write(filename,'(i2.2,a)') it,'_qcstat_'//trim(iv%instid(i)%rttovid_string)
      end if

      call da_get_unit(fgat_rad_unit)
      open(fgat_rad_unit,file=trim(filename),form='formatted',iostat=ios)
      if (ios /= 0) then
         write(unit=message(1),fmt='(A,A)') 'error opening the output file ', filename
         call da_error(__FILE__,__LINE__,message(1:1))
      end if

      write(fgat_rad_unit, fmt='(/a/)') ' Quality Control Statistics for '//iv%instid(i)%rttovid_string
      if(num_proc_domain > 0) write(fgat_rad_unit,'(a20,i7)') ' num_proc_domain  = ', num_proc_domain
      write(fgat_rad_unit,'(a20,i7)') ' nrej_mixsurface  = ', nrej_mixsurface
	  write(fgat_rad_unit,'(a20,i7)') ' nrej_land  = ', nrej_land	  
      write(fgat_rad_unit,'(a20)')    ' nrej_clw(:)  = '
	  write(fgat_rad_unit,'(10i7)')     nrej_clw(:)	  
      do itest = 1, num_clddet_tests+1
      write(fgat_rad_unit,'(a20,i2,a2)')    ' nrej_clddet',itest,"="
	  write(fgat_rad_unit,'(10i7)')     nrej_clddet(:,itest)	  	  
      end do		 
      write(fgat_rad_unit,'(a20)')    ' nrej_omb_abs(:)  = '
      write(fgat_rad_unit,'(10i7)')     nrej_omb_abs(:)
      write(fgat_rad_unit,'(a20)')    ' nrej_omb_std(:)  = '
      write(fgat_rad_unit,'(10i7)')     nrej_omb_std(:)
      write(fgat_rad_unit,'(a20)')    ' nrej(:)          = '
      write(fgat_rad_unit,'(10i7)')     nrej(:)
      write(fgat_rad_unit,'(a20)')    ' ngood(:)         = '
      write(fgat_rad_unit,'(10i7)')     ngood(:)

      close(fgat_rad_unit)
      call da_free_unit(fgat_rad_unit)
   end if
   if (trace_use) call da_trace_exit("da_qc_ahi")

end subroutine da_qc_ahi


function relative_azimuth ( sol_az ,sen_az ) 1

  implicit none

  real :: sol_az
  real :: sen_az
  real :: relative_azimuth

  relative_azimuth = abs(sol_az - sen_az)
  if (relative_azimuth > 180.0) then
       relative_azimuth = 360.0 - relative_azimuth
  endif
  relative_azimuth = 180.0 - relative_azimuth

end function relative_azimuth



function glint_angle ( sol_zen , sat_zen , rel_az  ) 1
  !------------------------------------------------------------------------------------
  ! Glint angle  (the angle difference between direct "specular" reflection off
  ! the surface and actual reflection toward the satellite.)
  !------------------------------------------------------------------------------------

  implicit none

  real :: sol_zen
  real :: sat_zen
  real :: rel_az
  real :: glint_angle

  glint_angle = cos(sol_zen * deg2rad) * cos(sat_zen * deg2rad) + &
                sin(sol_zen * deg2rad) * sin(sat_zen * deg2rad) * cos(rel_az * deg2rad)
  glint_angle = max(-1.0 , min( glint_angle ,1.0 ))
  glint_angle = acos(glint_angle) / deg2rad

end function glint_angle