SUBROUTINE barnes_cntrl(nx,ny,mxstn,nvar,maxrng,nobs,x,y,               & 2,4
           obs,odiff,obanx,xsta,ysta,isrc,qobs,rely,                    &
           nm_var,npass,kappa,irngsel,range,rngobs,                     &
           wlim,klim,knt,rngwgt,wgtsum,zsum,istat,                      &
           anx,istatus)
!
!  Routine to control Barnes analysis looping.
!  For each pass it calls routines to analyze at grid points,
!  analyze at obs points, computes new obs differences and
!  reports rms statistics.
!
!  Keith Brewster, CAPS
!  March, 1994
!
  IMPLICIT NONE
  INTEGER :: nx,ny,mxstn,nvar,maxrng
  INTEGER :: istat
!
!  Grid arrays
!
  REAL :: x(nx),y(ny)
  REAL :: anx(nx,ny,nvar)
!
!  Observation arrays
!
  INTEGER :: nobs
  REAL :: obs(mxstn,nvar)
  REAL :: odiff(mxstn,nvar)
  REAL :: obanx(mxstn,nvar)
  REAL :: xsta(mxstn),ysta(mxstn)
  REAL :: qobs(mxstn,nvar)
  REAL :: rngobs(mxstn,maxrng)
  INTEGER :: isrc(mxstn)
  INTEGER :: rely(mxstn,nvar)
  CHARACTER (LEN=6) :: nm_var(nvar)
!
!  Barnes control variables
!
  INTEGER :: npass
  INTEGER :: irngsel(nvar)
  REAL :: kappa(npass)
  REAL :: range(nx,ny,maxrng)
  REAL :: wlim
  INTEGER :: klim(npass)
!
!  Barnes scratch space
!
  INTEGER :: knt(nvar)
  REAL :: rngwgt(maxrng)
  REAL :: wgtsum(nvar)
  REAL :: zsum(nvar)
!
  INTEGER :: istatus
!
!  Misc internal variables
!
  INTEGER :: i,j,k,ipass,ista,irng
  INTEGER :: iloc,jloc
  REAL :: delx,dely,rpass
!
!  Initialize rngobs, the appropriate analysis scale range at
!  each observation point.  It is important that this be
!  consistant with the ranges assigned at the grid points.
!  Bilinear interpolation is used rather than an independent
!  calculation.  Obs outside the domain use the nearest grid
!  point in the domain.
!
  delx=x(2)-x(1)
  dely=y(2)-y(1)
  DO ista=1,nobs
    iloc=INT((xsta(ista)-x(1))/delx) + 1
    iloc=MIN(iloc,nx-1)
    iloc=MAX(iloc,1)
    jloc=INT((ysta(ista)-y(1))/dely) + 1
    jloc=MIN(jloc,ny-1)
    jloc=MAX(jloc,1)
    DO irng=1,maxrng
      CALL bilin1(nx,ny,1,xsta(ista),ysta(ista),iloc,jloc,              &
                x,y,range(1,1,irng),rngobs(ista,irng))
    END DO
  END DO
!
!  Establish initial condition.   Analysis is zero and
!  ob differences are equal to obs.
!
  DO k=1,nvar
    DO j=1,ny
      DO i=1,nx
        anx(i,j,k)=0.
      END DO
    END DO
  END DO
!
  DO k=1,nvar
    DO i=1,mxstn
      obanx(i,k)=0.
      odiff(i,k)=obs(i,k)
    END DO
  END DO
!
  DO ipass=1,npass
!
    rpass=kappa(ipass)*kappa(ipass)
!
    CALL bargrid(nx,ny,mxstn,nvar,maxrng,x,y,                           &
                   odiff,xsta,ysta,qobs,isrc,nobs,                      &
                   knt,rngwgt,wgtsum,zsum,                              &
                   rpass,irngsel,range,wlim,klim(ipass),                &
                   anx,istatus)
!
    CALL barobs(mxstn,nvar,maxrng,                                      &
                  odiff,xsta,ysta,qobs,isrc,nobs,                       &
                  knt,rngwgt,wgtsum,zsum,                               &
                  rpass,irngsel,rngobs,wlim,klim(ipass),                &
                  obanx,istatus)
!
    CALL diffnstat(mxstn,nvar,nobs,obs,isrc,obanx,odiff,                &
                       knt,wgtsum,zsum)
