subroutine uv_check_comm(line,error)
  use gkernel_interfaces
  use gbl_message
  use imager_interfaces, except_this => uv_check_comm
  use clean_types
  use clean_arrays
  use clean_beams
  !----------------------------------------------------------------------
  ! @ private
  !*
  ! IMAGER s-- Spport routine for command UV_CHECK
  !
  !     UV_CHECK [Beams|Flags|Integration|Nulls] [/FILE File]
  !!
  !----------------------------------------------------------------------
  character(len=*), intent(in) :: line   !! Command line
  logical, intent(out) :: error          !! Logical error flag
  !
  ! Constants
  character(len=*), parameter :: rname='UV_CHECK'
  integer, parameter :: o_file=1
  integer, parameter :: mmode=4
  character(len=12) :: smode(mmode)
  data smode /'BEAMS','FLAGS','INTEGRATION','NULLS'/
  !
  ! Local ---
  real, save :: inte(1000)
  real, save :: total=0.
  integer, save :: nant=0
  integer, save :: ni=0
  !
  integer(kind=index_length) :: dim(4), nvisi
  integer :: nu,nc,na,ncase, jcase, ier, i, iv, kv
  integer, allocatable :: cases(:), bad(:)
  real, allocatable :: weights(:)
  real(4) :: dtol, dmax
  character(len=12) :: argum,cmode
  character(len=filename_length) :: file
  type (gildas) :: uvin
  logical :: dofile, err
  integer :: ib, nblock, nf, wcol
  character(len=128) :: mess
  !
  ! Code ----
  argum = 'BEAMS'
  call sic_ke(line,0,1,argum,na,.false.,error)
  call sic_ambigs (rname,argum,cmode,na,smode,mmode,error)
  if (error) return
  !
  dofile = sic_present(o_file,0)
  ! 
  if (cmode.eq.'BEAMS') then
    call sic_r4(line,0,2,beam_tolerance,.false.,error)
    if (error) return
    if ((beam_tolerance.lt.1e-6).or.(beam_tolerance.gt.1.)) then
      call map_message(seve%e,rname,'BEAM_TOLERANCE out of range ]0,1]')
      error = .true.
      return
    endif
  endif
  !
  if (.not.dofile) then
    !
    !  Memory mode
    if (huv%loca%size.eq.0) then
      call map_message(seve%e,rname,'No UV data loaded')
      error = .true.
      return
    endif
    nu = huv%gil%dim(1)
    nvisi = huv%gil%nvisi ! not %dim(2)
    nc = huv%gil%nchan
    !
    !
    select case (cmode)
    case('BEAMS')
      nbeam_ranges = -1
      call check_beams_mem(error)
    case ('FLAGS')
      wcol = 0
      call sic_i4(line,0,2,wcol,.false.,error)
      if (error) return
      if (wcol.eq.0)  wcol = (nc+2)/3
      wcol = max(1,min(wcol,nc))
      write(mess,'(A,I0)') 'Checking for flagged visibilities on Channel ',wcol
      call map_message(seve%i,rname,mess)
      allocate (bad(nvisi),stat=ier)
      kv = 0
      call sub_get_flags(duv,nvisi,nc,bad,kv,wcol)
      if (kv.ne.0) then
        !write(6,*) 'Flagged ',kv,' visibilities'
        !write(6,*) (bad(iv),iv=1,kv)
        write(mess,'(I0,A,I0)') kv,' flagged visibilities for Channel ',wcol
        call map_message(seve%w,rname,mess)
      else
        call map_message(seve%i,rname,'No flagged visibilities')
      endif
      deallocate (bad,stat=ier)
    case ('NULLS')
      call map_message(seve%i,rname,'Checking for unflagged null visibilities')
      allocate (bad(nvisi),stat=ier)
      kv = 0
      call sub_get_nulls(duv,nvisi,nc,bad,kv)
      if (kv.ne.0) then
        write(6,*) 'Flagged ',kv,' null visibilities'
        write(6,*) (bad(iv),iv=1,kv)
      else
        call map_message(seve%i,rname,'No null visibilities left unflagged')
      endif
      deallocate (bad,stat=ier)
      !
    case ('INTEGRATION')
      if (nant.eq.0) then
        call sic_def_inte('UV_NANT',nant,0,0,.true.,error)
        call sic_def_real('UV_TOTAL',total,0,0,.true.,error)
      endif
      !
      dmax = 300  ! Seconds
      call sic_r4(line,0,2,dmax,.false.,error)
      dtol = 0.05
      call sic_r4(line,0,3,dtol,.false.,error)
      if (error) return
      !
      ni = 0
      total = 0
      nant = 0
      call sub_get_inte(huv,duv,nvisi,dmax,dtol,inte,ni,total,nant,error)
      if (error) return
      !
      call sic_delvariable('UV_INTEGRATION',.false.,error)
      call sic_def_real('UV_INTEGRATION',inte,1,ni,.true.,error)
      write(6,'(A)') 'Possible integration times (seconds) '
      write(6,'((8(1X,F10.2)))') inte(1:ni)
    end select
  else
    call gildas_null (uvin, type = 'UVT')     ! Define a UVTable gildas header
    !
    call sic_ch(line,o_file,1,file,nf,.true.,error)
    if (error)  return
    call gdf_read_gildas (uvin, file, '.uvt', error, data=.false.)
    if (error) return
    !
    ! Define blocking factor, on largest data file, usually the input one
    ! but not always...
    !
    ! Set the Blocking Factor
    nblock = space_nitems('SPACE_IMAGER',uvin,1)
    ! Allocate respective space for each file
    allocate (uvin%r2d(uvin%gil%dim(1),nblock),stat=ier)
    if (failed_allocate(rname,'UV input data',ier,error)) then
      call gdf_close_image(uvin,err)
      return
    endif
    !
    nu = uvin%gil%dim(1)
