subroutine detect_comm(line,comm,error)
  use gkernel_interfaces
  use gbl_message
  use clean_default
  use imager_interfaces, only : map_message
  !---------------------------------------------------------------------
  ! @ private
  !*
  !   IMAGER -- Support for command  
  !   UV_DETECT Result [Data[.uvt] Model[.lmv-clean]]
  !
  ! Compute Frequency Cross-Correlation spectrum of a UV data set
  ! with that derived from the same UV coverage from a image data cube.
  !   This routine encapsulate the spectral resampling between the 
  ! image and the UV data and the derivation of the UV model before 
  ! calling UV_CORRELATE to perform the UV plane correlation.
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line    !! Command line
  character(len=*), intent(in) :: comm    !! Command name
  logical, intent(inout) :: error         !! Logical Error flag
  !
  ! Constants
  integer, parameter :: o_file=1
  !
  ! Local ---
  character(len=2) :: cb
  integer :: ip,jp,kp
  logical :: do_insert  
  !
  ! Code ----
  ip = sic_start(0,1)
  cb = line(ip:ip+1)
  if (cb.eq."?") then
    call exec_program('HELP UV_DETECT')
    return
  endif
  !
  ! Pass command to script
  do_insert = sic_lire().eq.0
  ImagerError = .true.
  !
  if (sic_narg(0).eq.0) then
    if (sic_present(o_file,0)) then
      jp = index(line,'/')
      kp = jp + index(line(jp:),' ')
      call exec_program('@ p_uv_detect gag_scratch:detect'//line(kp:) )
    else 
      call exec_program('@ p_uv_detect gag_scratch:detect')
    endif
  else if (sic_narg(0).eq.1) then
    if (sic_present(o_file,0)) then
      ip = index(line,' ')+1
      jp = index(line,'/')
      kp = jp + index(line(jp:),' ')
      call exec_program('@ p_uv_detect '//line(ip:jp-1)//line(kp:) )
    else
      ip = index(line,' ')+1
      call exec_program('@ p_uv_detect '//line(ip:))
    endif
  else
    call map_message(seve%e,comm,'Requires 0 or 1 argument')
    error = .true.
    return
  endif
  if (sic_ctrlc()) return
  ImagerError = .false.
  error = .false.
  !
  if (do_insert) call sic_insert_log(line)
end subroutine detect_comm
!
subroutine correlate_comm(line,comm,error)
  use gkernel_interfaces
  use image_def
  use clean_arrays
  use gbl_message
  use imager_interfaces, only : uvcorrel, map_message
  !$ use omp_lib
  !---------------------------------------------------------------------
  ! @ private
  !* 
  !   IMAGER -- Support for command  
  !   UV_CORRELATE Result [/FILE Data Model]
  !
  ! Compute Frequency Cross-Correlation spectrum of two UV data sets.
  ! This routine assumes proper velocity matching and Visibility
  ! matching between the UV data sets.
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line  !! Command line
  character(len=*), intent(in) :: comm  !! Command name
  logical, intent(inout) :: error       !! Logical error flag
  !
  ! Constants
  character(len=*), parameter :: rname='UV_CORRELATE'
  integer, parameter :: o_file=1
  !
  ! Code ---
  character(len=256) cmod,cuv 
  character(len=128) :: mess
  type (gildas) :: hmod,hdata,hspec
  !
  real, allocatable :: accum(:), accums(:,:), current(:), spe(:,:)
  real, allocatable :: normal(:,:), norm(:)  
  logical :: byfile, err
  integer :: it, iv, nspe, i, nthread, ithread, n
  integer :: ier, nblock, ib, nvisi, ospe, ic
  real :: weight, realt
  !
  ! Code ----
  error = .false.
  call gildas_null(hmod,type='UVT')
  call gildas_null(hdata,type='UVT')
  call gildas_null(hspec,type='UVT')
  !
  byfile = sic_present(o_file,0)
  if (.not.byfile) then
    if (huv%loca%size.eq.0) then
      call map_message(seve%e,rname,'No UV data')
      error = .true.
    endif
    call gdf_copy_header(huv,hdata,error)
    if (error) return
    if (huvm%loca%size.eq.0) then
      call map_message(seve%e,rname,'No UV model')
      error = .true.
    endif
    if (error) return
    call gdf_copy_header(huvm,hmod,error)
    if (error) return
  else
    call sic_ch(line,o_file,2,cmod,n,.true.,error)
    if (error) return
    call sic_ch(line,o_file,1,cuv,n,.true.,error)
    if (error) return
    !
    call sic_parse_file(cmod,' ','.uvt',hmod%file)
    call sic_parse_file(cuv,' ','.uvt',hdata%file)
    !
    call gdf_read_header(hmod,error)
    if (error) return
    call gdf_read_header(hdata,error)
    if (error) return
  endif
  !
  call sic_ch(line,0,1,cuv,n,.true.,error)
  if (error) return
  !
  ! Compare simply number of visibilities (assume they match)
  if (hmod%gil%nvisi.ne.hdata%gil%nvisi) then
    call map_message(seve%e,rname,'MODEL and UVDATA UV tables visibilities do not match')
    error = .true.
  endif
  ! Compare spectral resolution - Auto-resampling will be implemented later
  if (abs(hmod%gil%vres-hdata%gil%vres).gt.0.1*abs(hdata%gil%vres/hdata%gil%nchan)) then
    call map_message(seve%e,rname,'MODEL and UVDATA spectral resolution do not match')
    error = .true.
  endif
  if (error) return
  !
  ! This will be a Spectrum with Velocity,Frequency,Value
  nspe = hdata%gil%nchan   ! +/- half of kernel on each side ?...
  !
  ! !Print *,'Ready for kernel ',nker,' byfile ',byfile,' Dims ',nspe,3
  !
  nthread = 1
  !$ nthread = omp_get_max_threads()
  allocate(accum(nspe),accums(nspe,nthread),normal(nspe,nthread), &
    & norm(nspe),current(nspe),spe(nspe,3),stat=ier)
  if (ier.ne.0) then
    call map_message(seve%e,rname,'Allocation error')
    error =.true.
    return
  endif
  !
  accums = 0.0
  normal = 0.0
  !
  ! This is not obviously correct. 
  ! It may depend whether the Kernel is centered or not... How do we know ?...
  ! Also needs to be adjusted if the number of output channels is changed
  ospe = (hmod%gil%nchan+1)/2-hmod%gil%ref(1)  ! Sign verified
  do i=1,nspe
    spe(i,1) = (i-hdata%gil%ref(1)-ospe)*hdata%gil%vres + hdata%gil%voff
    spe(i,2) = (i-hdata%gil%ref(1)-ospe)*hdata%gil%fres + hdata%gil%freq
    spe(i,3) = 0.
  enddo
  !
  if (.not.byfile) then
    !
    ! !Print *,'SPEC Dim ',nspe,' Kernel size ',nker,' Channels ',hdata%gil%nchan
    !$OMP PARALLEL DEFAULT(NONE) &
    !$OMP &  SHARED(hdata,hmod,duv,duvm,accums,normal,nspe) &
    !$OMP &  PRIVATE(iv,current,ithread,weight,norm,ic, err, realt)
    !
    err = .false.
    ithread = 1
    !$ ithread = omp_get_thread_num()+1
    !$OMP DO
    do iv = 1, hdata%gil%nvisi
      ! We use the weight of the Data
      ! weight = duvm(7+3*(hmod%gil%nchan/2),iv)   ! Kernel
      weight =  duv(7+3*(hdata%gil%nchan/3),iv)  ! Data 
      if (weight.le.0) cycle  ! Flagged visibility
      !
      call uvcorrel(duv(1:hdata%gil%dim(1),iv),hdata%gil%nchan, & 
        & duvm(1:hmod%gil%dim(1),iv),hmod%gil%nchan,norm,current,nspe) 
      !
      accums(:,ithread) = accums(:,ithread) + current
      normal(:,ithread) = normal(:,ithread) + norm    ! Accumulate the weight    
    enddo
    !$OMP END DO
    !$OMP END PARALLEL
    !
  else
    !
    ! Define blocking factor, on largest data file usually.
    call gdf_nitems('SPACE_GILDAS',nblock,hdata%gil%dim(1)) ! Visibilities at once
    nblock = min(nblock,hdata%gil%dim(2))
    ! Allocate respective space for each file
    allocate (hdata%r2d(hdata%gil%dim(1),nblock), hmod%r2d(hmod%gil%dim(1),nblock), stat=ier)
    if (ier.ne.0) then
      write(mess,*) 'Memory allocation error ',hdata%gil%dim(1), nblock
      call map_message(seve%e,rname,mess)
      error = .true.
      return
    endif
    !
    ! Loop over line tables 
    hdata%blc = 0
    hdata%trc = 0
    hmod%blc = 0
    hmod%trc = 0
    do ib = 1,hdata%gil%dim(2),nblock
      write(mess,*) ib,' / ',hdata%gil%dim(2),nblock
      call map_message(seve%i,rname,mess)
      hdata%blc(2) = ib
      hdata%trc(2) = min(hdata%gil%dim(2),ib-1+nblock)
      hmod%blc(2) = ib
      hmod%trc(2) = hdata%trc(2)
      call gdf_read_data(hdata,hdata%r2d,error)
      if (error) exit
      call gdf_read_data(hmod,hmod%r2d,error)
      if (error) exit
      !
      ! Here do the job
      nvisi = hmod%trc(2)-hmod%blc(2)+1
      !
      !$OMP PARALLEL DEFAULT(NONE) &
      !$OMP &  SHARED(hdata,hmod,accums,nvisi,normal,nspe) &
      !$OMP &  PRIVATE(iv,current,ithread,weight,norm)
      !
      ithread = 1
      !$ ithread = omp_get_thread_num()+1
      !$OMP DO
      do iv = 1, nvisi
        ! We use the weight of the Data
        ! weight = hmod%r2d(7+3*(hmod%gil%nchan/2),iv)    ! Kernel
        weight = hdata%r2d(7+3*(hdata%gil%nchan/3),iv)  ! Data 
        if (weight.le.0) cycle  ! Flagged visibility
        !
        call uvcorrel(hdata%r2d(:,iv),hdata%gil%nchan, & 
          & hmod%r2d(:,iv),hmod%gil%nchan,norm,current,nspe)
        accums(:,ithread) = accums(:,ithread) + current
        normal(:,ithread) = normal(:,ithread) + norm    ! Accumulate the weight    
      enddo
      !$OMP END DO
      !$OMP END PARALLEL
    enddo
    !
    ! Finalize the image
    call gdf_close_image(hdata,err)
    call gdf_close_image(hmod,err)
    if (error) return
  endif
  !
  ! Accumulate the Thread results
  spe(:,3) = 0.0
  norm = 0.0
  do it = 1,nthread
    spe(:,3) = spe(:,3) + accums(:,it)
    norm(:) = norm + normal(:,it)
  enddo
  !
  where (norm.le.0) norm = 1.0
  ! Normalize to S/N ratio - ! Units are MHz by default
  ! Do we need the sqrt(2) factor ?
  spe(:,3) = spe(:,3)/sqrt(norm(:))*1E3 ! *sqrt(2.0)
  !
  ! Define the result as a SPECTRUM - Would be better to write
  ! it as a UV table with only the (0,0) spacing, since UV_PREVIEW
  ! could then be used on it directly 
  call sic_parse_file(cuv,' ','.dat',hspec%file)
  open(unit=2,file=hspec%file,status='unknown',iostat=ier)
  if (ier.ne.0) then
    call map_message(seve%w,rname,'Error opening '//trim(hspec%file))
    call putios('E-UV_CORRELATE,  ',ier)
  else
    rewind(2)
    do i=1,nspe
      write(2,*) spe(i,1), spe(i,2), spe(i,3)
    enddo
    close(unit=2)
  endif
  !
  ! Now save the result also as a Zero spacing UV table, for easy display
  call gdf_copy_header(hdata,hspec,error)
  call sic_parse_file(cuv,' ','.uvt',hspec%file)
  !
  hspec%gil%ref(1) = hdata%gil%ref(1)-ospe
  hspec%gil%nchan = nspe
  hspec%gil%dim(1) = 7+3*nspe
  hspec%gil%dim(2) = 1
  hspec%gil%nvisi = 1
  !
  weight = 1E-6 !! Normalized to 1 sigma, per MHz
  !
  call gdf_allocate(hspec,error)
  if (error) return
  hspec%r2d(1:7,1) = 0
  do i=1,nspe
    hspec%r2d(5+3*i,1) = spe(i,3)
    hspec%r2d(6+3*i,1) = 0.
    hspec%r2d(7+3*i,1) = weight ! Sum of initial weights
  enddo
  hspec%char%unit = 'Unknown'
  call gdf_write_image(hspec,hspec%r2d,error)
  !
end subroutine correlate_comm
!
subroutine uvcorrel(uvdata,nc,kernel,nk,norm,current,mc)
  !---------------------------------------------------------------------
  ! @ private
  !*
  !   IMAGER -- Support for command UV_CORRELATE
  !
  ! Compute complex cross-correlation of two visibilities
  !!
  !---------------------------------------------------------------------  
  real, intent(in) :: uvdata(:)     !! UV data to be matched
  real, intent(in) :: kernel(:)     !! Matching Kernel 
  integer, intent(in) :: nc         !! Number of channels of uvdata
  integer, intent(in) :: nk         !! Kernel size. 
  real, intent(out) :: norm(:)      !! Normalisation factor (per channel)
  real, intent(out) :: current(:)   !! Correlation spectrum
  integer, intent(in) :: mc         !! Size  of correlation spectrum
  !
  ! Local ---
  integer :: ik, ic, jc
  real(8) :: r2, rp
  real(4) :: r, red, imd, rek, imk, w
  !
!!
!! xc += np.correlate(data.VV[v], np.matmul(data.wgts[v]*R_inv, kernel[v]))
!!        # normalize the output such that real and imag noise powers are both 1 (hence factor of sqrt(2))
!!                kernel_noise_power += np.dot(kernel[v],np.matmul(data.wgts[v]*R_inv, kernel[v].conj()))
!!        xc = xc/np.sqrt(kernel_noise_power)*np.sqrt(2)  
!!
  ! Assume no correlation between channels
  do ic = 1,mc      ! Output channel
    r2 = 0.        
    rp = 0. ! Noise power
    !
    do jc=1,nc
      ik = jc-ic+(nk+1)/2
      !
      if (ik.lt.1) cycle
      if (ik.gt.nk) cycle
      w = uvdata(7+3*jc)
      if (w.gt.0) then
        red = uvdata(5+3*jc)
        imd = uvdata(6+3*jc)
        rek = kernel(5+3*ik)
        imk = kernel(6+3*ik) 
        r = (red*rek + imd*imk)
        r2 = r2 + r*w  ! Use the Weights
        r = (rek**2 + imk**2)
        rp = rp + r*w
      endif
    enddo
    current(ic) = r2
    norm (ic) = rp
  enddo
end subroutine uvcorrel