!
    WRITE(6,820) ipass
    820   FORMAT('  Statistics for Barnes pass ',i4,/                   &
                 '  ivar  name    knt      bias        rms')
    DO k=1,nvar
      WRITE(6,822) k,nm_var(k),knt(k),wgtsum(k),zsum(k)
      822     FORMAT(i6,2X,a6,i8,f10.3,f10.3)
    END DO
    IF(istat > 0) THEN
      WRITE(istat,820) ipass
      DO k=1,nvar
        WRITE(istat,822) k,nm_var(k),knt(k),wgtsum(k),zsum(k)
      END DO
    END IF
!
  END DO
  istatus=0
  RETURN
END SUBROUTINE barnes_cntrl
!


SUBROUTINE diffnstat(mxstn,nvar,nobs,obs,isrc,obanx,odiff,              & 1
           knt,dmean,drms)
!
!  Routine to calculate new observation differences
!  and report statistics on the differences.
!
!  Keith Brewster, CAPS
!  March, 1994
!
  IMPLICIT NONE
  INTEGER :: mxstn,nvar
!
  REAL :: obs(mxstn,nvar)
  INTEGER :: isrc(mxstn)
  REAL :: obanx(mxstn,nvar)
  REAL :: odiff(mxstn,nvar)
  INTEGER :: nobs
  INTEGER :: knt(nvar)
  REAL :: dmean(nvar)
  REAL :: drms(nvar)
!
!  Misc internal variables
!
  INTEGER :: i,k
  REAL :: flknt
!
  DO k=1,nvar
    knt(k)=0
    dmean(k)=0.
    drms(k)=0.
  END DO
!
  DO i=1,nobs
    IF(isrc(i) /= 0) THEN
      DO k=1,nvar
        IF(obs(i,k) > -90.) THEN
          odiff(i,k)=obs(i,k)-obanx(i,k)
          knt(k)=knt(k)+1
          dmean(k)=dmean(k)+odiff(i,k)
          drms(k)=drms(k)+odiff(i,k)*odiff(i,k)
        END IF
      END DO
    END IF
  END DO
!
!  Calculate stats from the sums formed above
!
  DO k=1,nvar
    IF(knt(k) > 0) THEN
      flknt=FLOAT(knt(k))
      dmean(k)=dmean(k)/flknt
      drms(k)=SQRT(drms(k)/flknt)
    ELSE
      dmean(k)=0.
      drms(k)=0.
    END IF
  END DO
!
  RETURN
END SUBROUTINE diffnstat


SUBROUTINE barqc(maxsta,nvar,nsrc,nsta,z,zdiff,                         & 1,1
           obstime,xsta,ysta,isrc,                                      &
           knt,wgtsum,zsum,sqsum,                                       &
           range,wlim,klim,qclim,varnam,snam,                           &
           iqclist,iqcsave,                                             &
           flagz,flagd)
!
!   Routine to objectively quality control data using a
!   Barnes analysis of nearby stations.
!
!   If an observations is found to be bad it is set to missing flag
!   flagz, and the corresponding difference variable is set to flagz.
!
!   Keith Brewster, April, 1991
!   Based on Barnes analysis    Keith Brewster, March, 1989
!
  IMPLICIT NONE
!
!  Observation Arguments
!
  INTEGER :: maxsta,nvar,nsrc
  REAL :: z(maxsta,nvar),     & ! original variables
   zdiff(maxsta,nvar)      ! variable to analyze
  INTEGER :: obstime(maxsta)
  REAL :: xsta(maxsta),ysta(maxsta)  ! x and y locations of stations
  INTEGER :: isrc(maxsta)
  INTEGER :: nsta
!
!  Scratch space
!
  INTEGER :: knt(nvar)
  REAL :: wgtsum(nvar),zsum(nvar),sqsum(nvar)
!
!  File pointers
!
  INTEGER :: iqclist,iqcsave
!
!  Analysis specification arguments
!
!  INTEGER iwgt
  REAL :: range,    & ! e-folding range km**2 of barnes weight
   wlim      ! limit of weight to set max range
  INTEGER :: klim   ! minimum # of stations to influence grid pt
  REAL :: qclim(nsrc,nvar)   ! QC cutoffs
!
!  Diagnostics and other argument variables
!
  CHARACTER (LEN=6) :: varnam(nvar)
  CHARACTER (LEN=5) :: snam(maxsta)
  REAL :: flagz,flagd
!
!  Misc internal variables
!
  REAL :: rlimsq,dxsta,dysta,dist,                                      &
       wgt,zanx,dob,sqanx,chklim
  INTEGER :: jsta,ksta,ivar
  LOGICAL :: listit,saveit
