!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_calibration(mrtset,filebuf,backcal,error)
  use gbl_message
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use imbfits_interfaces_public
  use mrtcal_interfaces, except_this => mrtcal_calib_calibration
  !---------------------------------------------------------------------
  ! @ private
  !
  !  1. Read the calsky, calambient, calcold subscan in the order they
  !     appear in the IMBFITS and compute the mean counts over the channels.
  !  2. Fill the chopper structure, compute the Tamt, Trec, Tsys, and Tcal,
  !     and fill the chunkset structure with these results.
  !  3. List results on screen and write results in the output CLASS file.
  !
  !  When the calsky is the only missing subscan, we can still compute an
  !  approximated receiver temperature (i.e., assuming 100% coupling to the
  !  hot and cold loads). To do this, we create a blanked calsky so that we
  !  can use exactly the same steps/pieces of code. It's only in the
  !  chopper code that we'll catch the fact we can only compute the Trec.  
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),   intent(in)    :: mrtset
  type(imbfits_buffer_t), intent(inout) :: filebuf
  type(calib_backend_t),  intent(inout) :: backcal
  logical,                intent(inout) :: error
  ! Local
  logical :: calgridpresent
  logical :: poldatapresent
  character(len=*), parameter :: rname='CALIB>CALIBRATION'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  calgridpresent = imbfits_subscan_exists(filebuf%imbf,'calgrid')
  poldatapresent = filebuf%imbf%back%polchunks
  if (calgridpresent.and..not.poldatapresent) then
     ! This one is just a warning
     call mrtcal_message(seve%w,rname,'The CALGRID subscan does not contain polarimetry data!')
  endif
  if (.not.calgridpresent.and.poldatapresent) then
     ! This one implies that the observer will not be able to use
     ! the next science scan to do polarimetry => Clear feedback
     ! is needed now => That's an error.
     call mrtcal_message(seve%e,rname,'No CALGRID subscan!')
     error = .true.
     return
  endif
  backcal%polar = calgridpresent.and.poldatapresent
  !
  if (imbfits_subscan_exists(filebuf%imbf,'calsky')) then
     call mrtcal_calibrate_average_load(mrtset,'calsky',filebuf%imbf,filebuf%subscanbuf,backcal%sky,error)
     if (failed_calibrate(rname,'calsky',error)) return
  endif
  call mrtcal_calibrate_average_load(mrtset,'calambient',filebuf%imbf,filebuf%subscanbuf,backcal%hot,error)
  if (failed_calibrate(rname,'calambient',error)) return
  call mrtcal_calibrate_average_load(mrtset,'calcold',filebuf%imbf,filebuf%subscanbuf,backcal%cold,error)
  if (failed_calibrate(rname,'calcold',error)) return
  if (.not.imbfits_subscan_exists(filebuf%imbf,'calsky')) then
     ! At this point, calambient and calcold were successfully read.
     ! Let's use one of them to create a blanked calsky.
     call mrtcal_chunkset_2d_blank(backcal%hot,backcal%sky,error)
     if (error) return
     ! Force the source name 
     call mrtcal_chunkset_2d_modify_source('calnosky',backcal%sky,error)
     if (error)  return
  endif
  call mrtcal_calibrate_chunkset2chopperset(backcal,mrtset,error)
  if (error) return
  call mrtcal_calibrate_compute_chopperset(backcal,error)
  if (error) return
  call mrtcal_calibrate_chopperset2chunkset(backcal,error)
  if (error) return
  if (backcal%polar) then
     call mrtcal_calibrate_grid(mrtset,filebuf%imbf,filebuf%subscanbuf,backcal,error)
     if (failed_calibrate(rname,'calgrid',error)) return
  endif
  !
  ! Feedback:
  ! - Terminal and ASCII table
  call mrtcal_calibrate_user_feedback(backcal,mrtset%out,error)
  if (error) return
  ! - VO XML
  if (mrtset%out%voxml) then
    call calibration_to_VO(filebuf%imbf,backcal,error)
    if (error)  return
  endif
  ! - Class file
  call mrtcal_calibrate_toclass(mrtset,backcal,error)
  if (error) return
  !
