!-------------------------------------- LICENCE BEGIN ------------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
!                     version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
!without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
!See the above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------
!

      SUBROUTINE SETERRGPSRO(lcolumnhr,lobsSpaceData) 1,44
#if defined (DOC)
*
***s/r SETERRGPSRO - Compute estimated errors for GPSRO observations
*
*
*Author  : J. M. Aparicio Apr 2008
*          Adapted Nov 2012 for both refractivity and bending angle data
*    -------------------
*
*Arguments
*
#endif
      use EarthConstants_mod
      use MathPhysConstants_mod
      use modgps01ctphys    , only : p_TC, p_knot
      use modgps02wgs84grav , only : gpsgravitysrf
      use modgps03diff      , only : gpsdiff
      use modgps04profile   , only : gpsprofile, gpsstruct1sw
      use modgps08refop     , only : gpsrefopv
      use modgps09bend      , only : gpsbndopv1
      use modgpsro_mod
      use obsSpaceData_mod
      use columnData_mod
      IMPLICIT NONE
C
      type(struct_columnData) :: lcolumnhr
      type(struct_obs)        :: lobsSpaceData
C
      INTEGER INDEX_HEADER, IDATYP, INDEX_BODY, iProfile
      REAL*8 zLat, Lat, sLat
      REAL*8 zLon, Lon
      REAL*8 zAzm, Azm
      REAL*8, allocatable :: ZPP(:)
      REAL*8, allocatable :: ZDP(:)
      REAL*8, allocatable :: ZTT(:)
      REAL*8, allocatable :: ZHU(:)
      REAL*8, allocatable :: ZUU(:)
      REAL*8, allocatable :: ZVV(:)
C
      REAL*8 DH,DDH
      INTEGER JL, IAZM, ISAT, JH, NGPSLEV, NWNDLEV
      REAL*8 zMT, Rad, Geo
      REAL*8 HNH1, SUM0, SUM1, ZMIN
C
      LOGICAL  ASSIM, L1, L2, L3

      INTEGER NH, NH1
      TYPE(GPSPROFILE)           :: PRF
      REAL*8       , allocatable :: H   (:),AZMV(:)
      REAL*8       , allocatable :: ZOBS(:),ZREF(:),ZOFF(:),ZERR(:), ZMHX(:)
      TYPE(GPSDIFF), allocatable :: RSTV(:)

      WRITE(*,*)'ENTER SETERRGPSRO'
C
C     * 1.  Initializations
C     *     ---------------
C
      NGPSLEV=col_getNumLev(LCOLUMNHR,'TH')
      NWNDLEV=col_getNumLev(LCOLUMNHR,'MM')
      allocate(ZPP (NGPSLEV))
      allocate(ZDP (NGPSLEV))
      allocate(ZTT (NGPSLEV))
      allocate(ZHU (NGPSLEV))
      allocate(ZUU (NGPSLEV))
      allocate(ZVV (NGPSLEV))
C
      allocate( H    (GPSRO_MAXPRFSIZE) )
      allocate( AZMV (GPSRO_MAXPRFSIZE) )
      allocate( ZOBS (GPSRO_MAXPRFSIZE) )
      allocate( ZREF (GPSRO_MAXPRFSIZE) )
      allocate( ZOFF (GPSRO_MAXPRFSIZE) )
      allocate( ZERR (GPSRO_MAXPRFSIZE) )
      allocate( RSTV (GPSRO_MAXPRFSIZE) )
      allocate( ZMHX (GPSRO_MAXPRFSIZE) )
C
C     Loop over all header indices of the 'RO' family:
C
      call obs_set_current_header_list(lobsSpaceData,'RO')
      HEADER: do
         INDEX_HEADER = obs_getHeaderIndex(lobsSpaceData)
         if (INDEX_HEADER < 0) exit HEADER
C
C     *  Process only refractivity data (codtyp 169)
C
         IDATYP = obs_headElem_i(lobsSpaceData,OBS_ITY,INDEX_HEADER)
         IF ( IDATYP .EQ. 169 ) THEN
C
C     *     Scan for requested data values of the profile, and count them
C
            ASSIM = .FALSE.
            NH = 0
            call obs_set_current_body_list(lobsSpaceData, INDEX_HEADER)
            BODY: do 
               index_body = obs_getBodyIndex(lobsSpaceData)
               if (index_body < 0) exit BODY
               IF ( obs_bodyElem_i(lobsSpaceData,OBS_ASS,INDEX_BODY).EQ.1 ) THEN
                  ASSIM = .TRUE.
                  NH = NH + 1
               ENDIF
            ENDDO BODY