!
!  Set printing logicals
!
  listit=(iqclist > 0)
  saveit=(iqcsave > 0)
!
!  Based on the minimum weight to consider, set
!  a maximum range
!
  rlimsq=-range*ALOG(wlim)
!
!  print *, ' Analyzing...'
!  print *, '    rlim= ', rlim
!  print *, '    range = ',range
!
!   Uses Barnes weighting function.  see variable wgt.
!
!   Ksta is location that is being examined
!
  DO ksta=nsta,1,-1
    DO ivar=1,nvar
      zsum(ivar)=0.
      sqsum(ivar)=0.
      wgtsum(ivar)=0.
      knt(ivar)=0
    END DO
    DO jsta=1,nsta
      IF(jsta /= ksta) THEN
        dxsta=xsta(ksta)-xsta(jsta)
        dysta=ysta(ksta)-ysta(jsta)
        dist=dxsta*dxsta + dysta*dysta
        IF(dist < rlimsq) THEN
          wgt=EXP(-dist/range)
          DO ivar=1,nvar
            IF(zdiff(jsta,ivar) > flagd) THEN
              knt(ivar)=knt(ivar)+1
              wgtsum(ivar)=wgtsum(ivar)+wgt
              zsum(ivar)=zsum(ivar)+wgt*zdiff(jsta,ivar)
              sqsum(ivar)=sqsum(ivar)+                                  &
                   wgt*zdiff(jsta,ivar)*zdiff(jsta,ivar)
            END IF
          END DO
        END IF
      END IF  ! dont use current station
    END DO
    DO ivar=1,nvar
      IF(zdiff(ksta,ivar) > flagd) THEN
        IF(knt(ivar) > klim) THEN
          sqanx=sqsum(ivar)-                                            &
                (zsum(ivar)*zsum(ivar)/wgtsum(ivar))
          sqanx=AMAX1(sqanx,0.)
          sqanx=3.0*SQRT(sqanx/wgtsum(ivar))
          zanx=zsum(ivar)/wgtsum(ivar)
          dob=zdiff(ksta,ivar)-zanx
          chklim=AMAX1(sqanx,qclim(isrc(ksta),ivar))
          IF(ABS(dob) > chklim) THEN
            PRINT *, ' QC flagging data...sta=',snam(ksta)
            PRINT *, '     var, ob =',                                  &
                        varnam(ivar),z(ksta,ivar)
            PRINT *, '     delta ob =',dob
            PRINT *, '  3.0*stdv,qclimit=',sqanx,                       &
                                 qclim(isrc(ksta),ivar)
            IF(listit) WRITE(iqclist,810)                               &
                snam(ksta),obstime(ksta),ivar,varnam(ivar),             &
                z(ksta,ivar),dob,                                       &
                sqanx,qclim(isrc(ksta),ivar)
            810           FORMAT(2X,a5,i5,2X,i3,1X,a6,f11.2,f11.2,f11.2,f11.2)
            z(ksta,ivar)=flagz
            zdiff(ksta,ivar)=flagd
          END IF
          zsum(ivar)=dob
        ELSE
!          print *, ' Not enuf data to check ivar= ',
!    +                   ivar,' for ',snam(ksta)
          zsum(ivar)=-999.
        END IF
      ELSE
        zsum(ivar)=-999.
      END IF  ! data missing, dont bother checking
    END DO
    IF(saveit) WRITE(iqcsave,820) snam(ksta),obstime(ksta),             &
              (zdiff(ksta,ivar),ivar=1,nvar)
    820   FORMAT(2X,a4,i6,2X,15(f7.2))
  END DO
  RETURN
END SUBROUTINE barqc


SUBROUTINE barobs(mxstn,nvar,maxrng,                                    & 1
           odiff,xsta,ysta,qobs,isrc,nobs,                              &
           knt,rngwgt,wgtsum,zsum,                                      &
           rpass,irngsel,rngobs,wlim,klim,                              &
           obanx,istatus)
!
!  Routine to objectively analyze data at the observation locations
!  Purpose is to create an analysis parallel to the gridded analysis
!  for obtaining a new observation difference for the next pass.
!  Barnes weight function is employed.
!
!  Keith Brewster, March, 1989
!  Streamlined to remove unneeded options, kb, April, 1990
!  Modified for use in OLAPS  Keith Brewster, March, 1994
!
  IMPLICIT NONE