!    nv = uvin%gil%nvisi ! not %dim(2)
    nc = uvin%gil%nchan
    !
    ! Loop over line table
    uvin%blc = 0
    uvin%trc = 0
    !
    select case (cmode)
    case ('INTEGRATION')
      if (nant.eq.0) then
        call sic_def_inte('UV_NANT',nant,0,0,.true.,error)
        call sic_def_real('UV_TOTAL',total,0,0,.true.,error)
      endif
      !
      dmax = 300  ! Seconds
      call sic_r4(line,0,2,dmax,.false.,error)
      dtol = 0.05
      call sic_r4(line,0,3,dtol,.false.,error)
      if (error) return
      !
      ni = 0
      nant = 0
      total = 0.
      !
      do ib = 1,uvin%gil%dim(2),nblock
        write(mess,*) ib,' / ',uvin%gil%dim(2),nblock
        call map_message(seve%i,rname,mess)
        uvin%blc(2) = ib
        uvin%trc(2) = min(uvin%gil%dim(2),ib-1+nblock)
        call gdf_read_data(uvin,uvin%r2d,error)
        if (error) exit
        !
        nvisi = uvin%trc(2)-uvin%blc(2)+1
        !
        call sub_get_inte(uvin,uvin%r2d,nvisi,dmax,dtol,inte,ni,total,nant,error)
      enddo
      !
      call sic_delvariable('UV_INTEGRATION',.false.,error)
      call sic_def_real('UV_INTEGRATION',inte,1,ni,.true.,error)
      write(6,'(A)') 'Possible integration times (seconds) '
      write(6,'((8(1X,F10.2)))') inte(1:ni)
    case ('NULLS')
      call map_message(seve%i,rname,'Checking for null visibilities')
      allocate (bad(uvin%gil%nvisi),stat=ier)
      bad(0) = 0
      kv = 0
      !
      do ib = 1,uvin%gil%dim(2),nblock
        write(mess,*) ib,' / ',uvin%gil%dim(2),nblock
        call map_message(seve%i,rname,mess)
        uvin%blc(2) = ib
        uvin%trc(2) = min(uvin%gil%nvisi,ib-1+nblock)
        call gdf_read_data(uvin,uvin%r2d,error)
        if (error) exit
        !
        nvisi = uvin%trc(2)-uvin%blc(2)+1
        !
        call sub_get_nulls(uvin%r2d,nvisi,nc,bad,kv)
      enddo
      if (kv.ne.0) then
        write(6,*) 'Flagged ',kv,' null visibilities'
        write(6,*) (bad(iv),iv=1,kv)
      else
        call map_message(seve%i,rname,'No null visibilities left unflagged')
      endif
      deallocate (bad,stat=ier)
    case ('BEAMS')
      call sic_delvariable ('BEAM_RANGES',.false.,error)
      allocate (cases(nc),weights(nc),stat=ier)
      if (ier.ne.0) then
        call map_message(seve%e,rname,'Memory allocation error')
        error = .true.
        return
      endif
      !
      weights = 0.
      wcol = 0
      do ib = 1,uvin%gil%dim(2),nblock
        write(mess,*) ib,' / ',uvin%gil%dim(2),nblock
        call map_message(seve%i,rname,mess)
        uvin%blc(2) = ib
        uvin%trc(2) = min(uvin%gil%nvisi,ib-1+nblock)
        call gdf_read_data(uvin,uvin%r2d,error)
        if (error) exit
        !
        nvisi = uvin%trc(2)-uvin%blc(2)+1
        !
        call sub_get_nbeams(uvin, uvin%r2d, nvisi, nc, weights, wcol)
      enddo
      call howmany_beams(weights, nc, cases, ncase, beam_tolerance) 
      !
      ! Write the result
      if (ncase.le.1) then
        call map_message(seve%i,rname,'Only one beam needed')
      else
        if (ncase.eq.nc) then
          call map_message(seve%w,rname,'Need one beam per channel')
        else
          call map_message(seve%w,rname,'Beams needed for the following channel ranges:')
          if (allocated(beam_ranges)) deallocate(beam_ranges)
          allocate(beam_ranges(3,ncase),stat=ier)
          cases(ncase+1) = nc+1
          jcase = 0
          do i=1, ncase
            write(6,'(a,i6,a,i6,a,1pg10.3)') '[',cases(i),'-',cases(i+1)-1,'] ', weights(cases(i))
            if (weights(cases(i)).ne.0) then
              jcase = jcase+1
              beam_ranges(1,jcase) = cases(i)
              beam_ranges(2,jcase) = cases(i+1)-1
              beam_ranges(3,jcase) = weights(cases(i))
            endif
          enddo
          dim = [3,jcase,0,0]
          call sic_def_real('BEAM_RANGES',beam_ranges,2,dim,.false.,error)
        endif
      endif
    case default
      call map_message(seve%e,rname,'/FILE option not yet implemented for '//cmode)
      error = .true.
    end select
    !
    deallocate(uvin%r2d,stat=ier)
    call gdf_close_image(uvin,err)
    error = err.or.error
  endif
end subroutine uv_check_comm
!
subroutine sub_get_inte(huvl,visi,nv,dmax,dtol,inte,kv,total,nant,error)
  use image_def
  use gkernel_interfaces, only : gr8_trie
  !----------------------------------------------------------------------
  ! @ private
  !*
  ! IMAGER -- support routine for command  
  !     UV_CHECK Integration [/FILE File]
  !
  ! Attempt to derive integration times, by differencing time stamps
  ! of consecutive visibvilities.  
  !  To be developpe: if an integration time column is present,
  ! Use the values from the that columns.
  !!
  !----------------------------------------------------------------------
  type(gildas), intent(in) :: huvl  !! UV data header
  real, intent(in) :: visi(:,:)     !! UV data
  integer(kind=index_length), intent(in) :: nv  !! Number of visibilities
  real, intent(inout) :: dmax       !! Longest allowed time step
  real, intent(in) :: dtol          !! Time stamp tolerance
  real, intent(inout) :: inte(:)    !! Possible integration times
  integer, intent(inout) :: kv      !! Number of possible times
  real, intent(inout) :: total      !! Total integration time
  integer, intent(inout) :: nant    !! Number of antennas
  logical, intent(out) :: error     !! Error flag
  !
  ! Local --
  integer(kind=index_length), allocatable :: idx(:)
  real(8), allocatable :: times(:)
  real(4) :: dt
  logical :: found  !
  integer :: iv
  integer :: jv,ier
  !
  ! Code ----
  allocate(times(nv),idx(nv),stat=ier)
  !
  do iv=1,nv
    times(iv) = visi(4,iv)*86400d0 + visi(5,iv)
  enddo
  call gr8_trie(times,idx,nv,error)
  if (error) return
  !
  do iv=2,nv
    if (times(iv).ne.times(iv-1)) then
      dt = real(times(iv)-times(iv-1))
      if (dt.lt.dmax) then
        found = .false.
        do jv=1,kv
          if (abs(dt-inte(jv)).lt.dtol*dt) then
            found = .true.
            exit
          endif
        enddo
        if (.not.found) then
          kv = kv+1
          inte(kv) = dt 
        endif
        total = total+dt  ! Increment per-baseline total time
        nant = max(nant,int(visi(7,iv))) 
      endif
    endif
  enddo
  !
end subroutine sub_get_inte
!
subroutine sub_get_nulls(visi,nv,nc,bad,kv)
  use image_def
  !----------------------------------------------------------------------
  ! @ private
  !*
  ! IMAGER -- Support routine for command  
  !     UV_CHECK Nulls [/FILE File]
  !
  !  Find out Null visibilities with non zero weights
  ! (occasionally happens with CASA data)
  !!
  !----------------------------------------------------------------------
  real, intent(inout) :: visi(:,:)       !! UV data
  integer(kind=index_length), intent(in) :: nv  !! Number of visibilities
  integer, intent(in) :: nc             !! Number of channels
  integer, intent(inout) :: bad(nv)     !! Bad visi list
  integer, intent(inout) :: kv          !! Current pointer in list
  !
  ! Local ---
  integer :: iv,ic
  !
  ! Code ----
  do iv=1,nv
    do ic=1,nc
      if (visi(7+3*ic,iv).gt.0) then
        if (visi(5+3*ic,iv).eq.0 .and. visi(6+3*ic,iv).eq.0) then
          ! Flag all channels - Something was wrong
          kv = kv+1
          bad(kv) = iv
          visi(8:7+3*nc,iv) = 0
          exit
        endif
      endif
    enddo
  enddo
end subroutine sub_get_nulls
!
subroutine sub_get_flags(visi,nv,nc,bad,kv,wc)
  use image_def
  !----------------------------------------------------------------------
  ! @ private
  !*
  ! IMAGER support routine for command  
  !     UV_CHECK Flags [Channel] [/FILE File]
  !
  !  Find out Flagged visibilities. 
  !  Returns the list of flagged visibilities, based on the specified
  !  weight channel. KV must be initialized outside, as the routine
  !  can be called by visbility blocks.
  !
  !----------------------------------------------------------------------
  real, intent(inout) :: visi(:,:)       !! UV data
  integer(kind=address_length), intent(in) :: nv  !! Number of visibilities
  integer, intent(in) :: nc             !! Number of channels
  integer, intent(inout) :: bad(nv)     !! Bad visi list
  integer, intent(inout) :: kv          !! Current pointer in list
  integer, intent(in) :: wc             !! Weight channel
  !
  ! Local ---
  integer :: iv
  !
  ! Code ----
  do iv=1,nv
    if (visi(7+3*wc,iv).le.0) then
      kv = kv+1
      bad(kv) = iv
    endif
  enddo
end subroutine sub_get_flags
!
!
subroutine sub_get_nbeams(uvin, visi, nvisi, nchan, weights, wcol)
  use image_def
  !----------------------------------------------------------------------
  ! @ public
  !*
  ! IMAGER support routine for command  
  !     UV_CHECK BEAM
  !
  ! Get the sum of weights for each channel.  
  ! Verify it is constant, or just a global scale factor from one channel to 
  ! another, so that the relative weights are all the same. Noise variations
  ! due to e.g. Tsys or slightly different bandwidths (not accounted for in
  ! the data information) are irrelevant in the imaging process as only 
  ! relative weights as a function of time matter. 
  !
  ! Used implicitely by UV_MAP if not called before.
  !!
  !----------------------------------------------------------------------
  type(gildas), intent(in) :: uvin                  !! UV table header
  integer(kind=index_length), intent(in) :: nvisi   !! Number of visibilities
  integer, intent(in) :: nchan                      !! Number of channels 
  real, intent(in) :: visi(uvin%gil%dim(1),nvisi)    !! UV data
  real, intent(inout) :: weights(uvin%gil%nchan)    !! Cumulative weights
  integer, intent(inout) :: wcol                    !! non-null Weight Column
  !
  ! Local ---
  integer :: iv,ic,i
  real(8) :: dw, wmean, o_uv, o_ww, o_dw
  real(8) :: uvweigh(nchan), dweight(nchan), wscale(nchan), lweight(nchan)
  !
  if (nchan.eq.1) return
  !
  wscale(:) = 1.0
  ! Two passes with per-channel scaling factor.
  do i=1,2
    !
    lweight = 0
    uvweigh = 0
    dweight = 0
    !$OMP PARALLEL DEFAULT(NONE) &
    !$OMP SHARED(nvisi, nchan, uvin, visi, wscale) &
    !$OMP PRIVATE(iv,ic,dw) &
    !$OMP REDUCTION(+:lweight,uvweigh,dweight)
    !$OMP DO
    do iv=1,nvisi
      do ic=1,nchan
        dw = visi(uvin%gil%fcol-1+3*ic,iv)*wscale(ic)
        if (dw.gt.0) then
          lweight(ic) = lweight(ic) + dw
          uvweigh(ic) = uvweigh(ic) + (visi(1,iv)**2+visi(2,iv)**2)*dw
          dweight(ic) = dweight(ic) + dw**2
        endif
      enddo
    enddo
    !$OMP ENDDO
    !$OMP END PARALLEL
    !
    ! It may just be a scale factor - Double check
    wmean = sum(lweight)/nchan
    ! !Print *,'Pass # ',i, wmean, lweight
    if (i.eq.1) then
      if (all(lweight.eq.wmean)) return   ! Could have a more relaxed tolerance
      if (all(abs(lweight-wmean).le.1d-6*wmean)) return
      !
      ! Make a 2nd pass with scaling factor to check other moments
      wscale = 1./lweight
    else
      ! Verify other moments are also equal
      o_ww = lweight(1)
      o_dw = uvweigh(1)
      o_uv = dweight(1)
      if (any(abs(lweight-wmean).gt.1d-4*wmean)) then
        uvweigh = uvweigh/wmean
        dweight = dweight/wmean**2
        write(6,'(A)') &
          & "     Channel   Relative Weight Estimator"
        do ic=1,nchan
          if (lweight(ic).ne.0) then
            if ( (abs(lweight(ic)-o_ww)/(lweight(ic)+o_ww).gt.1d-3) .or. &
              &  (abs(uvweigh(ic)-o_uv)/(uvweigh(ic)+o_uv).gt.1d-3) .or. &
              &  (abs(dweight(ic)-o_dw)/(dweight(ic)+o_dw).gt.1d-3) ) then
              if (o_ww.eq.0) wcol = ic
              write(6,'(I12,3(1PG13.3))') ic,uvweigh(ic),dweight(ic)
              o_ww = lweight(ic)
              o_uv = uvweigh(ic)
              o_dw = dweight(ic)
            endif
          endif
        enddo
        !
        wmean = sum(dweight)/nchan
        if (all(dweight.eq.wmean)) then
          weights = uvweigh
        else
          weights = dweight
        endif
      else
        weights = lweight
      endif
    endif
  enddo
  !
end subroutine sub_get_nbeams
!
subroutine howmany_beams(weights, nc, cases, ncase, tole) 
  use image_def
  !----------------------------------------------------------------------
  ! @ public
  !*
  ! IMAGER -- Support routine for command  
  !     UV_CHECK BEAM
  !
  ! Find out how many different sum of weights
  !!
  !----------------------------------------------------------------------
  integer, intent(in) :: nc                         !! Number of channels
  real, intent(in) :: weights(nc)                   !! Weight array
  integer, intent(inout) :: cases(nc)               !! Possible cases for beams
  integer, intent(inout) :: ncase                   !! Number of cases
  real, intent(in) :: tole                          !! Tolerance
  !
  ! Local ---
  real :: wlast, wtole, wnext
  integer :: ic
  !
  ! Code ----
  wlast = 0
  ncase = 0
  !
  do ic = 1,nc
    wnext = weights(ic)
    wtole = tole*max(wlast,wnext)
    !
    if (abs(wnext-wlast).gt.wtole) then
      ncase = ncase+1
      cases(ncase) = ic ! Which channel
      wlast = weights(ic)
    endif
  enddo
end subroutine howmany_beams
!
subroutine check_beams_mem(error)
  use clean_beams
  use clean_arrays
  use clean_default
  use gbl_message
  use gkernel_interfaces
  use imager_interfaces, only : map_message
  ! @ private
  !! IMAGER -- Support routine for command UV_CHECK
  logical, intent(inout) :: error     !! Error flag
  !
  character(len=*), parameter :: rname='UV_CHECK'
  !
  real, allocatable :: weights(:)
  integer :: wcol
  integer :: nc
  integer :: ncase, jcase, i, ier
  integer(kind=index_length) :: dim(4)
  integer, allocatable :: cases(:)
  !
  ! Return if already done
  if (nbeam_ranges.ne.-1) return
  !  
  nc = huv%gil%nchan
  !
  call sic_delvariable ('BEAM_RANGES',.false.,error)
  error = .false.
  allocate (cases(nc),weights(nc),stat=ier)
  if (ier.ne.0) then
    call map_message(seve%e,rname,'Memory allocation error')
    error = .true.
    return
  endif
  !
  weights = 0.
  wcol = 0
  call sub_get_nbeams(huv, duv, huv%gil%nvisi, nc, weights, wcol)
  !PRINT *,'WCOL set set to ',wcol
  call howmany_beams(weights, nc, cases, ncase, beam_tolerance) 
  !
  ! Write the result
  if (ncase.le.1) then
    call map_message(seve%i,rname,'Only one beam needed')
    nbeam_ranges = 0
  else
    if (ncase.eq.nc) then
      call map_message(seve%w,rname,'Need one beam per channel',3)
      nbeam_ranges = 0
    else
      call map_message(seve%w,rname,'Beams needed for the following channel ranges:',3)
      if (allocated(beam_ranges)) deallocate(beam_ranges)
      allocate(beam_ranges(3,ncase),stat=ier)
      cases(ncase+1) = nc+1
      jcase = 0
      do i=1, ncase
        if (weights(cases(i)).ne.0) then
          write(6,'(a,i6,a,i6,a,1pg10.3)') '[',cases(i),'-',cases(i+1)-1,']    Weight;', weights(cases(i))
          jcase = jcase+1
          beam_ranges(1,jcase) = cases(i)
          beam_ranges(2,jcase) = cases(i+1)-1
          beam_ranges(3,jcase) = weights(cases(i))
        endif
      enddo
      nbeam_ranges = jcase
      dim = [3,jcase,0,0]
      call sic_def_real('BEAM_RANGES',beam_ranges,2,dim,.false.,error)
    endif
  endif
end subroutine check_beams_mem