end subroutine mrtcal_calib_calibration
!
subroutine mrtcal_calibrate_toclass(mrtset,backcal,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calibrate_toclass
  use mrtcal_calib_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),  intent(in)    :: mrtset
  type(calib_backend_t), intent(inout) :: backcal
  logical,               intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CALIBRATE>TOCLASS'
  type(chunkset_3d_t) :: allinone
  type(mrtcal_setup_output_t) :: out
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  backcal%nspec = 0
  !
  select case (mrtset%out%calib)
  case (outputcalib_none)
    ! Nothing to write. Just invoke once mrtcal_write_toclass with toclass=.false.
    ! so that backcal%nspec is correctly set.
    out = mrtset%out
    out%toclass = .false.
    call mrtcal_write_toclass(backcal%sky,out,backcal%nspec,error)
    if (error) return
    !
  case (outputcalib_spec)
    call mrtcal_write_toclass(backcal%sky,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%hot,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%cold,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%trec,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%tcal,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%tsys,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%water,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%ztau,mrtset%out,backcal%nspec,error)
    if (error) return
    call mrtcal_write_toclass(backcal%flag,mrtset%out,backcal%nspec,error)
    if (error) return
    !
  case (outputcalib_asso)
    ! Set 'allinone' as kind calibration, i.e. 3rd dimension is special,
    ! with associated arrays.
    allinone%kind = ckind_calib
    !
    call reallocate_chunkset_3d(backcal%sky%nset,backcal%sky%npix,9,allinone,error)
    if (error)  return
    !
    call reassociate_chunkset_3d(1,backcal%sky,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(2,backcal%hot,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(3,backcal%cold,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(4,backcal%trec,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(5,backcal%tcal,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(6,backcal%tsys,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(7,backcal%water,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(8,backcal%ztau,allinone,error)
    if (error)  goto 10
    call reassociate_chunkset_3d(9,backcal%flag,allinone,error)
    if (error)  goto 10
    !
    call mrtcal_write_toclass(allinone,mrtset%out,backcal%nspec,error)
    if (error)  goto 10
    !
    if (backcal%polar) then
      ! Also write the cross-polarimetric products as a spectrum
      ! + associated arrays
      call mrtcal_calibrate_toclass_assoc_polar(backcal,allinone,error)
      if (error)  goto 10
    endif
    !
10 continue
    call free_chunkset_3d(allinone,error)
    if (error)  return
    !
  case default
    call mrtcal_message(seve%e,rname,'MSET OUTPUT CALIBRATION mode not implemented')
    error = .true.
    return
  end select
  !
contains
  !
  subroutine mrtcal_calibrate_toclass_assoc_polar(backcal,allinone,error)
    !-------------------------------------------------------------------
    ! Ad-hoc subroutine which builds a chunkset 3D to be written in
    ! Class as 1 spectrum with 5 Associated Arrays. In the context of
    ! polarimetry data, we have 3 chunkset 2D which contains
    ! alternatively:
    !   #1  REAL IMAG REAL IMAG
    !   #2  AMP  PHA  AMP  PHA
    !   #3  COS  SIN  COS  SIN
    ! We do NOT want to write 4 spectra with
    !   #1  REAL AMP COS
    !   #2  IMAG PHA SIN
    !   #3  REAL AMP COS
    !   #4  IMAG PHA SIN
    ! We want to write 2 spectra with
    !   #1  REAL IMAG AMP PHA COS SIN
    !   #2  REAL IMAG AMP PHA COS SIN
    ! This means that the various chunkset have to be reordered in the
    ! dummy chunkset 3D before using the generic tools for transfering
    ! data to class.
    !-------------------------------------------------------------------
    type(calib_backend_t), intent(in)    :: backcal
    type(chunkset_3d_t),   intent(inout) :: allinone
    logical,               intent(inout) :: error
    ! Local
    type(stokesset_book_t) :: book
    integer(kind=4) :: istokesset,nstokesset
    integer(kind=4) :: ipix,dummy
    !
    call imbfits_count_polar_stokesset(backcal%chunksetlist,nstokesset,error)
    if (error)  return
    call reallocate_chunkset_3d(nstokesset,backcal%grid%npix,6,allinone,error)
    if (error)  return
    !
    istokesset = 0
    do ipix=1,backcal%grid%npix
       call imbfits_init_stokesloop(book,error)
       if (error) return
       do
          call imbfits_get_next_stokesset(backcal%chunksetlist,book,error)
          if (error) return
          if (.not.book%found) exit
          if (book%nstokes.ne.4) cycle
          istokesset = istokesset+1
          call reassociate_chunkset(&
               backcal%grid%chunkset(book%iset(code_r),ipix),&
               allinone%chunkset(istokesset,ipix,1),error)
          if (error) return
          call reassociate_chunkset(&
               backcal%grid%chunkset(book%iset(code_i),ipix),&
               allinone%chunkset(istokesset,ipix,2),error)
          if (error) return
          call reassociate_chunkset(&
               backcal%amppha%chunkset(book%iset(code_r),ipix),&
               allinone%chunkset(istokesset,ipix,3),error)
          if (error) return
          call reassociate_chunkset(&
               backcal%amppha%chunkset(book%iset(code_i),ipix),&
               allinone%chunkset(istokesset,ipix,4),error)
          if (error) return
          call reassociate_chunkset(&
               backcal%sincos%chunkset(book%iset(code_r),ipix),&
               allinone%chunkset(istokesset,ipix,5),error)
          if (error)  return
          call reassociate_chunkset(&
               backcal%sincos%chunkset(book%iset(code_i),ipix),&
               allinone%chunkset(istokesset,ipix,6),error)
          if (error)  return
       enddo ! istokesset
    enddo ! ipix
    !
    dummy = 0
    call mrtcal_write_toclass(allinone,mrtset%out,dummy,error)
    if (error) return
    !
  end subroutine mrtcal_calibrate_toclass_assoc_polar
  !
end subroutine mrtcal_calibrate_toclass
!
subroutine mrtcal_calibrate_user_feedback(backcal,setout,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calibrate_user_feedback
  use mrtcal_buffers
  use mrtcal_calib_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  ! Some direct allocations and deallocations here. It should maybe be 
  ! changed *** JP
  !---------------------------------------------------------------------
  type(calib_backend_t),       intent(in)    :: backcal
  type(mrtcal_setup_output_t), intent(in)    :: setout
  logical,                     intent(inout) :: error
  ! Local
  real(kind=8),                  allocatable :: freqs(:) ! Probably needs a structure here *** JP
  character(len=5),              allocatable :: idfe(:)
  integer(kind=4),               allocatable :: idx(:)
  character(len=message_length), allocatable :: results(:)
  !
  integer(kind=4), parameter :: iperpixel = 1
  integer(kind=4), parameter :: iperset   = 2
  integer(kind=4), parameter :: iperchunk = 3
  integer(kind=4) :: ier,mylun
  integer(kind=4) :: ipix,iset,ncal,ical
  character(len=backname_length) :: backendname
  character(len=8) :: date
  character(len=64) :: filename
  character(len=*), parameter :: rname='CALIBRATE>USER>FEEDBACK'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_calibrate_user_feedback_head(error)
  if (error) return
  !
  ncal = 0
  if (rsetup%cal%feedback.eq.iperpixel) then
    ! Less verbose (1 per pixel)
    ncal = backcal%sky%npix
  elseif (rsetup%cal%feedback.eq.iperset) then
    ! More verbose (1 per chunkset)
    ncal = backcal%sky%npix*backcal%sky%nset
  elseif (rsetup%cal%feedback.eq.iperchunk) then
    ! Most verbose (1 per chunk)
    do ipix=1,backcal%sky%npix
      do iset=1,backcal%sky%nset
        ncal = ncal+backcal%chopperset(iset,ipix)%n
      enddo ! iset
    enddo ! ipix
  endif
  !
  allocate(idx(ncal),freqs(ncal),idfe(ncal),results(ncal),stat=ier)
  if (failed_allocate(rname,'Result arrays',ier,error)) return
  !
  if (rsetup%cal%feedback.eq.iperpixel) then
    call calibrate_user_feedback_perpixel(mrtcal_gr8_median)
  elseif (rsetup%cal%feedback.eq.iperset) then
    call calibrate_user_feedback_perset(mrtcal_gr8_median)
  elseif (rsetup%cal%feedback.eq.iperchunk) then
    call calibrate_user_feedback_perchunk
  endif
  !
  ! Sort by increasing frequency, then by idFe
  do ical=1,ncal
    idx(ical) = ical
  enddo
  call gi4_quicksort_index_with_user_gtge(idx,ncal,  &
    feedback_gt,feedback_ge,error)
  if (error)  return
  !
  ! Display in terminal by increasing frequency, then by idFe
  do ical=1,ncal
     call mrtcal_message(seve%r,rname,results(idx(ical)))
  enddo ! ical
  !
  ! Also display in a file?
  if (setout%caltable) then
    ! Build file name
    backendName = mrtindex_backend(backcal%head%key%backend)
    call gag_toyyyymmdd(backcal%head%key%dobs,date,error)
    if (error)  return
    write(filename,'(a20,a,a1,a8,a1,i0,a4)')  &
        'iram30m-calibration-',trim(backendName),'-',date,  &
        's',backcal%head%key%scan,'.dat'
    !
    ! Open file
    ier = sic_getlun(mylun)
    if (ier.ne.1) then
      error = .true.
      return
    endif
    ier = sic_open(mylun,filename,'NEW',.false.)
    if (ier.ne.0) then
      call mrtcal_message(seve%e,rname,'Cannot open new file '//filename)
      error = .true.
      call sic_frelun(mylun)
      return
    endif
    !
    ! Write lines
    do ical=1,ncal
      write (mylun,'(A)') trim(results(idx(ical)))
    enddo
    !
    ! Close file
    ier = sic_close(mylun)
    call sic_frelun(mylun)
    !
  endif
  !
  deallocate(idx,freqs,idfe,results)
  !
contains
  !
  subroutine mrtcal_calibrate_user_feedback_head(error)
    logical, intent(inout) :: error
    ! Local
    character(len=message_length) :: mess
    character(len=*), parameter :: rname='CALIBRATE>USER>FEEDBACK>HEAD'
    !
    call mrtcal_message(seve%t,rname,'Welcome')
    !
    call mrtcal_separator(seve%r,rname,1,error)
    if (error) return
    write(mess,'(t1,a4,t7,a4,t13,a8,t22,a9,t32,a4,t38,a3,t43,a7,t51,a7,t59,a7,t67,a9,t77,a3)') &
         'idFe','freq','THotLoad','TColdLoad','idBe','Pix','recTemp','sysTemp','calTemp','tauZenith','pwv'
    call mrtcal_message(seve%r,rname,mess)
    write(mess,'(t7,a5,t13,a18,t43,a23,t68,a7,t77,a4)') &
         '[GHz]','---- [Kelvin] ----','------ [Kelvin] -------','[Neper]','[mm]'
    call mrtcal_message(seve%r,rname,mess)
    write(mess,'(t1,a80)') &
         '--------------------------------------------------------------------------------'
    call mrtcal_message(seve%r,rname,mess)
    !
  end subroutine mrtcal_calibrate_user_feedback_head
  !
  subroutine calibrate_user_feedback_perpixel(gr8_compute)
    external :: gr8_compute  ! Computation routine, same calling sequence
                             ! as gr8_median
    integer(kind=4) :: ical
    character(len=8) :: mfrontend
    real(kind=8) :: mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o
    ical = 0
    do ipix=1,backcal%sky%npix
      call calibrate_user_feedback_compute_perpixel(gr8_compute,  &
           backcal,ipix,                                          &
           mfrontend,mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o,error)
      if (error)  return
      !
      ical = ical+1
      freqs(ical) = mfreq
      idfe(ical) = mfrontend
      call calibrate_user_feedback_oneline(  &
        mfrontend,                 &
        mfreq,                     &
        mthot,                     &
        mtcold,                    &
        backcal%head%key%backend,  &
        ipix,                      &
        mtrec,                     &
        mtsys,                     &
        mtcal,                     &
        mtau,                      &
        mh2o,                      &
        results(ical))
    enddo ! ipix
  end subroutine calibrate_user_feedback_perpixel
  !
  subroutine calibrate_user_feedback_compute_perpixel(gr8_compute,backcal,ipix,  &
    frontend,freq,thot,tcold,trec,tsys,tcal,tau,h2omm,error)
    !-------------------------------------------------------------------
    ! Make the computations for the input chunkset
    !-------------------------------------------------------------------
    external                             :: gr8_compute
    type(calib_backend_t), intent(in)    :: backcal
    integer(kind=4),       intent(in)    :: ipix
    character(len=8),      intent(out)   :: frontend
    real(kind=8),          intent(out)   :: freq
    real(kind=8),          intent(out)   :: thot
    real(kind=8),          intent(out)   :: tcold
    real(kind=8),          intent(out)   :: trec
    real(kind=8),          intent(out)   :: tsys
    real(kind=8),          intent(out)   :: tcal
    real(kind=8),          intent(out)   :: tau
    real(kind=8),          intent(out)   :: h2omm
    logical,               intent(inout) :: error
    ! Local
    integer(kind=4) :: iset,ic,nc
    character(len=8) :: myfrontend
    integer(kind=size_length) :: nelem
    real(kind=8), allocatable :: r8data(:)
    !
    ! Compute an 'average' frontend name
    frontend = backcal%sky%chunkset(1,ipix)%chunks(1)%frontend
    do iset=2,backcal%sky%nset
      myfrontend = backcal%sky%chunkset(iset,ipix)%chunks(1)%frontend
      do ic=1,8
        if (frontend(ic:ic).ne.myfrontend(ic:ic))  frontend(ic:ic) = '-'
      enddo
    enddo
    !
    ! Need to fill a temporary data array for gr8_compute. First get its size
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      nelem = nelem+nc
    enddo
    !
    allocate(r8data(nelem))
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%freqs(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,freq,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%loads(1:nc)%hot%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,thot,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%loads(1:nc)%cold%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,tcold,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%recs(1:nc)%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,trec,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%tsyss(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,tsys,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%tcals(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,tcal,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%atms(1:nc)%taus%tot
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,tau,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopperset(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopperset(iset,ipix)%atms(1:nc)%h2omm
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopperset(1,1)%bad,0.d0,h2omm,error)
    if (error)  return
    !
    deallocate(r8data)
  end subroutine calibrate_user_feedback_compute_perpixel
  !
  subroutine calibrate_user_feedback_perset(gr8_compute)
    external :: gr8_compute  ! Computation routine, same calling sequence
                             ! as gr8_median
    integer(kind=4) :: ical
    character(len=8) :: mfrontend
    real(kind=8) :: mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o
    ical = 0
    do ipix=1,backcal%sky%npix
      do iset=1,backcal%sky%nset
          call calibrate_user_feedback_compute_perset(gr8_compute,  &
               backcal%chopperset(iset,ipix),                       &
               mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o,error)
          if (error)  return
          !
          ical = ical+1
          freqs(ical) = mfreq
          ! Assume consistent telescope name per chunksets, use first one
          mfrontend = backcal%sky%chunkset(iset,ipix)%chunks(1)%frontend
          idfe(ical) = mfrontend
          call calibrate_user_feedback_oneline(  &
            mfrontend,                 &
            mfreq,                     &
            mthot,                     &
            mtcold,                    &
            backcal%head%key%backend,  &
            ipix,                      &
            mtrec,                     &
            mtsys,                     &
            mtcal,                     &
            mtau,                      &
            mh2o,                      &
            results(ical))
      enddo ! iset
    enddo ! ipix
  end subroutine calibrate_user_feedback_perset
  !
  subroutine calibrate_user_feedback_compute_perset(gr8_compute,chopperset,freq,  &
    thot,tcold,trec,tsys,tcal,tau,h2omm,error)
    !-------------------------------------------------------------------
    ! Make the computations for the input chunkset
    !-------------------------------------------------------------------
    external                       :: gr8_compute
    type(chopper_t), intent(in)    :: chopperset
    real(kind=8),    intent(out)   :: freq
    real(kind=8),    intent(out)   :: thot
    real(kind=8),    intent(out)   :: tcold
    real(kind=8),    intent(out)   :: trec
    real(kind=8),    intent(out)   :: tsys
    real(kind=8),    intent(out)   :: tcal
    real(kind=8),    intent(out)   :: tau
    real(kind=8),    intent(out)   :: h2omm
    logical,         intent(inout) :: error
    ! Local
    integer(kind=size_length) :: nc
    real(kind=8), allocatable :: r8buf(:)
    !
    nc = chopperset%n
    allocate(r8buf(nc))
    !
    r8buf(:) = chopperset%freqs(1:nc)%s
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,freq,error)
    if (error)  return
    !
    r8buf(:) = chopperset%loads(1:nc)%hot%temp
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,thot,error)
    if (error)  return
    !
    r8buf(:) = chopperset%loads(1:nc)%cold%temp
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,tcold,error)
    if (error)  return
    !
    r8buf(:) = chopperset%recs(1:nc)%temp
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,trec,error)
    if (error)  return
    !
    r8buf(:) = chopperset%tsyss(1:nc)%s
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,tsys,error)
    if (error)  return
    !
    r8buf(:) = chopperset%tcals(1:nc)%s
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,tcal,error)
    if (error)  return
    !
    r8buf(:) = chopperset%atms(1:nc)%taus%tot
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,tau,error)
    if (error)  return
    !
    r8buf(:) = chopperset%atms(1:nc)%h2omm
    call gr8_compute(r8buf,nc,chopperset%bad,0.d0,h2omm,error)
    if (error)  return
  end subroutine calibrate_user_feedback_compute_perset
  !
  subroutine calibrate_user_feedback_perchunk
    ! Local
    type(chopper_t), pointer :: chopperset
    integer(kind=4) :: ical,ichunk
    character(len=8) :: mfrontend
    !
    ical = 0
    do ipix=1,backcal%sky%npix
      do iset=1,backcal%sky%nset
          chopperset => backcal%chopperset(iset,ipix)
          !
          do ichunk=1,chopperset%n
            ical = ical+1
            freqs(ical) = chopperset%freqs(ichunk)%s
            mfrontend = backcal%sky%chunkset(iset,ipix)%chunks(ichunk)%frontend
            idfe(ical) = mfrontend
            call calibrate_user_feedback_oneline(  &
              mfrontend,                           &
              chopperset%freqs(ichunk)%s,          &
              chopperset%loads(ichunk)%hot%temp,   &
              chopperset%loads(ichunk)%cold%temp,  &
              backcal%head%key%backend,            &
              ipix,                                &
              chopperset%recs(ichunk)%temp,        &
              chopperset%tsyss(ichunk)%s,          &
              chopperset%tcals(ichunk)%s,          &
              chopperset%atms(ichunk)%taus%tot,    &
              chopperset%atms(ichunk)%h2omm,       &
              results(ical))
          enddo ! ichunk
      enddo ! iset
    enddo ! ipix
  end subroutine calibrate_user_feedback_perchunk
  !
  subroutine calibrate_user_feedback_oneline(frontend,freq,thot,tcold,  &
    backend,ipix,trec,tsys,tcal,tau,h2omm,oneline)
    character(len=*),  intent(in)  :: frontend
    real(kind=8),      intent(in)  :: freq
    real(kind=8),      intent(in)  :: thot
    real(kind=8),      intent(in)  :: tcold
    integer(kind=4),   intent(in)  :: backend
    integer(kind=4),   intent(in)  :: ipix
    real(kind=8),      intent(in)  :: trec
    real(kind=8),      intent(in)  :: tsys
    real(kind=8),      intent(in)  :: tcal
    real(kind=8),      intent(in)  :: tau
    real(kind=8),      intent(in)  :: h2omm
    character(len=*),  intent(out) :: oneline
    !
    write(oneline,100)  frontend,                                          &
                        calibrate_user_feedback_onedouble(5,'f5.1',freq),  &
                        calibrate_user_feedback_onedouble(8,'f8.3',thot),  &
                        calibrate_user_feedback_onedouble(9,'f9.3',tcold), &
                        mrtindex_backend(backend),                         &
                        ipix,                                              &
                        calibrate_user_feedback_onedouble(7,'f7.2',trec),  &
                        calibrate_user_feedback_onedouble(7,'f7.2',tsys),  &
                        calibrate_user_feedback_onedouble(7,'f7.2',tcal),  &
                        calibrate_user_feedback_onedouble(5,'f5.2',tau),   &
                        calibrate_user_feedback_onedouble(4,'f4.1',h2omm)
    !
100 format(t1,a5,t7,a5,t13,a8,t20,a9,t32,a5,t37,i3,t43,a7,t51,a7,t59,a7,t69,a5,t77,a4)
  end subroutine calibrate_user_feedback_oneline
  !
  function calibrate_user_feedback_onedouble(nchar,oneformat,onevalue)
    character(len=32)                :: calibrate_user_feedback_onedouble
    integer(kind=4),   intent(in)    :: nchar
    character(len=*),  intent(in)    :: oneformat
    real(kind=8),      intent(in)    :: onevalue
    !
    character(len=*), parameter :: hyphens='--------------------------------'
    !
    if (onevalue.eq.backcal%chopperset(1,1)%bad) then 
      calibrate_user_feedback_onedouble=hyphens(1:nchar)
    else
       write(calibrate_user_feedback_onedouble,"("//oneformat//")") onevalue
    endif
  end function calibrate_user_feedback_onedouble
  !
  ! Sorting functions: first by frequency, then by idFe
  function feedback_gt(m,l)
    logical :: feedback_gt
    integer(kind=4), intent(in) :: m,l
    if (freqs(m).ne.freqs(l)) then
      feedback_gt = freqs(m).gt.freqs(l)
      return
    endif
    feedback_gt = lgt(idfe(m),idfe(l))
  end function feedback_gt
  function feedback_ge(m,l)
    logical :: feedback_ge
    integer(kind=4), intent(in) :: m,l
    if (freqs(m).ne.freqs(l)) then
      feedback_ge = freqs(m).ge.freqs(l)
      return
    endif
    feedback_ge = lge(idfe(m),idfe(l))
  end function feedback_ge
end subroutine mrtcal_calibrate_user_feedback
!
subroutine mrtcal_calibrate_compute_chopperset(backcal,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_compute_chopperset
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), intent(inout) :: backcal
  logical,               intent(inout) :: error
  ! Local
  type(stokesset_book_t) :: book
  integer(kind=size_length) :: ipix,istokes
  character(len=*), parameter :: rname='CALIBRATE>COMPUTE>CHOPPERSET'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ipix=1,backcal%sky%npix
     call imbfits_init_stokesloop(book,error)
     if (error) return
     do while (.true.)
        call imbfits_get_next_stokesset(backcal%chunksetlist,book,error)
        if (error) return
        if (.not.book%found) exit
        do istokes=1,book%nstokes
           if (istokes.le.2) then
              call telcal_chopper(backcal%chopperset(book%iset(istokes),ipix),error)
              if (error)  return
           endif
        enddo ! istokes
     enddo ! istokesset
  enddo ! ipix
end subroutine mrtcal_calibrate_compute_chopperset
!
subroutine mrtcal_calibrate_chunkset2chopperset(backcal,mrtset,error)
  use gbl_message
  use phys_const
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chunkset2chopperset
  use mrtcal_calib_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), intent(inout) :: backcal
  type(mrtcal_setup_t),  intent(in)    :: mrtset
  logical,               intent(inout) :: error
  ! Local
  type(chunkset_t), pointer :: skyset,hotset,coldset
  type(chopper_t),  pointer :: chopperset
  integer(kind=size_length) :: ipix,iset,ichunk
  real(kind=8) :: sigfre,imafre
  real(kind=8), parameter :: GHz_per_MHz = 1d-3
  character(len=*), parameter :: rname='CALIBRATE>CHUNKSET2CHOPPERSET'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Check consistency of sky, hot, and cold
  call chunkset_2d_consistency(rname,backcal%sky,backcal%hot,error)
  if (error)  return
  call chunkset_2d_consistency(rname,backcal%sky,backcal%cold,error)
  if (error)  return
  !
  call reallocate_chopperset(backcal%sky,backcal%chopperset,error)
  if (error) return
  ! Save size of chopperset (re)allocation:
  backcal%nset = backcal%sky%nset
  backcal%npix = backcal%sky%npix
  !
  do ipix=1,backcal%npix
     do iset=1,backcal%nset
        skyset  => backcal%sky%chunkset(iset,ipix)
        hotset  => backcal%hot%chunkset(iset,ipix)
        coldset => backcal%cold%chunkset(iset,ipix)
        chopperset => backcal%chopperset(iset,ipix)
        !
        chopperset%search%strict = mrtset%cal%chopperstrict
        chopperset%search%water  = mrtset%cal%watermode
        chopperset%search%atm    = .true.
        chopperset%search%trec   = .true.
        chopperset%search%tcal   = .true.
        chopperset%search%tsys   = .true.
        !
        chopperset%n = skyset%n
        do ichunk=1,skyset%n
           ! Telescope
           chopperset%tel%alti = skyset%chunks(ichunk)%cal%alti/1d3  ! telcal_chopper needs km
           chopperset%tel%lati = skyset%chunks(ichunk)%cal%geolat
           chopperset%tel%elev = skyset%chunks(ichunk)%gen%el
           chopperset%tel%hwat = 2.0 ! [km]
           chopperset%tel%pres = skyset%chunks(ichunk)%cal%pamb
           chopperset%tel%tout = skyset%chunks(ichunk)%cal%tamb
           chopperset%tel%tcab = 0.8*skyset%chunks(ichunk)%cal%tchop + 0.2*skyset%chunks(ichunk)%cal%tamb
           ! Signal and image frequencies
           ! *** JP It should be the frequencies in the observatory, not in the rest frame...
           call abscissa_sigabs_middle(skyset%chunks(ichunk)%spe,sigfre)
           call abscissa_imaabs_middle(skyset%chunks(ichunk)%spe,imafre)
           chopperset%freqs(ichunk)%s = sigfre*GHz_per_MHz
           chopperset%freqs(ichunk)%i = imafre*GHz_per_MHz
           ! Loads
           chopperset%loads(ichunk)%dark_count = 0d0
           chopperset%loads(ichunk)%sky_count = skyset%chunks(ichunk)%cont1
           chopperset%loads(ichunk)%hot%count = hotset%chunks(ichunk)%cont1
           chopperset%loads(ichunk)%hot%temp = hotset%chunks(ichunk)%cal%tchop
           chopperset%loads(ichunk)%hot%ceff = 1d0
           chopperset%loads(ichunk)%cold%count = coldset%chunks(ichunk)%cont1
           chopperset%loads(ichunk)%cold%temp = coldset%chunks(ichunk)%cal%tcold
           chopperset%loads(ichunk)%cold%ceff = 1d0
           ! Receiver
           chopperset%recs(ichunk)%sbgr = skyset%chunks(ichunk)%cal%gaini
           chopperset%recs(ichunk)%beff = skyset%chunks(ichunk)%cal%beeff
           chopperset%recs(ichunk)%feff = skyset%chunks(ichunk)%cal%foeff
           chopperset%recs(ichunk)%fout = 0d0
           chopperset%recs(ichunk)%fcab = 0d0
           ! Atmosphere
           chopperset%atms(ichunk)%h2omm = mrtset%cal%watervalue  ! Initial guess for telcal_chopper
           ! ZZZ other initializations?
        enddo ! ichunk
     enddo ! iset
  enddo ! ipix
  !
end subroutine mrtcal_calibrate_chunkset2chopperset
!
subroutine mrtcal_calibrate_chopperset2chunkset(backcal,error)
  use gbl_message
  use chopper_definitions
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chopperset2chunkset
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), target, intent(inout) :: backcal
  logical,                       intent(inout) :: error
  ! Local
  type(stokesset_book_t) :: book
  type(chopper_t), pointer :: hh,vv,hv,vh
  integer(kind=size_length) :: ipix,iset
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET2CHUNKSET'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Cross-correlation calibration products are deduced from previously
  ! compute auto-correlation calibration products
  do ipix=1,backcal%sky%npix
     call imbfits_init_stokesloop(book,error)
     if (error) return
     do while (.true.)
        call imbfits_get_next_stokesset(backcal%chunksetlist,book,error)
        if (error) return
        if (.not.book%found) exit
        if (book%nstokes.ne.4) cycle
        hh => backcal%chopperset(book%iset(code_h),ipix)
        vv => backcal%chopperset(book%iset(code_v),ipix)
        hv => backcal%chopperset(book%iset(code_r),ipix)
        vh => backcal%chopperset(book%iset(code_i),ipix)
        call mrtcal_calibrate_chopperset_cross(hh,vv,hv,vh,error)
        if (error) return
     enddo ! istokesset
  enddo ! ipix
  !
  call clone_chunkset_2d(backcal%sky,backcal%trec,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%tcal,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%atsys,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%tsys,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%water,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%ztau,.false.,error)
  if (error) return
  call clone_chunkset_2d(backcal%sky,backcal%flag,.false.,error)
  if (error) return
  do ipix=1,backcal%sky%npix
     do iset=1,backcal%sky%nset
        ! Fill chunk headers from the chopperset structure
        call mrtcal_calibrate_chopperset2loadheader(&
             backcal%chopperset(iset,ipix),&
             backcal%sky%chunkset(iset,ipix),&
             error)
        call mrtcal_calibrate_chopperset2loadheader(&
             backcal%chopperset(iset,ipix),&
             backcal%hot%chunkset(iset,ipix),&
             error)
        call mrtcal_calibrate_chopperset2loadheader(&
             backcal%chopperset(iset,ipix),&
             backcal%cold%chunkset(iset,ipix),&
             error)
        ! Interpolate calibration results in frequency
        call mrtcal_calibrate_chopperset2calarray(&
             backcal%chopperset(iset,ipix),&
             backcal%sky%chunkset(iset,ipix),&  ! Used as reference (not modified)
             backcal%trec%chunkset(iset,ipix),&
             backcal%tcal%chunkset(iset,ipix),&
             backcal%atsys%chunkset(iset,ipix),&
             backcal%tsys%chunkset(iset,ipix),&
             backcal%water%chunkset(iset,ipix),&
             backcal%ztau%chunkset(iset,ipix),&
             backcal%flag%chunkset(iset,ipix),&
             error)
         ! Ad-hoc patch: backcal%atsys must provide the attenuated Tsys,
         ! not Tsys itself which was inherited when duplicating the
         ! backcal%sky headers in mrtcal_calibrate_chopperset2calarray
         call mrtcal_calibrate_patch_atsysheader(&
             backcal%chopperset(iset,ipix),&
             backcal%atsys%chunkset(iset,ipix),&
             error)
     enddo ! iset
  enddo ! ipix
  !
end subroutine mrtcal_calibrate_chopperset2chunkset
!
subroutine mrtcal_calibrate_chopperset_cross(hh,vv,hv,vh,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chopperset_cross
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(chopper_t),  intent(in)    :: hh
  type(chopper_t),  intent(in)    :: vv
  type(chopper_t),  intent(inout) :: hv
  type(chopper_t),  intent(inout) :: vh
  logical,          intent(inout) :: error
  ! Local
  integer(kind=size_length) :: ichunk
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET>CROSS'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ichunk=1,hh%n
     hv%atms(ichunk)%h2omm    = arithmetic(hh%atms(ichunk)%h2omm,   vv%atms(ichunk)%h2omm)
     hv%atms(ichunk)%taus%tot = arithmetic(hh%atms(ichunk)%taus%tot,vv%atms(ichunk)%taus%tot)
     hv%atms(ichunk)%taui%tot = arithmetic(hh%atms(ichunk)%taui%tot,vv%atms(ichunk)%taui%tot)
     hv%atms(ichunk)%temp%s   = geometric( hh%atms(ichunk)%temp%s,  vv%atms(ichunk)%temp%s)
     hv%atms(ichunk)%temp%i   = geometric( hh%atms(ichunk)%temp%i,  vv%atms(ichunk)%temp%i)
     hv%recs(ichunk)%temp     = geometric( hh%recs(ichunk)%temp,    vv%recs(ichunk)%temp)
     hv%tcals(ichunk)%s       = geometric( hh%tcals(ichunk)%s,      vv%tcals(ichunk)%s)
     hv%atsyss(ichunk)%s      = geometric( hh%atsyss(ichunk)%s,     vv%atsyss(ichunk)%s)
     hv%tsyss(ichunk)%s       = geometric( hh%tsyss(ichunk)%s,      vv%tsyss(ichunk)%s)
     hv%errors(ichunk)        = hv%errors(ichunk).and.vh%errors(ichunk)
     !
     vh%atms(ichunk)%h2omm    = hv%atms(ichunk)%h2omm
     vh%atms(ichunk)%taus%tot = hv%atms(ichunk)%taus%tot
     vh%atms(ichunk)%taui%tot = hv%atms(ichunk)%taui%tot
     vh%atms(ichunk)%temp%s   = hv%atms(ichunk)%temp%s
     vh%atms(ichunk)%temp%i   = hv%atms(ichunk)%temp%i
     vh%recs(ichunk)%temp     = hv%recs(ichunk)%temp
     vh%tcals(ichunk)%s       = hv%tcals(ichunk)%s
     vh%atsyss(ichunk)%s      = hv%atsyss(ichunk)%s
     vh%tsyss(ichunk)%s       = hv%tsyss(ichunk)%s
     vh%errors(ichunk)        = hv%errors(ichunk)
  enddo ! ichunk
  !
contains
  function geometric(val1,val2)
    ! Return the geometric mean dealing correctly with bad values
    real(kind=8) :: geometric  ! Function value on return
    real(kind=8), intent(in) :: val1,val2
    if ((val1.eq.hh%bad).or.(val2.eq.vv%bad)) then
       geometric = class_bad
    else
       if ((val1.ge.0).and.(val2.ge.0)) then
          geometric = sqrt(val1*val2)
       else
          geometric = class_bad
       endif
    endif
  end function geometric
  function arithmetic(val1,val2)
    ! Return the arithmetic mean, dealing correctly with bad values
    real(kind=8) :: arithmetic  ! Function value on return
    real(kind=8), intent(in) :: val1,val2
    if ((val1.eq.hh%bad).or.(val2.eq.vv%bad)) then
       arithmetic = class_bad
    else
       arithmetic = 0.5*(val1+val2)
    endif
  end function arithmetic
end subroutine mrtcal_calibrate_chopperset_cross
!
subroutine mrtcal_calibrate_chopperset2loadheader(chopperset,chunkset,error)
  use gbl_message
  use phys_const
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chopperset2loadheader
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(chopper_t),  intent(in)    :: chopperset
  type(chunkset_t), intent(inout) :: chunkset
  logical,          intent(inout) :: error
  ! Local
  integer(kind=4), parameter :: isky  = 1
  integer(kind=4), parameter :: ihot  = 2
  integer(kind=4), parameter :: icold = 3
  integer(kind=size_length) :: ichunk
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET2LOADHEADER'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ichunk=1,chunkset%n
     ! *** JP There is redundancy between count1 and these counts... We could avoid this...
     chunkset%chunks(ichunk)%cal%count(isky)  = chopperset%loads(ichunk)%sky_count
     chunkset%chunks(ichunk)%cal%count(ihot)  = chopperset%loads(ichunk)%hot%count
     chunkset%chunks(ichunk)%cal%count(icold) = chopperset%loads(ichunk)%cold%count
     !
     chunkset%chunks(ichunk)%cal%h2omm = chopper_value(chopperset%atms(ichunk)%h2omm)
     chunkset%chunks(ichunk)%cal%tatms = chopper_value(chopperset%atms(ichunk)%temp%s)
     chunkset%chunks(ichunk)%cal%tatmi = chopper_value(chopperset%atms(ichunk)%temp%i)
     chunkset%chunks(ichunk)%cal%taus = chopper_value(chopperset%atms(ichunk)%taus%tot)
     chunkset%chunks(ichunk)%cal%taui = chopper_value(chopperset%atms(ichunk)%taui%tot)
     chunkset%chunks(ichunk)%cal%trec = chopper_value(chopperset%recs(ichunk)%temp)
     chunkset%chunks(ichunk)%cal%atfac = chopper_value(chopperset%tcals(ichunk)%s)
     !
     ! *** JP Next one should be something cleverer
     chunkset%chunks(ichunk)%cal%cmode = 0
     chunkset%chunks(ichunk)%gen%tau = chopper_value(chopperset%atms(ichunk)%taus%tot)
     chunkset%chunks(ichunk)%gen%tsys = chopper_value(chopperset%tsyss(ichunk)%s)
  enddo ! ichunk
  !
contains
  function chopper_value(val)
    ! Return the input value, dealing correctly with bad values
    real(kind=8) :: chopper_value  ! Function value on return
    real(kind=8), intent(in) :: val
    if (val.eq.chopperset%bad) then
      chopper_value = class_bad
    else
      chopper_value = val
    endif
  end function chopper_value
end subroutine mrtcal_calibrate_chopperset2loadheader
!
subroutine mrtcal_calibrate_patch_atsysheader(chopperset,chunkset,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_patch_atsysheader
  !---------------------------------------------------------------------
  ! @ private
  ! Ad-hoc patch to replace gen%tsys in the chunk header as the
  ! attenuated Tsys from chopper type.
  !---------------------------------------------------------------------
  type(chopper_t),  intent(in)    :: chopperset
  type(chunkset_t), intent(inout) :: chunkset
  logical,          intent(inout) :: error
  ! Local
  integer(kind=size_length) :: ichunk
  character(len=*), parameter :: rname='CALIBRATE>PATCH>ATSYSHEADER'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ichunk=1,chunkset%n
    if (chopperset%atsyss(ichunk)%s.eq.chopperset%bad) then
      chunkset%chunks(ichunk)%gen%tsys = class_bad
    else
      chunkset%chunks(ichunk)%gen%tsys = chopperset%atsyss(ichunk)%s
    endif
  enddo ! ichunk
  !
end subroutine mrtcal_calibrate_patch_atsysheader
!
subroutine mrtcal_calibrate_chopperset2calarray(chopperset,ref,trec,tcal,  &
  atsys,tsys,water,ztau,flag,error)
  use gbl_message
  use phys_const
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chopperset2calarray
  use mrtcal_buffers
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(chopper_t),  intent(in)    :: chopperset
  type(chunkset_t), intent(in)    :: ref
  type(chunkset_t), intent(inout) :: trec
  type(chunkset_t), intent(inout) :: tcal
  type(chunkset_t), intent(inout) :: atsys
  type(chunkset_t), intent(inout) :: tsys
  type(chunkset_t), intent(inout) :: water
  type(chunkset_t), intent(inout) :: ztau
  type(chunkset_t), intent(inout) :: flag
  logical,          intent(inout) :: error
  ! Local
  integer(kind=size_length) :: ichunk
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET2CALARRAY'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ichunk=1,ref%n
    !
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),trec%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),tcal%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),atsys%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),tsys%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),water%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),ztau%chunks(ichunk),error)
    if (error) return
    call mrtcal_chunk_copy_header(ref%chunks(ichunk),flag%chunks(ichunk),error)
    if (error) return
    !
    trec%chunks(ichunk)%pos%sourc = 'TREC'
    tcal%chunks(ichunk)%pos%sourc = 'TCAL'
    atsys%chunks(ichunk)%pos%sourc = 'ATSYS'
    tsys%chunks(ichunk)%pos%sourc = 'TSYS'
    water%chunks(ichunk)%pos%sourc = 'WATER'
    ztau%chunks(ichunk)%pos%sourc = 'TAUZEN'
    flag%chunks(ichunk)%pos%sourc = 'FLAG'
    !
    ! Initialize data1(:), dataw(:), datac. data1(:) is initialized as class_bad
    ! for safety, but actual values are set in mrtcal_calibrate_chopperset_interpolate
    call mrtcal_chunk_init_data(trec%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    call mrtcal_chunk_init_data(tcal%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    call mrtcal_chunk_init_data(atsys%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    call mrtcal_chunk_init_data(tsys%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    call mrtcal_chunk_init_data(water%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    call mrtcal_chunk_init_data(ztau%chunks(ichunk),class_bad,0.,0.,error)
    if (error) return
    ! 'flag' values are set here:
    if (chopperset%errors(ichunk)) then
      call mrtcal_chunk_init_data(flag%chunks(ichunk),1.,0.,0.,error)
    else
      call mrtcal_chunk_init_data(flag%chunks(ichunk),0.,0.,0.,error)
    endif
    if (error) return
  enddo ! ichunk
  !
  call mrtcal_calibrate_chopperset_interpolate(chopperset,ref%n,rsetup,  &
    trec,tcal,atsys,tsys,water,ztau,error)
  if (error)  return
  !
end subroutine mrtcal_calibrate_chopperset2calarray
!
subroutine mrtcal_calibrate_chopperset_interpolate(chopperset,nchunk,mrtset,  &
  trec,tcal,atsys,tsys,water,ztau,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calibrate_chopperset_interpolate
  use mrtcal_calib_types
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !  Interpolate the calibration products (Trec, Tcal, Tsys, Water,
  ! Ztau) per chunk and per channels, according to the desired
  ! interpolation mode.
  !---------------------------------------------------------------------
  type(chopper_t),      intent(in)    :: chopperset  !
  integer(kind=4),      intent(in)    :: nchunk      ! Number of chunks
  type(mrtcal_setup_t), intent(in)    :: mrtset      !
  type(chunkset_t),     intent(inout) :: trec        !
  type(chunkset_t),     intent(inout) :: tcal        !
  type(chunkset_t),     intent(inout) :: atsys       !
  type(chunkset_t),     intent(inout) :: tsys        !
  type(chunkset_t),     intent(inout) :: water       !
  type(chunkset_t),     intent(inout) :: ztau        !
  logical,              intent(inout) :: error       !
  ! Local
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET2TSYSARRAY'
  !
  select case (mrtset%cal%interpprod)
  case (interp_nearest)
    call mrtcal_calibrate_chopperset_interpolate_nearest(chopperset,nchunk,  &
      trec,tcal,atsys,tsys,water,ztau,error)
    !
  case (interp_linear)
    call mrtcal_calibrate_chopperset_interpolate_linear(chopperset,nchunk,  &
      trec,tcal,atsys,tsys,water,ztau,error)
    !
  case (interp_spline)
    call mrtcal_calibrate_chopperset_interpolate_spline(chopperset,nchunk,  &
      trec,tcal,atsys,tsys,water,ztau,error)
    !
  case default
    call mrtcal_message(seve%e,rname,'Mode not implemented')
    error = .true.
    return
  end select
  !
end subroutine mrtcal_calibrate_chopperset_interpolate
!
subroutine mrtcal_calibrate_chopperset_interpolate_nearest(chopperset,nchunk,  &
  trec,tcal,atsys,tsys,water,ztau,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calibrate_chopperset_interpolate_nearest
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  !  Interpolate the calibration products (Trec, Tcal, Tsys, Water,
  ! Ztau) per chunk and per channels, using nearest value.
  !---------------------------------------------------------------------
  type(chopper_t),      intent(in)    :: chopperset  !
  integer(kind=4),      intent(in)    :: nchunk      ! Number of chunks
  type(chunkset_t),     intent(inout) :: trec        !
  type(chunkset_t),     intent(inout) :: tcal        !
  type(chunkset_t),     intent(inout) :: atsys       !
  type(chunkset_t),     intent(inout) :: tsys        !
  type(chunkset_t),     intent(inout) :: water       !
  type(chunkset_t),     intent(inout) :: ztau        !
  logical,              intent(inout) :: error       !
  ! Local
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET>INTERPOLATE>NEAREST'
  integer(kind=4) :: ichunk
  !
  do ichunk=1,nchunk
    trec%chunks(ichunk)%data1(:) = chopper_value(chopperset%recs(ichunk)%temp)
    tcal%chunks(ichunk)%data1(:) = chopper_value(chopperset%tcals(ichunk)%s)
    atsys%chunks(ichunk)%data1(:) = chopper_value(chopperset%atsyss(ichunk)%s)
    tsys%chunks(ichunk)%data1(:) = chopper_value(chopperset%tsyss(ichunk)%s)
    water%chunks(ichunk)%data1(:) = chopper_value(chopperset%atms(ichunk)%h2omm)
    ztau%chunks(ichunk)%data1(:) = chopper_value(chopperset%atms(ichunk)%taus%tot)
  enddo
  !
contains
  !
  function chopper_value(val)
    ! Return the input value, dealing correctly with bad values
    real(kind=4) :: chopper_value  ! Function value on return
    real(kind=8), intent(in) :: val
    if (val.eq.chopperset%bad) then
      chopper_value = class_bad
    else
      chopper_value = val
    endif
  end function chopper_value
  !
end subroutine mrtcal_calibrate_chopperset_interpolate_nearest
!
subroutine mrtcal_calibrate_chopperset_interpolate_linear(chopperset,nchunk,  &
  trec,tcal,atsys,tsys,water,ztau,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calibrate_chopperset_interpolate_linear
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  !  Interpolate the calibration products (Trec, Tcal, Tsys, Water,
  ! Ztau) per chunk and per channels, using linear interpolation.
  !---------------------------------------------------------------------
  type(chopper_t),      intent(in)    :: chopperset  !
  integer(kind=4),      intent(in)    :: nchunk      ! Number of chunks
  type(chunkset_t),     intent(inout) :: trec        !
  type(chunkset_t),     intent(inout) :: tcal        !
  type(chunkset_t),     intent(inout) :: atsys       !
  type(chunkset_t),     intent(inout) :: tsys        !
  type(chunkset_t),     intent(inout) :: water       !
  type(chunkset_t),     intent(inout) :: ztau        !
  logical,              intent(inout) :: error       !
  ! Local
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET>INTERPOLATE>LINEAR'
  integer(kind=4) :: ichunk,ip,ic,in
  real(kind=8) :: freqs(nchunk)  ! Automatic array. Inefficient?
  integer(kind=4) :: it(nchunk)  ! Automatic array. Inefficient?
  real(kind=8) :: fp,trecp,tcalp,atsysp,tsysp,waterp,ztaup
  real(kind=8) :: fn,trecn,tcaln,atsysn,tsysn,watern,ztaun
  !
  ! Sort chunks by ascending frequencies. Warning: chopperset frequencies
  ! are in GHz!
  freqs(:) = chopperset%freqs(:)%s
  call gr8_trie(freqs,it,nchunk,error)
  !
  do ichunk=1,nchunk
    ic = it(ichunk)  ! Current
    !
    if (ichunk.eq.1) then
      fp = 0.d0
      trecp = chopperset%bad
      tcalp = chopperset%bad
      atsysp = chopperset%bad
      tsysp = chopperset%bad
      waterp = chopperset%bad
      ztaup = chopperset%bad
    else
      ip = it(ichunk-1)
      fp = chopperset%freqs(ip)%s
      trecp = chopperset%recs(ip)%temp
      tcalp = chopperset%tcals(ip)%s
      atsysp = chopperset%atsyss(ip)%s
      tsysp = chopperset%tsyss(ip)%s
      waterp = chopperset%atms(ip)%h2omm
      ztaup = chopperset%atms(ip)%taus%tot
    endif
    if (ichunk.eq.nchunk) then
      fn = 0.d0
      trecn = chopperset%bad
      tcaln = chopperset%bad
      atsysn = chopperset%bad
      tsysn = chopperset%bad
      watern = chopperset%bad
      ztaun = chopperset%bad
    else
      in = it(ichunk+1)
      fn = chopperset%freqs(in)%s
      trecn = chopperset%recs(in)%temp
      tcaln = chopperset%tcals(in)%s
      atsysn = chopperset%atsyss(in)%s
      tsysn = chopperset%tsyss(in)%s
      watern = chopperset%atms(in)%h2omm
      ztaun = chopperset%atms(in)%taus%tot
    endif
    !
    call interp_chunk(fp,   chopperset%freqs(ic)%s,      fn,     &
                      trecp,chopperset%recs(ic)%temp,    trecn,  &
                      trec%chunks(ic))
    call interp_chunk(fp,   chopperset%freqs(ic)%s,      fn,     &
                      tcalp,chopperset%tcals(ic)%s,      tcaln,  &
                      tcal%chunks(ic))
    call interp_chunk(fp,    chopperset%freqs(ic)%s,     fn,     &
                      atsysp,chopperset%atsyss(ic)%s,    atsysn, &
                      atsys%chunks(ic))
    call interp_chunk(fp,   chopperset%freqs(ic)%s,      fn,     &
                      tsysp,chopperset%tsyss(ic)%s,      tsysn,  &
                      tsys%chunks(ic))
    call interp_chunk(fp,   chopperset%freqs(ic)%s,      fn,     &
                      waterp,chopperset%atms(ic)%h2omm,  watern, &
                      water%chunks(ic))
    call interp_chunk(fp,   chopperset%freqs(ic)%s,      fn,     &
                      ztaup,chopperset%atms(ic)%taus%tot,ztaun,  &
                      ztau%chunks(ic))
    !
  enddo
  !
contains
  !
  subroutine interp_chunk(fp,fc,fn,tp,tc,tn,chunk)
    real(kind=8),  intent(in)    :: fp,fc,fn  ! Freq previous, current, next
    real(kind=8),  intent(in)    :: tp,tc,tn  ! Temp previous, current, next
    type(chunk_t), intent(inout) :: chunk     !
    ! Local
    real(kind=8) :: ml,pl,mr,pr
    real(kind=8) :: freq
    logical :: noprev,nonext
    integer(kind=4) :: ichan
    !
    ! No interpolation for bad calibration chunks (where chopper failed)
    ! Already initialized as class_bad => values not modified
    if (tc.eq.chopperset%bad)  return
    !
    ! Left half
    if (tp.eq.chopperset%bad) then
      noprev = .true.
    else
      noprev = .false.
      call slope_and_offset(fc,fp,tc,tp,ml,pl)
    endif
    ! Right half
    if (tn.eq.chopperset%bad) then
      nonext = .true.
    else
      nonext = .false.
      call slope_and_offset(fc,fn,tc,tn,mr,pr)
    endif
    ! Special case for boundaries (extrapolate)
    if (noprev.and.nonext) then
      ! No neighbours: flat value
      chunk%data1(:) = tc
      return
    elseif (noprev) then
      ml = mr
      pl = pr
    elseif (nonext) then
      mr = ml
      pr = pl
    endif
    !
    do ichan=1,chunk%spe%nchan
      call abscissa_chan2sigabs(chunk%spe,real(ichan,kind=8),freq)
      if (freq.le.fc*1d3) then
        chunk%data1(ichan) = ml*freq+pl
      else
        chunk%data1(ichan) = mr*freq+pr
      endif
    enddo
    !
  end subroutine interp_chunk
  !
  subroutine slope_and_offset(f1,f2,y1,y2,m,p)
    real(kind=8), intent(in)  :: f1,f2  ! [GHz]     Frequencies
    real(kind=8), intent(in)  :: y1,y2  ! [Any]     Intensities
    real(kind=8), intent(out) :: m      ! [Any/MHz] Slope
    real(kind=8), intent(out) :: p      ! [Any]     Offset
    m = (y1-y2)/(f1-f2) / 1d3  ! GHz to MHz
    p = y1 - m*f1*1d3
  end subroutine slope_and_offset
  !
end subroutine mrtcal_calibrate_chopperset_interpolate_linear
!
subroutine mrtcal_calibrate_chopperset_interpolate_spline(chopperset,nchunk,  &
  trec,tcal,atsys,tsys,water,ztau,error)
  use gbl_message
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this=>mrtcal_calibrate_chopperset_interpolate_spline
  use mrtcal_calib_types
  !---------------------------------------------------------------------
  ! @ private
  !  Interpolate the calibration products (Trec, Tcal, Tsys, Water,
  ! Ztau) per chunk and per channels, using cubic spline interpolation.
  !---------------------------------------------------------------------
  type(chopper_t),      intent(in)    :: chopperset  !
  integer(kind=4),      intent(in)    :: nchunk      ! Number of chunks
  type(chunkset_t),     intent(inout) :: trec        !
  type(chunkset_t),     intent(inout) :: tcal        !
  type(chunkset_t),     intent(inout) :: atsys       !
  type(chunkset_t),     intent(inout) :: tsys        !
  type(chunkset_t),     intent(inout) :: water       !
  type(chunkset_t),     intent(inout) :: ztau        !
  logical,              intent(inout) :: error       !
  ! Local
  character(len=*), parameter :: rname='CALIBRATE>CHOPPERSET>INTERPOLATE>SPLINE'
  integer(kind=4) :: ichunk,ic
  real(kind=8) :: reffreq
  ! Automatic arrays. Inefficient?
  real(kind=8) :: freqs(nchunk)
  integer(kind=4) :: it(nchunk)
  real(kind=8) :: myfreqs(nchunk),mytrec(nchunk),mytcal(nchunk),myatsys(nchunk),mytsys(nchunk)
  real(kind=8) :: mywater(nchunk),myztau(nchunk)
  !
  ! Sort chunks by ascending frequencies. Warning: chopperset frequencies
  ! are in GHz!
  freqs(:) = chopperset%freqs(:)%s
  call gr8_trie(freqs,it,nchunk,error)
  !
  reffreq = freqs(1)
  do ichunk=1,nchunk
    ic = it(ichunk)  ! Current
    myfreqs(ichunk) = freqs(ichunk)-reffreq  ! Already sorted, offsets because single precision
    mytrec(ichunk)  = chopperset%recs(ic)%temp
    mytcal(ichunk)  = chopperset%tcals(ic)%s
    mytsys(ichunk)  = chopperset%tsyss(ic)%s
    myatsys(ichunk) = chopperset%atsyss(ic)%s
    mywater(ichunk) = chopperset%atms(ic)%h2omm
    myztau(ichunk)  = chopperset%atms(ic)%taus%tot
  enddo
  !
  call interp_chunks1(nchunk,myfreqs,mytrec, it,trec%chunks, error)
  if (error)  return
  call interp_chunks1(nchunk,myfreqs,mytcal, it,tcal%chunks, error)
  if (error)  return
  call interp_chunks1(nchunk,myfreqs,myatsys,it,atsys%chunks,error)
  if (error)  return
  call interp_chunks1(nchunk,myfreqs,mytsys, it,tsys%chunks, error)
  if (error)  return
  call interp_chunks1(nchunk,myfreqs,mywater,it,water%chunks,error)
  if (error)  return
  call interp_chunks1(nchunk,myfreqs,myztau, it,ztau%chunks, error)
  if (error)  return
  !
contains
  !
  subroutine interp_chunks1(nchunk,freqs,vals,it,chunks,error)
    ! Spline interpolation of a discontiguous serie of chunks
    integer(kind=4), intent(in)    :: nchunk
    real(kind=8),    intent(in)    :: freqs(nchunk)  ! X: must be sorted
    real(kind=8),    intent(inout) :: vals(nchunk)   ! Y: associated to X
    integer(kind=4), intent(in)    :: it(nchunk)     ! Sorting array for chunks()
    type(chunk_t),   intent(inout) :: chunks(nchunk) ! Unsorted
    logical,         intent(inout) :: error
    ! Local
    integer(kind=size_length) :: nxy,is,ne,in
    integer(kind=4) :: ic,nc
    !
    in = 1
    nxy = nchunk
    do while (in.ne.0)
      call find_blank8(vals,chopperset%bad,0.d0,nxy,is,ne,in)
      if (ne.le.1) then
        ! 1 point: flat value
        ic = it(is)
        chunks(ic)%data1(:) = vals(is)
      else
        ! 2 or more points: spline interpolation
        nc = ne
        call interp_chunks2(nc,freqs(is:),vals(is:),it(is:),chunks,error)
        if (error)  return
      endif
    enddo
    !
  end subroutine interp_chunks1
  !
  subroutine interp_chunks2(nchunk,freqs,vals,it,chunks,error)
    ! Spline interpolation of a contiguous serie of chunks
    integer(kind=4), intent(in)    :: nchunk
    real(kind=8),    intent(in)    :: freqs(nchunk)  ! X: must be sorted
    real(kind=8),    intent(inout) :: vals(nchunk)   ! Y: associated to X
    integer(kind=4), intent(in)    :: it(nchunk)     ! Sorting array for chunks()
    type(chunk_t),   intent(inout) :: chunks(*)      ! All the unsorted chunks
    logical,         intent(inout) :: error
    ! Local
    real(kind=8) :: c1(nchunk),c2(nchunk),c3(nchunk)
    integer(kind=4) :: bc,ichunk,ic,ichan,jfreq
    real(kind=8) :: freq
    !
    ! Boundary conditions: spline is evaluated WITHIN the N-1 intervals
    ! provided by the N frequencies. For the 1st half of the first chunk,
    ! we are missing spline coefficients. It is chosen to extrapolate
    ! the spline evaluation from the coefficients of the neighbour
    ! interval. However, as they provide a cubic function, the evaluation
    ! can diverge very rapidly. It is decided to control what happens at
    ! boundary thanks to cubspl8 tuning. Same choice for 2nd half of the
    ! last chunk.
    !
    bc = 2       ! Boundary condition == 2: force 2nd derivatives to c1(1)
                 ! and c1(N) at first and last boundaries resp.
    c1(:) = 0.d0 ! c1(1) == c2(N) == 0.: 2nd derivatives forced to 0 at
                 ! boundary points. In other words this adds an inflection
                 ! point which will strongly limit the divergence of the
                 ! spline beyond the neighbour interval (as we extrapolate
                 ! only over an half-chunk).
    !
    call cubspl8(nchunk,freqs,vals,c1,c2,c3,bc,bc,error)
    if (error)  return
    ! In return we get spline coefficients for nchunk-1 intervals
    !
    do ichunk=1,nchunk
      ic = it(ichunk)  ! Iterate the chunks in ascending frequency
      !
      do ichan=1,chunks(ic)%spe%nchan
        call abscissa_chan2sigabs(chunks(ic)%spe,real(ichan,kind=8),freq)
        freq = freq/1d3-reffreq
        !
        ! Find in which spline interval we lie. For half interval beyond
        ! boundary, reuse the coefficient of nearest interval.
        if (freq.lt.freqs(ichunk)) then
          jfreq = max(1,ichunk-1)
        else
          jfreq = min(ichunk,nchunk-1)
        endif
        !
        freq = freq - freqs(jfreq)
        chunks(ic)%data1(ichan)  = spline_eval(jfreq,freq,vals,c1,c2,c3)
        !
      enddo
      !
    enddo
    !
  end subroutine interp_chunks2
  !
  function spline_eval(in,h,c0,c1,c2,c3)
    ! See details in subroutine cubspl4
    real(kind=8) :: spline_eval    ! Function value on return
    integer(kind=4), intent(in) :: in  ! Interval number
    real(kind=8),    intent(in) :: h   ! X offset from start of interval
    real(kind=8),    intent(in) :: c0(*),c1(*),c2(*),c3(*)  ! Spline coeffs for all intervals
    spline_eval = c0(in) + h * ( c1(in) + h * (c2(in) + h*c3(in)/3.d0)/2.d0 )
  end function spline_eval
  !
end subroutine mrtcal_calibrate_chopperset_interpolate_spline
!
subroutine mrtcal_calibrate_average_load(mrtset,name,imbf,subscanbuf,load,error)
  use gbl_message
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_average_load
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),   intent(in)    :: mrtset
  character(len=*),       intent(in)    :: name
  type(imbfits_t),        intent(in)    :: imbf
  type(subscan_buffer_t), intent(inout) :: subscanbuf
  type(chunkset_2d_t),    intent(inout) :: load
  logical,                intent(inout) :: error
  ! Local
  logical, parameter :: iscal = .true.
  integer(kind=4), parameter :: first = 1
  character(len=*), parameter :: rname='CALIBRATE>AVERAGE>LOAD'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_average_times(mrtset,iscal,first,name,imbf,subscanbuf,load,error)
  if (error) return
  call mrtcal_chunkset_2d_modify_source(name,load,error)
  if (error) return
  call mrtcal_average_channels(load,error)
  if (error) return
  !
end subroutine mrtcal_calibrate_average_load
!
subroutine mrtcal_calibrate_grid(mrtset,imbf,subscanbuf,backcal,error)
  use gbl_message
  use mrtcal_buffer_types
  use mrtcal_setup_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_grid
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtcal_setup_t),   intent(in)    :: mrtset
  type(imbfits_t),        intent(in)    :: imbf
  type(subscan_buffer_t), intent(inout) :: subscanbuf
  type(calib_backend_t),  intent(inout) :: backcal
  logical,                intent(inout) :: error
  ! Local
  logical, parameter :: iscal = .true.
  integer(kind=4), parameter :: first = 1
  character(len=*), parameter :: rname='CALIBRATE>GRID'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call mrtcal_average_times(mrtset,iscal,first,'calgrid',imbf,subscanbuf,backcal%grid,error)
  if (error) return
  call mrtcal_chunkset_2d_calgrid(backcal%chunksetlist,backcal%grid,backcal%amppha,backcal%sincos,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_cross,backcal%atsys,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_cross,backcal%tsys,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_cross,backcal%tcal,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_cross,backcal%trec,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_mean,backcal%water,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_mean,backcal%ztau,error)
  if (error) return
  call mrtcal_chunkset_2d_cross(backcal%chunksetlist,mrtcal_chunk_flag,backcal%flag,error)
  if (error) return
  !
end subroutine mrtcal_calibrate_grid
!
subroutine mrtcal_chunkset_2d_calgrid(chunksetlist,grid_2d,amppha_2d,sincos_2d,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_chunkset_2d_calgrid
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(imbfits_back_chunks_t), intent(in)    :: chunksetlist
  type(chunkset_2d_t), target, intent(in)    :: grid_2d
  type(chunkset_2d_t), target, intent(inout) :: amppha_2d
  type(chunkset_2d_t), target, intent(inout) :: sincos_2d
  logical,                     intent(inout) :: error
  ! Local
  type(stokesset_book_t) :: book
  type(chunkset_t), pointer :: real,imag,amp,pha,cos,sin
  integer(kind=size_length) :: ipix,ichunk
  character(len=*), parameter :: rname='CHUNKSET2D>CALGRID'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  call clone_chunkset_2d(grid_2d,amppha_2d,.true.,error)
  if (error) return
  call clone_chunkset_2d(grid_2d,sincos_2d,.true.,error)
  if (error) return
  !
  do ipix=1,grid_2d%npix
     call imbfits_init_stokesloop(book,error)
     if (error) return
     do while (.true.)
        call imbfits_get_next_stokesset(chunksetlist,book,error)
        if (error) return
        if (.not.book%found) exit
        if (book%nstokes.ne.4) cycle
        real => grid_2d%chunkset(book%iset(code_r),ipix)
        imag => grid_2d%chunkset(book%iset(code_i),ipix)
        amp  => amppha_2d%chunkset(book%iset(code_r),ipix)
        pha  => amppha_2d%chunkset(book%iset(code_i),ipix)
        cos  => sincos_2d%chunkset(book%iset(code_r),ipix)
        sin  => sincos_2d%chunkset(book%iset(code_i),ipix)
        do ichunk=1,real%n
           call mrtcal_chunk_calgrid(&
                real%chunks(ichunk),&
                imag%chunks(ichunk),&
                amp%chunks(ichunk),&
                pha%chunks(ichunk),&
                cos%chunks(ichunk),&
                sin%chunks(ichunk),&
                error)
           if (error) return
        enddo ! ichunk
     enddo ! istokesset
  enddo ! ipix
  !
end subroutine mrtcal_chunkset_2d_calgrid
!
subroutine mrtcal_chunkset_2d_cross(chunksetlist,operation,chunkset_2d,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_chunkset_2d_cross
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(imbfits_back_chunks_t), intent(in)    :: chunksetlist
  type(chunkset_2d_t), target, intent(inout) :: chunkset_2d
  logical,                     intent(inout) :: error
  external :: operation
  ! Local
  type(stokesset_book_t) :: book
  type(chunkset_t), pointer :: h,v,hv,vh
  integer(kind=size_length) :: ipix,ichunk
  character(len=*), parameter :: rname='CHUNKSET2D>CROSS'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  do ipix=1,chunkset_2d%npix
     call imbfits_init_stokesloop(book,error)
     if (error) return
     do while (.true.)
        call imbfits_get_next_stokesset(chunksetlist,book,error)
        if (error) return
        if (.not.book%found) exit
        if (book%nstokes.eq.1) cycle
        h  => chunkset_2d%chunkset(book%iset(code_h),ipix)
        v  => chunkset_2d%chunkset(book%iset(code_v),ipix)
        hv => chunkset_2d%chunkset(book%iset(code_r),ipix)
        vh => chunkset_2d%chunkset(book%iset(code_i),ipix) 
        do ichunk=1,h%n
           call operation(&
                h%chunks(ichunk),&
                v%chunks(ichunk),&
                hv%chunks(ichunk),&
                vh%chunks(ichunk),&
                error)
           if (error) return
        enddo ! ichunk
     enddo ! istokesset
  enddo ! ipix
  !
end subroutine mrtcal_chunkset_2d_cross
!
subroutine mrtcal_chunkset_2d_modify_source(source,chunkset_2d,error)
  use gbl_message
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_chunkset_2d_modify_source
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  character(len=*),    intent(in)    :: source
  type(chunkset_2d_t), intent(inout) :: chunkset_2d
  logical,             intent(inout) :: error
  ! Local
  character(len=12) :: mysource
  type(chunkset_t), pointer :: chunkset
  integer(kind=size_length) :: ipix,iset,ichunk
  character(len=*), parameter :: rname='CHUNKSET2D>MODIFY>SOURCE'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  mysource = source  ! 12 characters
  call sic_upper(mysource)
  !
  do ipix=1,chunkset_2d%npix
     do iset=1,chunkset_2d%nset
        chunkset => chunkset_2d%chunkset(iset,ipix)
        do ichunk=1,chunkset%n
           chunkset%chunks(ichunk)%pos%sourc = mysource
        enddo ! ichunk
     enddo ! iset
  enddo ! ipix
  !
end subroutine mrtcal_chunkset_2d_modify_source
!
subroutine mrtcal_gr8_median(data,ndata,bval,eval,median,error)
  use gildas_def
  use gkernel_interfaces
  !---------------------------------------------------------------------
  ! @ private-mandatory
  ! *** JP Trick because gr8_median does not return the blanking
  ! *** JP value when there is no valid data
  !---------------------------------------------------------------------
  integer(kind=size_length), intent(in)    :: ndata        ! Array size
  real(kind=8),              intent(in)    :: data(ndata)  ! Data
  real(kind=8),              intent(in)    :: bval         ! Blanking value
  real(kind=8),              intent(in)    :: eval         ! Tolerance
  real(kind=8),              intent(inout) :: median       ! Median of input array
  logical,                   intent(inout) :: error        ! Logical error flag
  !
  median = bval
  call gr8_median(data,ndata,bval,eval,median,error)
  if (error) return
end subroutine mrtcal_gr8_median
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