!
!  Observation Arguments
!
  INTEGER :: mxstn,nvar,maxrng
  REAL :: odiff(mxstn,nvar),             & ! variable to analyse
   xsta(mxstn),ysta(mxstn),  & ! x and y locations of stations
   qobs(mxstn,nvar)
  INTEGER :: isrc(mxstn)
  INTEGER :: nobs
!
!    Scratch space
!
  INTEGER :: knt(nvar)
  REAL :: rngwgt(maxrng)
  REAL :: wgtsum(nvar),zsum(nvar)
!
!  Input/Output analysis at observation locations.Grid arguments
!
  REAL :: obanx(mxstn,nvar)    ! analysed data --  Input and OUTPUT
  INTEGER :: istatus
!
!  Analysis specification arguments
!
!  integer iwgt
  INTEGER :: irngsel(nvar)
  REAL :: rpass,                                                        &
       rngobs(mxstn,maxrng),    & ! e-folding range km**2 of barnes weight
   wlim      ! limit in km of station influence
  INTEGER :: klim   ! minimum # of stations to influence grid pt
!
!  Misc internal variables
!
  INTEGER :: ista,jsta,ivar,irng
  REAL :: denom,rlimsq,dxsta,dysta,dist,wgt,rmax
!  real wvar
!
!  print *, ' Analyzing...'
!  print *, '    rlim= ', rlim
!  print *, '    range = ',range
!
!   Uses Barnes weighting function.  see variable wgt.
!
  DO ista=1,nobs
    IF(isrc(ista) /= 0) THEN
      rmax=rngobs(ista,1)
      DO irng=1,maxrng
        rmax=AMAX1(rmax,rngobs(ista,irng))
      END DO
      denom=rpass*rmax
      rlimsq=-denom*ALOG(wlim)
      DO ivar=1,nvar
        zsum(ivar)=0.
        wgtsum(ivar)=0.
        knt(ivar)=0
      END DO
      DO jsta=1,nobs
        IF(isrc(jsta) /= 0) THEN
          dxsta=xsta(ista)-xsta(jsta)
          dysta=ysta(ista)-ysta(jsta)
          dist=dxsta*dxsta + dysta*dysta
          IF(dist < rlimsq) THEN
            DO irng=1,maxrng
              rngwgt(irng)=                                             &
                     EXP(-dist/(rpass*rngobs(ista,irng)))
            END DO
            DO ivar=1,nvar
              wgt=rngwgt(irngsel(ivar))
              IF(odiff(jsta,ivar) > -90.) THEN
                knt(ivar)=knt(ivar)+1
!                wvar=wgt/qobs(jsta,ivar)
                wgtsum(ivar)=wgtsum(ivar)+wgt
                zsum(ivar)=zsum(ivar)+wgt*odiff(jsta,ivar)
              END IF
            END DO
          END IF
        END IF
      END DO
      DO ivar=1,nvar
        IF(knt(ivar) > klim)                                            &
            obanx(ista,ivar)=obanx(ista,ivar)+zsum(ivar)/wgtsum(ivar)
      END DO
    END IF  ! valid station?
  END DO
  istatus=0
  RETURN
END SUBROUTINE barobs


SUBROUTINE bargrid(nx,ny,mxstn,nvar,maxrng,x,y,                         & 1
           obs,xsta,ysta,qobs,isrc,nobs,                                &
           knt,rngwgt,wgtsum,zsum,                                      &
           rpass,irngsel,range,wlim,klim,                               &
           anx,istatus)
!
!   Routine to objectively analyze data on a regular grid specified
!   by x and y.  Barnes weight function is employed.
!
!   Keith Brewster, March, 1989
!   Streamlined to remove unneeded options, kb, April, 1990
!   Modified for use in OLAPS  Keith Brewster, March, 1994
!
  IMPLICIT NONE
!
!  Observation Arguments
!
  INTEGER :: mxstn,nvar,maxrng
  REAL :: obs(mxstn,nvar),             & ! variable to analyse
   xsta(mxstn),ysta(mxstn),  & ! x and y locations of stations
   qobs(mxstn,nvar)
  INTEGER :: isrc(mxstn)
  INTEGER :: nobs
!
!  Scratch space
!
  INTEGER :: knt(nvar)
  REAL :: rngwgt(maxrng)
  REAL :: wgtsum(nvar),zsum(nvar)
!
!  Grid arguments
!
  INTEGER :: nx,ny
  REAL :: x(nx),y(ny)        ! x and y locations of grid points
  REAL :: anx(nx,ny,nvar)    ! analysed grid --  Input and OUTPUT
  INTEGER :: istatus
