subroutine mira_chopper(search,fixOpacity,opac,fixPWV,pwv0,tel,nfreq,freqs, &
&                       loads,recs,atms,tcals,tsyss,quiet,errors)
!
! Called by MIRA subroutine do_tcal (in mira/lib/calibrate.f90).
! Replaces telcal's subroutine chopper. Calls gildas interface
! to ATM at packages/atm (which in turn calls legacy/atm/lib).
!
!
  use chopper_def
  type (chop_mode), intent(in)    :: search        ! search criteria
  type (telescope), intent(in)    :: tel           ! telescope data
  integer,          intent(in)    :: nfreq         ! number of frequency points
  type (dsb_value), intent(in)    :: freqs(nfreq)  ! signa/image frequencies
  type (chop_meas), intent(in)    :: loads(nfreq)  ! cal counts (sky,hot,cold)
  type (mixer),     intent(inout) :: recs(nfreq)   ! rx efficiencies and gains
  type (atm_prop),  intent(inout) :: atms(nfreq)   ! atmspheric data
  type (dsb_value), intent(out)   :: tcals(nfreq)  ! calibration temperatures
  type (dsb_value), intent(out)   :: tsyss(nfreq)  ! system temperatures
  logical,          intent(out)   :: errors(nfreq) ! error flags
  logical,          intent(in)    :: quiet         ! suppress verbose output
  logical,          intent(in)    :: fixOpacity    ! if fixOpacity: no iterations, but
                                                   ! get Tcal,Tsys for fixed opacity
  real(8),          intent(in)    :: opac          ! value of that opacity
  logical,          intent(in)    :: fixPWV        ! if fixPWD: no iterations, but
                                                   ! get Tcal,Tsys, and taus for fixed pwv 
  real(8),          intent(in)    :: pwv0          ! value of that pwv 

  !
  ! Local variables
  type (dsb_value) :: tcal,tsys,attenuation
  type (chop_load) :: cold,hot
  logical :: error
  integer :: ifreq,ier
  real(8) :: dark_count,sky_count,mira_airmass,tsky0,tsky,temi
  !
  ! variables for interface to mnbrak & brent (numerical recipes)
  !
  integer kiter
  real(8)            :: wLower,wMin,wResult,wUpper,fa,fb,fc,chiSquared
  real(8)            :: brent,chiSquaredAtm,chiSquaredTau,tbg0
  real(8), parameter :: tol = 1.d-6, tbg = 2.73
  real(8), parameter :: hp = 6.62620d-27, kb = 1.38062d-16, &
 &                      clight = 2.9979247d+10    
  real(8), parameter :: hoverk = hp/kb*1d9 !! in K/GHz
  real(8), parameter :: planck = 2.*hp*1.d27/clight**2  ! in c.g.s / GHz^3
  external chiSquaredAtm, chiSquaredTau
  !
  character (len=4)  :: atmVersion
  character (len=34) :: mess(3)
  !
  data mess &
       /'zero atmospheric opacity', &
        'no oxygen in atmosphere', &
        'precipitable water vapour very low'/
  !
  errors = .false.
  call sic_get_char('atmVersion',atmVersion,ier,error)
  call atm_setup(atmVersion,error)
  if (error) then
    if (.not.quiet) call message(8,3,'ATM',                       &
                    'ATM version '//atmVersion//' does not exist.')
    errors = .true.
    return
  endif
  !
  atms%airmass = mira_airmass(tel%elev,quiet,error)
  if (error) then
     if (.not.quiet) call message (8,3,'CHOPPER','Elevation is not available')
     errors = .true.
     return
  endif
  !
  if (fixOpacity) tau0 = opac
  !
  call atm_atmosp(real(tel%tout),real(tel%pres),real(tel%alti))
  !
  do ifreq = 1, nfreq
     !
     error = .false.
     !
     rec  = recs(ifreq)
     atm  = atms(ifreq)
     freq = freqs(ifreq)
     tcal = tcals(ifreq)
     tsys = tsyss(ifreq)
     !
     dark_count = loads(ifreq)%dark_count
     sky_count  = loads(ifreq)%sky_count
     hot        = loads(ifreq)%hot
     cold       = loads(ifreq)%cold
     !
     ! Sanity checks
     if (sky_count.ge.hot%count) then
        if (.not.quiet) call message (8,3,'CHOPPER','Signal stronger on SKY than on HOT load => Bad atmosphere')
        error = .true.
        return
     endif
     if (cold%count.ge.hot%count) then
        if (.not.quiet) call message (8,3,'CHOPPER','Signal stronger on COLD than on HOT load')
        error = .true.
        return
     endif
     !
     ! Initialize the ATM status
     ier = 0
     !
     ! Search for water vapor content when asked
     if (search%water.and..not.error) then
        !
        ! Local estimation Trec if needed
        ! This computation does *not* take the cold and hot coupling into account.
        !
        tsky0 = ((hot%ceff*hot%temp + rec%temp) * sky_count        &
     &          / hot%count - rec%temp)                           &
     &        / (1. - sky_count * (1.-hot%ceff) / hot%count)
        temi0 = (tsky0 - (1.-rec%feff) * tel%tcab) / rec%feff
        !
        atm%h2omm = 1.d-10 
        call mira_atm_dsb_transmission(tel,ier)
        temi = (atm%temi%s + atm%temi%i*rec%sbgr) / (1.+rec%sbgr)
        if (temi.lt.temi0) then
           telNumRec = tel
           wLower = 10.d0
           wMin = 9.9d0
           call mnbrak(wLower,wMin,wUpper,fa,fb,fc,chiSquaredAtm)
           chiSquared = brent(wLower,wMin,wUpper,chiSquaredAtm,tol,wResult,error)
           if (error) then
              errors = .true.
              return
           endif
           atm%h2omm = wResult
        endif
        !
     else if (fixOpacity) then
        telNumRec = tel
        wLower = 10.d0
        wMin = 9.9d0
        call mnbrak(wLower,wMin,wUpper,fa,fb,fc,chiSquaredTau)
        chiSquared = brent(wLower,wMin,wUpper,chiSquaredTau,tol,wResult,error)
        atm%h2omm = wResult
        tsky0 = ((hot%ceff*hot%temp + rec%temp) * sky_count        &
     &          / hot%count - rec%temp)                           &
     &        / (1. - sky_count * (1.-hot%ceff) / hot%count)
        temi0 = (tsky0 - (1.-rec%feff) * tel%tcab) / rec%feff
        !
     else if (fixPWV) then
        atm%h2omm = pwv0 
        tsky0 = ((hot%ceff*hot%temp + rec%temp) * sky_count        &
     &          / hot%count - rec%temp)                           &
     &        / (1. - sky_count * (1.-hot%ceff) / hot%count)
        temi0 = (tsky0 - (1.-rec%feff) * tel%tcab) / rec%feff
        !
     endif
     !
     ! Compute atmosphere transmission when asked or needed
     if ((search%atm.or.search%water).and.(.not.error)) then
        ! Atmospheric parameters are recomputed from water, airmass and frequency
        !   => The water vapor content is assumed to be known
        call mira_atm_dsb_transmission(tel,ier)
        if (atm%h2omm.eq.1.d-10) ier = 3
        if (ier.eq.3.and..not.quiet) then
           call gagout('W-CHOPPER: '//mess(ier))
        else if (ier.ne.0.and..not.quiet) then
           call gagout('E-CHOPPER: '//mess(ier))
           error = .true.
        endif        
     else
        ! Atmospheric parameters are assumed to be known
        atm%temi%s = atm%temp%s*(1.-exp(-atm%taus%tot*atm%airmass))
        atm%temi%i = atm%temp%i*(1.-exp(-atm%taui%tot*atm%airmass))
     endif
     !
     ! Compute sky temperatures at signal and image frequency
     temi = (atm%temi%s + atm%temi%i*rec%sbgr) / (1.+rec%sbgr)
     !tsky = temi*rec%feff + tel%tout*(1.-rec%feff)
     tsky = temi*rec%feff + tel%tcab*(1.-rec%feff)
     !
     ! Compute Trec when asked
     if (search%trec.and.(.not.error)) then
        rec%temp = (tsky * (sky_count*(1-hot%ceff) - hot%count) + sky_count * hot%ceff*hot%temp) &
             / (hot%count-sky_count)
     endif
     !
     ! Compute Tcal at zenith when asked
     if (search%tcal.and.(.not.error)) then
        tcal%s = (hot%temp-tsky0)*(1.+rec%sbgr) / rec%beff*hot%ceff
        tcal%i = (hot%temp-tsky0)*(1.+1./rec%sbgr) / rec%beff*hot%ceff
     endif
     !
     ! Compute Tsys at current elevation when asked
     if (search%tsys.and.(.not.error)) then
        attenuation%s = exp(-atm%taus%tot*atm%airmass)
        attenuation%i = exp(-atm%taui%tot*atm%airmass)
        if (attenuation%s.ne.0) then
           tsys%s = tcal%s*sky_count / (hot%count-sky_count) / attenuation%s
        endif
        if (attenuation%i.ne.0) then
           tsys%i = tcal%i*sky_count / (hot%count-sky_count) / attenuation%i
        endif
        tsys%s = max(min(1d10,tsys%s),0.d0)
        tsys%i = max(min(1d10,tsys%i),0.d0)
     endif
     !
!print '(5(a,f7.4))',                   'chopper: rec%beff', rec%beff, ' rec%feff', rec%feff, ' rec%sbgr', rec%sbgr, ' rec%fOut', rec%fOut, ' rec%fCab', rec%fCab
!print '(a,f5.2,3(a,f10.2),2(a,f7.4))', 'chopper: dark_count', dark_count, ' sky_count', sky_count, ' hot%count', hot%count, ' cold%count', cold%count, ' hot%ceff', hot%ceff, ' cold%ceff', cold%ceff
!print '(2(a,f9.4),4(a,f9.3))',         'chopper: freq%s', freq%s, ' freq%i', freq%i, ' hot%temp', hot%temp, ' cold%temp', cold%temp, ' tCal%s', tCal%s, ' tCal%i', tCal%i
!write(2,'(5(a,f7.4))')                   'chopper: rec%beff', rec%beff, ' rec%feff', rec%feff, ' rec%sbgr', rec%sbgr, ' rec%fOut', rec%fOut, ' rec%fCab', rec%fCab
!write(2,'(a,f5.2,3(a,f10.2),2(a,f7.4))') 'chopper: dark_count', dark_count, ' sky_count', sky_count, ' hot%count', hot%count, ' cold%count', cold%count, ' hot%ceff', hot%ceff, ' cold%ceff', cold%ceff
!write(2,'(2(a,f9.4),4(a,f9.3))')         'chopper: freq%s', freq%s, ' freq%i', freq%i, ' hot%temp', hot%temp, ' cold%temp', cold%temp, ' tCal%s', tCal%s, ' tCal%i', tCal%i
     recs(ifreq)   = rec
     atms(ifreq)   = atm
     tcals(ifreq)  = tcal
     tsyss(ifreq)  = tsys
     errors(ifreq) = error
     !
  enddo
  !
  ! Should average water vapor contents here!...
  !
end subroutine mira_chopper
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
function mira_airmass(el,quiet,error)
  ! EXPORT
  !----------------------------------------------------------------------
  ! Compute airmass number corresponding to elevation EL
  ! Use a curved atmosphere, equivalent height 5.5 km
  !----------------------------------------------------------------------
  real(8), intent(in)  :: el      ! Elevation [radian]
  logical, intent(out) :: error   ! Error flag
  logical, intent(in)  :: quiet   ! suppresses error messages
  real(8)              :: mira_airmass ! Airmass
  !
  ! Local variables
  real(8) :: r0,h0,delev,eps,gamma,hz
  parameter (h0=5.5d0,r0=6370.d0)
  !
  error = .false.
  if (el.eq.0.) then
     if (.not.quiet) call message(6,2,'AIRMASS','Elevation is zero-valued')
     error = .true.
     return
  endif
  delev = el
  eps= asin(r0/(r0+h0)*cos(delev))
  gamma= delev + eps
  hz = r0*r0 + (r0+h0)**2 - 2.d0*r0*(r0+h0)*sin(gamma)
  hz = sqrt (max(h0**2,hz))
  hz = hz/h0
  hz = min (hz, 20.d0)
  mira_airmass = hz
end function mira_airmass
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mira_atm_dsb_transmission(tel,ier)
  ! EXPORT
  use atm_interfaces_public
  use chopper_def
  !----------------------------------------------------------------------
  !
  !----------------------------------------------------------------------
  type (telescope), intent(in)    :: tel  !
  integer,          intent(inout) :: ier  ! Error flag
  character(len=4)                :: atmVersion
  logical                         :: error
  !
  ! Local variables
  real :: temi,tatm,tau_wat,tau_oth,tau_tot
  !
  ! Signal frequency
  call sic_get_char('atmVersion',atmVersion,ier,error)
  call atm_setup(atmVersion,error)
  call atm_transm( & 
       real(atm%h2omm), &
       real(atm%airmass), &
       real(freq%s), &
       temi,tatm, &
       tau_oth,tau_wat,tau_tot, &
       ier)
  ! Implicit casting from real to double
  atm%temi%s = temi
  atm%temp%s = tatm
  atm%taus%wat = tau_wat
  atm%taus%oth = tau_oth
  atm%taus%tot = tau_tot
  !
  ! Image frequency
  call atm_transm( & 
       real(atm%h2omm), &
       real(atm%airmass), &
       real(freq%i), &
       temi,tatm, &
       tau_oth,tau_wat,tau_tot, &
       ier)
  ! Implicit casting from real to double
  atm%temi%i = temi
  atm%temp%i = tatm
  atm%taui%wat = tau_wat
  atm%taui%oth = tau_oth
  atm%taui%tot = tau_tot
  !
end subroutine mira_atm_dsb_transmission
!
      SUBROUTINE mnbrak(ax,bx,cx,fa,fb,fc,func)
!
! Subroutine from Numerical Recipes for bracketing
! a minimum using the golde rule.
!
! Input:
!
      implicit none
      REAL*8 ax,bx,cx,fa,fb,fc,func,GOLD,GLIMIT,TINY
      EXTERNAL func
      PARAMETER (GOLD=1.618034, GLIMIT=100., TINY=1.e-20)
      REAL*8 dum,fu,q,r,u,ulim
      fa=func(ax)
      fb=func(bx)
      if(fb.gt.fa)then
        dum=ax
        ax=bx
        bx=dum
        dum=fb
        fb=fa
        fa=dum
      endif
      cx=bx+GOLD*(bx-ax)
      fc=func(cx)
1     if(fb.ge.fc)then
        r=(bx-ax)*(fb-fc)
        q=(bx-cx)*(fb-fa)
        u=bx-((bx-cx)*q-(bx-ax)*r)/(2.*sign(max(abs(q-r),TINY),q-r))
        ulim=bx+GLIMIT*(cx-bx)
        if((bx-u)*(u-cx).gt.0.)then
          fu=func(u)
          if(fu.lt.fc)then
            ax=bx
            fa=fb
            bx=u
            fb=fu
            return
          else if(fu.gt.fb)then
            cx=u
            fc=fu
            return
          endif
          u=cx+GOLD*(cx-bx)
          fu=func(u)
        else if((cx-u)*(u-ulim).gt.0.)then
          fu=func(u)
          if(fu.lt.fc)then
            bx=cx
            cx=u
            u=cx+GOLD*(cx-bx)
            fb=fc
            fc=fu
            fu=func(u)
          endif
        else if((u-ulim)*(ulim-cx).ge.0.)then
          u=ulim
          fu=func(u)
        else
          u=cx+GOLD*(cx-bx)
          fu=func(u)
        endif
        ax=bx
        bx=cx
        cx=u
        fa=fb
        fb=fc
        fc=fu
        goto 1
      endif
      return
      end subroutine mnbrak

      FUNCTION brent(ax,bx,cx,f,tol,xmin,error)
      implicit none
      INTEGER ITMAX
      REAL*8 brent,ax,bx,cx,tol,xmin,f,CGOLD,ZEPS
      EXTERNAL f
      PARAMETER (ITMAX=100,CGOLD=.3819660,ZEPS=1.0e-10)
      INTEGER iter
      REAL*8 a,b,d,e,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm
      LOGICAL error
      a=min(ax,cx)
      b=max(ax,cx)
      v=bx
      w=v
      x=v
      e=0.
      fx=f(x)
      fv=fx
      fw=fx
      do 11 iter=1,ITMAX
        xm=0.5*(a+b)
        tol1=tol*abs(x)+ZEPS
        tol2=2.*tol1
        if(abs(x-xm).le.(tol2-.5*(b-a))) goto 3
        if(abs(e).gt.tol1) then
          r=(x-w)*(fx-fv)
          q=(x-v)*(fx-fw)
          p=(x-v)*q-(x-w)*r
          q=2.*(q-r)
          if(q.gt.0.) p=-p
          q=abs(q)
          etemp=e
          e=d
          if(abs(p).ge.abs(.5*q*etemp).or.p.le.q*(a-x).or.p.ge.q*(b-x)) goto 1
          d=p/q
          u=x+d
          if(u-a.lt.tol2 .or. b-u.lt.tol2) d=sign(tol1,xm-x)
          goto 2
        endif
1       if(x.ge.xm) then
          e=a-x
        else
          e=b-x
        endif
        d=CGOLD*e
2       if(abs(d).ge.tol1) then
          u=x+d
        else
          u=x+sign(tol1,d)
        endif
        fu=f(u)
        if(fu.le.fx) then
          if(u.ge.x) then
            a=x
          else
            b=x
          endif
          v=w
          fv=fw
          w=x
          fw=fx
          x=u
          fx=fu
        else
          if(u.lt.x) then
            a=u
          else
            b=u
          endif
          if(fu.le.fw .or. w.eq.x) then
            v=w
            fv=fw
            w=u
            fw=fu
          else if(fu.le.fv .or. v.eq.x .or. v.eq.w) then
            v=u
            fv=fu
          endif
        endif
11    continue
      call gagout('E-CHOPPER: BRENT: number of maximum iterations exceeded.')
      error = .true.
      return
3     xmin=x
      brent=fx
      return
      end function brent


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


      FUNCTION chiSquaredAtm(pwv)

      use chopper_def
 
      real(8), intent(in) :: pwv

      integer :: ier
      real(8) :: chiSquaredAtm, temi

      atm%h2omm = pwv
      call mira_atm_dsb_transmission(telNumRec,ier)
      temi = (atm%temi%s + atm%temi%i*rec%sbgr) / (1.+rec%sbgr)
      chiSquaredAtm = (temi-temi0)**2
      return

      END FUNCTION chiSquaredAtm


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


      FUNCTION chiSquaredTau(pwv)

      use chopper_def
 
      real(8), intent(in) :: pwv

      integer :: ier
      real(8) :: chiSquaredTau, tau

      atm%h2omm = pwv
      call mira_atm_dsb_transmission(telNumRec,ier)
      chiSquaredTau = (atm%taus%tot-tau0)**2
      return

      END FUNCTION chiSquaredTau