C
C     *     If assimilations are requested, prepare and apply the observation operator
C
            IF (ASSIM) THEN
               iProfile=iProfile_from_index(INDEX_HEADER)
C
C     *        Basic geometric variables of the profile:
C
               IAZM = obs_headElem_i(lobsSpaceData,OBS_AZA,INDEX_HEADER)
               ISAT = obs_headElem_i(lobsSpaceData,OBS_SAT,INDEX_HEADER)
               Rad  = obs_headElem_r(lobsSpaceData,OBS_TRAD,INDEX_HEADER)
               Geo  = obs_headElem_r(lobsSpaceData,OBS_GEOI,INDEX_HEADER)
               zAzm = 0.01d0*IAZM / MPC_DEGREES_PER_RADIAN_R8
               zMT  = col_getHeight(lcolumnhr,NGPSLEV,INDEX_HEADER,'TH')/RG
C     
C     *        Profile at the observation location:
C
               zLat = obs_headElem_r(lobsSpaceData,OBS_LAT,INDEX_HEADER)
               zLon = obs_headElem_r(lobsSpaceData,OBS_LON,INDEX_HEADER)
               Lat  = zLat * MPC_DEGREES_PER_RADIAN_R8
               Lon  = zLon * MPC_DEGREES_PER_RADIAN_R8
               Azm  = zAzm * MPC_DEGREES_PER_RADIAN_R8
               sLat = sin(zLat)
               zMT  = zMT * RG / gpsgravitysrf(sLat)
               DO JL = 1, NGPSLEV
C
C     *           Profile x
C
                  ZPP(JL) = col_getPressure(LCOLUMNHR,JL,INDEX_HEADER,'TH')
C     *           True implementation of ZDP (dP/dP0)
                  ZDP(JL) = col_getPressureDeriv(LCOLUMNHR,JL,INDEX_HEADER,'TH')
                  ZTT(JL) = col_getElem(lcolumnhr,JL,INDEX_HEADER,'TT') - p_TC
                  ZHU(JL) = col_getElem(lcolumnhr,JL,INDEX_HEADER,'HU')
                  ZUU(JL) = 0.d0
                  ZVV(JL) = 0.d0
               ENDDO
               DO JL = 1, NWNDLEV
                  ZUU(JL) = col_getElem(lcolumnhr,JL,INDEX_HEADER,'UU') * p_knot
                  ZVV(JL) = col_getElem(lcolumnhr,JL,INDEX_HEADER,'VV') * p_knot
               ENDDO
               ZUU(NGPSLEV) = ZUU(NWNDLEV)
               ZVV(NGPSLEV) = ZUU(NWNDLEV)
C     
C     *        GPS profile structure:
C
               call gpsstruct1sw(ngpslev,zLat,zLon,zAzm,zMT,Rad,geo,zPP,zDP,zTT,zHU,zUU,zVV,prf)
C
C     *        Prepare the vector of all the observations:
C
               NH1 = 0
C
C     *        Loop over all body indices for this index_header:
C     *        (start at the beginning of the list)
C
               call obs_set_current_body_list(lobsSpaceData, index_header)
               BODY_2: do 
                  index_body = obs_getBodyIndex(lobsSpaceData)
                  if (index_body < 0) exit BODY_2
                  IF ( obs_bodyElem_i(lobsSpaceData,OBS_ASS,INDEX_BODY).EQ.1 ) THEN
                     NH1      = NH1 + 1
                     H(NH1)   = obs_bodyElem_r(lobsSpaceData,OBS_PPP,INDEX_BODY)
                     AZMV(NH1)= zAzm
                     ZOBS(NH1)= obs_bodyElem_r(lobsSpaceData,OBS_VAR,INDEX_BODY)
C     *              Reference value:
                     IF (LEVELGPSRO.EQ.1) THEN
                        ZREF(NH1) = 0.025d0*exp(-(H(NH1)-Rad)/6500.d0)
                     ELSE
                        ZREF(NH1) = 300.d0*exp(-H(NH1)/6500.d0)
                     ENDIF
                  ENDIF
               ENDDO BODY_2
C
C     *        Apply the observation operator:
C
               IF (LEVELGPSRO.EQ.1) THEN
                  CALL GPSBNDOPV1(H, AZMV, NH, PRF, RSTV)
               ELSE
                  CALL GPSREFOPV (H,       NH, PRF, RSTV)
               ENDIF