!
!  Analysis specification arguments
!
!  integer iwgt
  INTEGER :: irngsel(nvar)
  REAL :: rpass,                                                        &
       range(nx,ny,maxrng),  & ! e-folding range km**2 of barnes weight
   wlim                  ! limit in km of station influence
  INTEGER :: klim   ! minimum # of stations to influence grid pt
!
!  Misc internal variables
!
  REAL :: rlimsq,denom,dxsta,dysta,dist,wgt,rmax
  INTEGER :: i,j,jsta,ivar,irng
!
!   Uses Barnes weighting function.  see variable wgt.
!
  DO j=1,ny
    DO i=1,nx
      rmax=range(i,j,1)
      DO irng=1,maxrng
        rmax=AMAX1(rmax,range(i,j,irng))
      END DO
      denom=rpass*rmax
      rlimsq=-denom*ALOG(wlim)
      DO ivar=1,nvar
        zsum(ivar)=0.
        wgtsum(ivar)=0.
        knt(ivar)=0
      END DO
      DO jsta=1,nobs
        IF(isrc(jsta) /= 0) THEN
          dxsta=x(i)-xsta(jsta)
          dysta=y(j)-ysta(jsta)
          dist=dxsta*dxsta + dysta*dysta
          IF(dist < rlimsq) THEN
            DO irng=1,maxrng
              rngwgt(irng)=EXP(-dist/(rpass*range(i,j,irng)))
            END DO
            DO ivar=1,nvar
              wgt=rngwgt(irngsel(ivar))
              IF(obs(jsta,ivar) > -90.) THEN
                knt(ivar)=knt(ivar)+1
                wgtsum(ivar)=wgtsum(ivar)+wgt
                zsum(ivar)=zsum(ivar)+wgt*obs(jsta,ivar)
              END IF
            END DO
          END IF
        END IF
      END DO
      DO ivar=1,nvar
        IF(knt(ivar) > klim) anx(i,j,ivar)=anx(i,j,ivar)+zsum(ivar)/wgtsum(ivar)
      END DO
    END DO
  END DO
  istatus=0
  RETURN
END SUBROUTINE bargrid


SUBROUTINE bilin1(nx,ny,nvar,xpos,ypos,i,j,x,y,var,varint) 1
!
!  Does bilinear interpolation of variables (nvar variables)
!  to position (xpos,ypos) given a grid of variables "var", their
!  x and y positions of the grid (x,y) and the index of the
!  grid point (i,j) which is to the "lower-left" of xpos and ypos.
!  That means (xpos,ypos) is between (i,j) and (i+1,j+1).
!
!  A saftey feature in case xpos and ypos are outside the grid
!  is built-in.
!
  IMPLICIT NONE
!
!  Arguments, input
!
  INTEGER :: nx,ny,nvar
  REAL :: var(nx,ny,nvar)
  REAL :: x(nx),y(ny),xpos,ypos
  INTEGER :: i,j
!
!  Arguments output
!
  REAL :: varint(nvar)
!
!  Misc Internal Variables
!
  REAL :: xtem,ytem,dxpos,dypos,dx,dy
  REAL :: w11,w12,w21,w22
  INTEGER :: ivar,ip1,jp1
!
  xtem=AMAX1(xpos,x(1))
  xtem=AMIN1(xtem,x(nx))
  ytem=AMAX1(ypos,y(1))
  ytem=AMIN1(ytem,y(ny))
  ip1=i+1
  jp1=j+1
  dx=x(ip1)-x(i)
  dy=y(jp1)-y(j)
  dxpos=xtem-x(i)
  dypos=ytem-y(j)
  IF((dx /= 0.) .AND. (dy /= 0.))THEN
    dx=dxpos/dx
    dy=dypos/dy
    w11=(1.-dx)*(1.-dy)
    w21=dx*(1.-dy)
    w12=dy*(1.-dx)
    w22=dx*dy
  ELSE IF(dx /= 0.) THEN
    dx=dxpos/dx
    w11=1.-dx
    w21=dx
    w12=0.
    w22=0.
  ELSE
    dy=dypos/dy
    w11=1.-dy
    w21=0.
    w12=dy
    w22=0.
  END IF
!
  DO ivar=1,nvar
    varint(ivar)=var(  i,  j,ivar)*w11                                  &
                +var(ip1,  j,ivar)*w21                                  &
                +var(  i,jp1,ivar)*w12                                  &
                +var(ip1,jp1,ivar)*w22
  END DO
  RETURN
END SUBROUTINE bilin1