module modgps06gravity 11,2
#if defined (DOC)
  !
  ! Josep M. Aparicio
  ! Meteorological Service of Canada, 2003.
  !
#endif
  use modgps00base      , only : i4, dp
  use modgps02wgs84const, only : WGS_a, WGS_f, WGS_m,                      &
       WGS_TNGk, WGS_e2, WGS_GammaE
  implicit none
  
  !private gpsgravitysurf

contains

  !  Normal gravity on ellipsoidal surface:
  !  Input:  Latitude
  !          Latitude                                     : rad
  !
  !  Output: Normal gravity
  !          gpsgravitysurf                               : m/s2
  !

  function gpsgravitysurf(Latitude) 3
    real(dp), intent(in)  :: Latitude
    real(dp)              :: gpsgravitysurf
    
    real(dp)              :: ks2
    real(dp)              :: e2s

    ks2 = WGS_TNGk * sin(Latitude)**2
    e2s = 1._dp - WGS_e2 * sin(Latitude)**2
    gpsgravitysurf = WGS_GammaE * (1._dp + ks2) / sqrt(e2s)
  end function gpsgravitysurf

  ! Normal gravity above the ellipsoidal surface:
  ! Input:  Latitude, altitude
  !         Latitude                                     : rad
  !         Altitude                                     : m
  !
  ! Output: Normal gravity
  !         gpsgravityabove                              : m/s2
  !

  function gpsgravityabove(Latitude, Altitude) 2,1
    real(dp), intent(in)  :: Latitude
    real(dp), intent(in)  :: Altitude
    real(dp)              :: gpsgravityabove

    real(dp)              :: C1
    real(dp)              :: C2

    C1 =-2._dp/WGS_a*(1._dp+WGS_f+WGS_m-2*WGS_f*sin(Latitude)**2)
    C2 = 3._dp/WGS_a**2
    gpsgravityabove = gpsgravitysurf(Latitude)*                            &
         (1._dp + C1 * Altitude + C2 * Altitude**2)
  end function gpsgravityabove


  function gpsgravitydiffabove(Latitude, Altitude),1
    real(dp), intent(in)  :: Latitude
    real(dp), intent(in)  :: Altitude
    real(dp)              :: gpsgravitydiffabove

    real(dp)              :: C1
    real(dp)              :: C2

    C1 =-2._dp/WGS_a*(1._dp+WGS_f+WGS_m-2*WGS_f*sin(Latitude)**2)
    C2 = 3._dp/WGS_a**2
    gpsgravitydiffabove = gpsgravitysurf(Latitude)*                        &
         (C1 + 2 * C2 * Altitude)
  end function gpsgravitydiffabove

  ! Geopotential energy at a given point.
  ! Result is based on the WGS84 approximate expression for the
  ! gravity acceleration as a function of latitude and altitude,
  ! integrated with the trapezoidal rule.
  ! Input:  Latitude, altitude
  !         Latitude                                     : rad
  !         Altitude                                     : m
  !
  ! Output: Geopotential
  !         gpsgeopotential                              : m2/s2

  function gpsgeopotential(Latitude, Altitude) 15,1
    real(dp), intent(in)  :: Latitude
    real(dp), intent(in)  :: Altitude
    real(dp)              :: gpsgeopotential

    real(dp)              :: dh
    real(dp)              :: geop
    integer               :: n, i
    real(dp), allocatable :: hi(:)
    real(dp), allocatable :: gi(:)
    
    dh = 500._dp
    n = 1 + int(Altitude/dh)

    allocate(hi(0:n))
    allocate(gi(0:n))

    do i = 0, n
       hi(i) = i * dh
       if (i.eq.n) hi(i) = Altitude
       gi(i) = gpsgravityabove(Latitude, hi(i))
    enddo

    gpsgeopotential = 0._dp
    do i = 1, n
       gpsgeopotential = gpsgeopotential + 0.5_dp * (gi(i)+gi(i-1)) * (hi(i)-hi(i-1))
    enddo

    deallocate(hi)
    deallocate(gi)
  end function gpsgeopotential

end module modgps06gravity