!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_calib_calibration(mrtset,filebuf,backcal,error)
  use mrtcal_buffer_types
  use mrtcal_messaging
  use mrtcal_setup_types
  use imbfits_interfaces_public
  use mrtcal_interfaces, except_this => mrtcal_calib_calibration
  !---------------------------------------------------------------------
  ! @ private
  !
  !  1. Check the polarimetry status of the calibration scan.
  !  2. Read the calambient, calcold subscan in the order they
  !     appear in the IMBFITS and compute the mean counts over the channels.
  !  3. Read the calsky when it exists (it probably needs to rewind a bit
  !     the file before reading it) or mock it.
  !  4. Fill the chopper structure, compute the Tamt, Trec, Tsys, and Tcal,
  !     and fill the chunkset structure with these results.
  !  5. Calibrate the grid subscan when it is available.
  !  6. 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
  !
  logical :: calgridpresent
  logical :: poldatapresent
  character(len=*), parameter :: rname='CALIB>CALIBRATION'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Check polarimetry status of the calibration scan. Check whether
  ! 1. the scan contains a calgrid subscan,
  ! 2. the data contains polarimetry chunks. 
  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
  !
  ! Average the ambient, cold load.
  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
  ! The sky subscan may not be present
  if (imbfits_subscan_exists(filebuf%imbf,'calsky')) then
     ! Present => average it.
     call mrtcal_calibrate_average_load(mrtset,'calsky',filebuf%imbf,filebuf%subscanbuf,backcal%sky,error)
     if (failed_calibrate(rname,'calsky',error)) return
  else
     ! Absent => Create it.
     ! 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
  !
  ! First, calibrate the receiver and system temperature
  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,mrtset,error)
  if (error) return
  ! Second, calibrate the grid subscan
  if (backcal%polar) then
     call mrtcal_calibrate_grid(mrtset,filebuf%imbf,filebuf%subscanbuf,backcal,error)
     if (failed_calibrate(rname,'calgrid',error)) return
  endif
  !
  !***JP: Are the following tables different in the case of polarimetry data?
  !
  ! 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 chopper_definitions
  use mrtcal_messaging
  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 allinone%reallocate(backcal%sky%nset,backcal%sky%npix,9,error)
    if (error)  return
    !
    call allinone%reassociate(1,backcal%sky,error)
    if (error)  goto 10
    call allinone%reassociate(2,backcal%hot,error)
    if (error)  goto 10
    call allinone%reassociate(3,backcal%cold,error)
    if (error)  goto 10
    call allinone%reassociate(4,backcal%trec,error)
    if (error)  goto 10
    call allinone%reassociate(5,backcal%tcal,error)
    if (error)  goto 10
    call allinone%reassociate(6,backcal%tsys,error)
    if (error)  goto 10
    call allinone%reassociate(7,backcal%water,error)
    if (error)  goto 10
    call allinone%reassociate(8,backcal%ztau,error)
    if (error)  goto 10
    call allinone%reassociate(9,backcal%flag,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 allinone%free(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  HH VV REAL IMAG HH VV REAL IMAG
    !   #2  AMP  PHA  AMP  PHA
    !   #3  COS  SIN  COS  SIN
    ! We want to write 2 spectra with
    !   #1  HH VV REAL IMAG AMP PHA COS SIN
    !   #2  HH VV 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 allinone%reallocate(nstokesset,backcal%grid%npix,8,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 allinone%chunkset(istokesset,ipix,1)%reassociate(&
               backcal%grid%chunkset(book%iset(code_h),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,2)%reassociate(&
               backcal%grid%chunkset(book%iset(code_v),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,3)%reassociate(&
               backcal%grid%chunkset(book%iset(code_r),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,4)%reassociate(&
               backcal%grid%chunkset(book%iset(code_i),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,5)%reassociate(&
               backcal%amppha%chunkset(book%iset(code_r),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,6)%reassociate(&
               backcal%amppha%chunkset(book%iset(code_i),ipix),error)
          if (error) return
          call allinone%chunkset(istokesset,ipix,7)%reassociate(&
               backcal%sincos%chunkset(book%iset(code_r),ipix),error)
          if (error)  return
          call allinone%chunkset(istokesset,ipix,8)%reassociate(&
               backcal%sincos%chunkset(book%iset(code_i),ipix),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 mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calibrate_user_feedback
  use mrtcal_buffers
  use mrtcal_calib_types
  use mrtcal_setup_types
  use mrtcal_user_feedback_tool
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), target, intent(in)    :: backcal
  type(mrtcal_setup_output_t),   intent(in)    :: setout
  logical,                       intent(inout) :: error
  !
  integer(kind=4), parameter :: iperpixel = 1
  integer(kind=4), parameter :: iperset   = 2
  integer(kind=4), parameter :: iperchunk = 3
  integer(kind=4) :: ipix,iset,ncal
  type(user_feedback_t) :: user
  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%chopper%set(iset,ipix)%n
      enddo ! iset
    enddo ! ipix
  endif
  !
  call user%allocate_and_init(ncal,error)
  if (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
  !
  call user%sort_by_freq_idfe(error)
  if (error) return
  call user%list(error)
  if (error) return
  if (setout%restable) then
     ! Also display in a file
     call user%tofile(backcal%head,error)
     if (error) return
  endif
  !
contains
  !
  subroutine mrtcal_calibrate_user_feedback_head(error)
    use mrtcal_messaging
    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
    real(kind=8) :: mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o
    character(len=8) :: mfrontend
    character(len=message_length) :: result
    !
    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
      call calibrate_user_feedback_oneline(  &
        mfrontend,                 &
        mfreq,                     &
        mthot,                     &
        mtcold,                    &
        backcal%head%key%backend,  &
        ipix,                      &
        mtrec,                     &
        mtsys,                     &
        mtcal,                     &
        mtau,                      &
        mh2o,                      &
        result)
      call user%fill(ical,mfrontend,mfreq,result,code_color_default)
    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%chopper%set(iset,ipix)%n
      nelem = nelem+nc
    enddo
    !
    allocate(r8data(nelem))
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%freqs(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,freq,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%loads(1:nc)%hot%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,thot,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%loads(1:nc)%cold%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,tcold,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%recs(1:nc)%temp
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,trec,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%tsyss(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,tsys,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%tcals(1:nc)%s
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,tcal,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%atms(1:nc)%taus%tot
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(1,1)%bad,0.d0,tau,error)
    if (error)  return
    !
    nelem = 0
    do iset=1,backcal%sky%nset
      nc = backcal%chopper%set(iset,ipix)%n
      r8data(nelem+1:nelem+nc) = backcal%chopper%set(iset,ipix)%atms(1:nc)%h2omm
      nelem = nelem+nc
    enddo
    call gr8_compute(r8data,nelem,backcal%chopper%set(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,jpix
    real(kind=8) :: mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o
    character(len=8) :: mfrontend
    character(len=message_length) :: result
    !
    ical = 0
    do ipix=1,backcal%sky%npix
      do iset=1,backcal%sky%nset
          call calibrate_user_feedback_compute_perset(gr8_compute,  &
               backcal%chopper%set(iset,ipix),                       &
               mfreq,mthot,mtcold,mtrec,mtsys,mtcal,mtau,mh2o,error)
          if (error)  return
          !
          if (ipix.eq.1) then
            ! Either single-pixel data, or multi-pixel data read with
            ! flat pixel*chunkset dimension. See MRTCAL prog documentation
            ! for details.
            jpix = backcal%chunksetlist%eclass%val2(iset)
          else
            ! Multi-pixel data with pixel dimension properly separated from
            ! the chunkset dimension.
            jpix = ipix
          endif
          !
          ical = ical+1
          ! Assume consistent telescope name per chunksets, use first one
          mfrontend = backcal%sky%chunkset(iset,ipix)%chunks(1)%frontend
          call calibrate_user_feedback_oneline(  &
            mfrontend,                 &
            mfreq,                     &
            mthot,                     &
            mtcold,                    &
            backcal%head%key%backend,  &
            jpix,                      &
            mtrec,                     &
            mtsys,                     &
            mtcal,                     &
            mtau,                      &
            mh2o,                      &
            result)
          call user%fill(ical,mfrontend,mfreq,result,code_color_default)
      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
    real(kind=8) :: mfreq
    character(len=8) :: mfrontend
    character(len=message_length) :: result
    !
    ical = 0
    do ipix=1,backcal%sky%npix
      do iset=1,backcal%sky%nset
          chopperset => backcal%chopper%set(iset,ipix)
          !
          do ichunk=1,chopperset%n
            ical = ical+1
            mfrontend = backcal%sky%chunkset(iset,ipix)%chunks(ichunk)%frontend
            mfreq = chopperset%freqs(ichunk)%s
            call calibrate_user_feedback_oneline(  &
              mfrontend,                           &
              mfreq,                               &
              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,       &
              result)
            call user%fill(ical,mfrontend,mfreq,result,code_color_default)
         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
    !
    real(kind=8) :: bad
    !
    bad = backcal%chopper%set(1,1)%bad
    write(oneline,100)  frontend,&
                        user_feedback_format_onedouble(5,'f5.1',freq,bad),&
                        user_feedback_format_onedouble(8,'f8.3',thot,bad),&
                        user_feedback_format_onedouble(9,'f9.3',tcold,bad),&
                        mrtindex_backend(backend),&
                        ipix,&
                        user_feedback_format_onedouble(7,'f7.2',trec,bad),&
                        user_feedback_format_onedouble(7,'f7.2',tsys,bad),&
                        user_feedback_format_onedouble(7,'f7.2',tcal,bad),&
                        user_feedback_format_onedouble(5,'f5.2',tau,bad),&
                        user_feedback_format_onedouble(4,'f4.1',h2omm,bad)
    !
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
end subroutine mrtcal_calibrate_user_feedback
!
subroutine mrtcal_calibrate_compute_chopperset(backcal,error)
  use mrtcal_messaging
  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%chopper%set(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 mrtcal_messaging
  use mrtcal_dependencies_interfaces
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chunkset2chopperset
  use mrtcal_calib_types
  use mrtcal_setup_types
  use mrtcal_chopperset_tools
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), intent(inout), target :: backcal
  type(mrtcal_setup_t),  intent(in)            :: mrtset
  logical,               intent(inout)         :: error
  ! Local
  integer(kind=size_length) :: ipix,iset
  character(len=*), parameter :: rname='CALIBRATE>CHUNKSET2CHOPPERSET'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Check consistency of sky, hot, and cold
  call backcal%sky%consistency(rname,backcal%hot,error)
  if (error)  return
  call backcal%sky%consistency(rname,backcal%cold,error)
  if (error)  return
  !
  call backcal%chopper%reallocate(backcal%sky,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
      call mrtcal_calibrate_chunkset2chopper(  &
           mrtset,                             &
           backcal%sky%chunkset(iset,ipix),    &
           backcal%hot%chunkset(iset,ipix),    &
           backcal%cold%chunkset(iset,ipix),   &
           backcal%chopper%set(iset,ipix),     &
           backcal%chopper%book(iset,ipix),    &
           error)
      if (error)  return
    enddo
  enddo
end subroutine mrtcal_calibrate_chunkset2chopperset
!
subroutine mrtcal_calibrate_chopperset2chunkset(backcal,mrtset,error)
  use mrtcal_messaging
  use chopper_definitions
  use mrtcal_calib_types
  use mrtcal_interfaces, except_this => mrtcal_calibrate_chopperset2chunkset
  use mrtcal_chopperset_tools
  use mrtcal_setup_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(calib_backend_t), target, intent(inout) :: backcal
  type(mrtcal_setup_t),          intent(in)    :: mrtset
  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%chopper%set(book%iset(code_h),ipix) ! input
        vv => backcal%chopper%set(book%iset(code_v),ipix) ! input
        hv => backcal%chopper%set(book%iset(code_r),ipix) ! output
        vh => backcal%chopper%set(book%iset(code_i),ipix) ! output
        call mrtcal_calibrate_chopperset_cross(hh,vv,hv,vh,error)
        if (error) return
     enddo ! istokesset
  enddo ! ipix
  !
  call backcal%trec%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%tcal%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%atsys%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%tsys%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%water%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%ztau%clone(backcal%sky,.false.,error)
  if (error) return
  call backcal%flag%clone(backcal%sky,.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%chopper%set(iset,ipix),&
             backcal%chopper%book(iset,ipix),&
             backcal%sky%chunkset(iset,ipix),&
             error)
        call mrtcal_calibrate_chopperset2loadheader(&
             backcal%chopper%set(iset,ipix),&
             backcal%chopper%book(iset,ipix),&
             backcal%hot%chunkset(iset,ipix),&
             error)
        call mrtcal_calibrate_chopperset2loadheader(&
             backcal%chopper%set(iset,ipix),&
             backcal%chopper%book(iset,ipix),&
             backcal%cold%chunkset(iset,ipix),&
             error)
        ! Interpolate calibration results in frequency
        call mrtcal_calibrate_chopperset2calarray(&
             mrtset,&
             backcal%chopper%set(iset,ipix),&
             backcal%chopper%book(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%chopper%set(iset,ipix),&
             backcal%chopper%book(iset,ipix),&
             backcal%atsys%chunkset(iset,ipix),&
             error)
     enddo ! iset
  enddo ! ipix
end subroutine mrtcal_calibrate_chopperset2chunkset
!
subroutine mrtcal_calibrate_average_load(mrtset,name,imbf,subscanbuf,load,error)
  use mrtcal_messaging
  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 mrtcal_messaging
  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 mrtcal_messaging
  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 :: hh,vv,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 amppha_2d%clone(grid_2d,.true.,error)
  if (error) return
  call sincos_2d%clone(grid_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
        hh   =>   grid_2d%chunkset(book%iset(code_h),ipix)
        vv   =>   grid_2d%chunkset(book%iset(code_v),ipix)
        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(&
                hh%chunks(ichunk),&
                vv%chunks(ichunk),&
                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 mrtcal_messaging
  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 :: hh,vv,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
        hh => chunkset_2d%chunkset(book%iset(code_h),ipix)
        vv => 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,hh%n
           call operation(&
                hh%chunks(ichunk),&
                vv%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 mrtcal_messaging
  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
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