C
C     *        Perform the (H(x)-Y)/R operation:
C
               DO NH1 = 1, NH
                  ZMHX(NH1) = RSTV(NH1)%VAR
C
C     *           Normalized offset:
C
                  IF ( NUMGPSSATS .GE. 1 ) THEN
                    ZOFF(NH1) = (ZOBS(NH1) - ZMHX(NH1)) / ZREF(NH1)
                  ELSE
                    ZOFF(NH1) = (ZOBS(NH1) - ZMHX(NH1)) / ZMHX(NH1)
                  ENDIF
               ENDDO
C
C     *        The procedure below is well tested to collectively
C     *        create error profiles from the data profile, and
C     *        intended to be used for these data.
C
               DH = 5000.d0
               IF (LEVELGPSRO.EQ.1) THEN
                  ZMIN=0.01D0
               ELSE
                  ZMIN=0.002D0
               ENDIF
               
               IF (LEVELGPSRO.EQ.2) THEN
                  DO NH1 = 1, NH
                     SUM0=0.d0
                     SUM1=0.d0
                     DO JH = 1, NH
                        DDH=H(JH)-H(NH1)
                        SUM0=SUM0+EXP(-(DDH/DH)**2)
                        SUM1=SUM1+EXP(-(DDH/DH)**2)*ZOFF(JH)**2
                     ENDDO
                     ZERR(NH1)=(SUM1/SUM0)**0.5D0
                     IF ( NUMGPSSATS .GE. 1 ) THEN
                       IF (ISAT.EQ.3 .OR. ISAT.EQ.4) ZERR(NH1) = 2*ZERR(NH1)
                     ENDIF
                     IF ( ZERR(NH1) < ZMIN ) ZERR(NH1) = ZMIN
                  ENDDO
               ELSE
                  DO NH1 = 1, NH
                     ZERR(NH1)=0.05d0
                     HNH1=H(NH1)-Rad
                     L1=( HNH1.LE.10000.d0 )
                     L2=( HNH1.GT.10000.d0 .AND. HNH1.LT.30000.d0 )
                     L3=( HNH1.GT.30000.d0 )
                     IF ( L1 ) ZERR(NH1)=0.02d0+0.08d0*(10000.d0-HNH1)/10000.d0
                     IF ( L2 ) ZERR(NH1)=0.02d0
                     IF ( L3 ) ZERR(NH1)=0.02d0+0.13d0*(HNH1-30000.d0)/30000.d0
                     IF (ISAT.EQ.3 .OR. ISAT.EQ.4 .OR. ISAT.EQ.5) ZERR(NH1) = 2*ZERR(NH1)
                     IF ( ZERR(NH1) < ZMIN ) ZERR(NH1) = ZMIN
                  ENDDO
               ENDIF

               NH1 = 0
C
C     *        Loop over all body indices for this index_header:
C     *        (start at the beginning of the list)
C
               call obs_set_current_body_list(lobsSpaceData, index_header)
               BODY_4: do 
                  index_body = obs_getBodyIndex(lobsSpaceData)
                  if (index_body < 0) exit BODY_4
                  IF ( obs_bodyElem_i(lobsSpaceData,OBS_ASS,INDEX_BODY).EQ.1 ) THEN
                     NH1 = NH1 + 1
C
C     *              Observation error    S
C
                     IF ( NUMGPSSATS .GE. 1 ) THEN
                       call obs_bodySet_r(lobsSpaceData,OBS_OER,index_body, ZERR(NH1) * ZREF(NH1))
                     ELSE
                       call obs_bodySet_r(lobsSpaceData,OBS_OER,index_body, ZERR(NH1) * ZMHX(NH1))
                     ENDIF
                  ENDIF
               ENDDO BODY_4
            ENDIF
         ENDIF
      ENDDO HEADER

      deallocate( RSTV )
      deallocate( ZERR )
      deallocate( ZOFF )
      deallocate( ZREF )
      deallocate( ZOBS )
      deallocate( AZMV )
      deallocate( H    )
      deallocate( ZMHX )

      deallocate(zVV)
      deallocate(zUU)
      deallocate(zHU)
      deallocate(zTT)
      deallocate(zDP)
      deallocate(zPP)

      WRITE(*,*)'EXIT SETERRGPSRO'
      RETURN
      END SUBROUTINE SETERRGPSRO