//#---------------------------------------------------------------------------
//# PKSFITSreader.cc: Class to read Parkes multibeam data from a FITS file.
//#---------------------------------------------------------------------------
//# livedata - processing pipeline for single-dish, multibeam spectral data.
//# Copyright (C) 2000-2009, Australia Telescope National Facility, CSIRO
//#
//# This file is part of livedata.
//#
//# livedata is free software: you can redistribute it and/or modify it under
//# the terms of the GNU General Public License as published by the Free
//# Software Foundation, either version 3 of the License, or (at your option)
//# any later version.
//#
//# livedata is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//# more details.
//#
//# You should have received a copy of the GNU General Public License along
//# with livedata.  If not, see .
//#
//# Correspondence concerning livedata may be directed to:
//#        Internet email: mcalabre@atnf.csiro.au
//#        Postal address: Dr. Mark Calabretta
//#                        Australia Telescope National Facility, CSIRO
//#                        PO Box 76
//#                        Epping NSW 1710
//#                        AUSTRALIA
//#
//# http://www.atnf.csiro.au/computing/software/livedata.html
//# $Id: PKSFITSreader.cc,v 19.23 2009-09-29 07:33:38 cal103 Exp $
//#---------------------------------------------------------------------------
//# Original: 2000/08/02, Mark Calabretta, ATNF
//#---------------------------------------------------------------------------
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//----------------------------------------------- PKSFITSreader::PKSFITSreader
// Constructor sets the method of position interpolation.
PKSFITSreader::PKSFITSreader(
        const String fitsType,
        const Int    retry,
        const Bool   interpolate)
{
  cMBrec.setNIFs(1);
  if (fitsType == "SDFITS") {
    cReader = new SDFITSreader();
  } else {
    cReader = new MBFITSreader(retry, interpolate ? 1 : 0);
  }
  // By default, messages are written to stderr.
  initMsg();
}
//---------------------------------------------- PKSFITSreader::~PKSFITSreader
// Destructor.
PKSFITSreader::~PKSFITSreader()
{
  close();
  delete cReader;
}
//------------------------------------------------------ PKSFITSreader::setMsg
// Set message disposition.  If fd is non-zero messages will be written
// to that file descriptor, else stored for retrieval by getMsg().
Int PKSFITSreader::setMsg(FILE *fd)
{
  PKSmsg::setMsg(fd);
  cReader->setMsg(fd);
  return 0;
}
//-------------------------------------------------------- PKSFITSreader::open
// Open the FITS file for reading.
Int PKSFITSreader::open(
        const String fitsName,
        Vector &beams,
        Vector &IFs,
        Vector &nChan,
        Vector &nPol,
        Vector &haveXPol,
        Bool   &haveBase,
        Bool   &haveSpectra)
{
  clearMsg();
  int    extraSysCal, haveBase_, *haveXPol_, haveSpectra_, nBeam, *nChan_,
         nIF, *nPol_, status;
  status = cReader->open((char *)fitsName.chars(), nBeam, cBeams, nIF, cIFs,
                         nChan_, nPol_, haveXPol_, haveBase_, haveSpectra_,
                         extraSysCal);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  if (status) {
    return status;
  }
  // Beams present in data.
  beams.resize(nBeam);
  for (Int ibeam = 0; ibeam < nBeam; ibeam++) {
    beams(ibeam) = cBeams[ibeam];
  }
  // IFs, channels, and polarizations present in data.
  IFs.resize(nIF);
  nChan.resize(nIF);
  nPol.resize(nIF);
  haveXPol.resize(nIF);
  for (Int iIF = 0; iIF < nIF; iIF++) {
    IFs(iIF)   = cIFs[iIF];
    nChan(iIF) = nChan_[iIF];
    nPol(iIF)  = nPol_[iIF];
    // Cross-polarization data present?
    haveXPol(iIF) = haveXPol_[iIF];
  }
  cNBeam = nBeam;
  cNIF   = nIF;
  cNChan.assign(nChan);
  cNPol.assign(nPol);
  cHaveXPol.assign(haveXPol);
  // Baseline parameters present?
  haveBase = haveBase_;
  // Spectral data present?
  haveSpectra = haveSpectra_;
  return 0;
}
//--------------------------------------------------- PKSFITSreader::getHeader
// Get parameters describing the data.
Int PKSFITSreader::getHeader(
        String &observer,
        String &project,
        String &antName,
        Vector &antPosition,
        String &obsType,
        String &bunit,
        Float  &equinox,
        String &dopplerFrame,
        Double &mjd,
        Double &refFreq,
        Double &bandwidth)
{
  char   bunit_[32], datobs[32], dopplerFrame_[32], observer_[32],
         obsType_[32], project_[32], radecsys[32], telescope[32];
  int    status;
  float  equinox_;
  double antPos[3], utc;
  status = cReader->getHeader(observer_, project_, telescope, antPos,
                              obsType_, bunit_, equinox_, radecsys,
                              dopplerFrame_, datobs, utc, refFreq, bandwidth);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  if (status) {
    return 1;
  }
  observer = trim(observer_);
  project  = trim(project_);
  antName  = trim(telescope);
  antPosition.resize(3);
  antPosition(0) = antPos[0];
  antPosition(1) = antPos[1];
  antPosition(2) = antPos[2];
  obsType = trim(obsType_);
  bunit   = trim(bunit_);
  equinox = equinox_;
  dopplerFrame = trim(dopplerFrame_);
  // Get UTC in MJD form.
  Int day, month, year;
  sscanf(datobs, "%4d-%2d-%2d", &year, &month, &day);
  MVTime date(year, month, Double(day));
  mjd = date.day() + utc/86400.0;
  return 0;
}
//------------------------------------------------- PKSFITSreader::getFreqInfo
// Get frequency parameters for each IF.
Int PKSFITSreader::getFreqInfo(
        Vector &startFreq,
        Vector &endFreq)
{
  int     nIF;
  double *startfreq, *endfreq;
  Int status = cReader->getFreqInfo(nIF, startfreq, endfreq);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  if (!status) {
    startFreq.takeStorage(IPosition(1,nIF), startfreq, TAKE_OVER);
    endFreq.takeStorage(IPosition(1,nIF), endfreq, TAKE_OVER);
  }
  return status;
}
//------------------------------------------------------ PKSFITSreader::select
// Set data selection by beam number and channel.
uInt PKSFITSreader::select(
        const Vector beamSel,
        const Vector IFsel,
        const Vector  startChan,
        const Vector  endChan,
        const Vector  refChan,
        const Bool getSpectra,
        const Bool getXPol,
        const Int  coordSys)
{
  // Apply beam selection.
  uInt nBeamSel = beamSel.nelements();
  for (uInt ibeam = 0; ibeam < cNBeam; ibeam++) {
    // This modifies FITSreader::cBeams[].
    if (ibeam < nBeamSel) {
      cBeams[ibeam] = cBeams[ibeam] && beamSel(ibeam);
    } else {
      cBeams[ibeam] = 0;
    }
  }
  // Apply IF selection.
  int *end   = new int[cNIF];
  int *ref   = new int[cNIF];
  int *start = new int[cNIF];
  for (uInt iIF = 0; iIF < cNIF; iIF++) {
    // This modifies FITSreader::cIFs[].
    if (iIF < IFsel.nelements()) {
      cIFs[iIF] = cIFs[iIF] && IFsel(iIF);
    } else {
      cIFs[iIF] = 0;
    }
    if (cIFs[iIF]) {
      if (iIF < startChan.nelements()) {
        start[iIF] = startChan(iIF);
        if (start[iIF] <= 0) {
          start[iIF] += cNChan(iIF);
        } else if (start[iIF] > Int(cNChan(iIF))) {
          start[iIF]  = cNChan(iIF);
        }
      } else {
        start[iIF] = 1;
      }
      if (iIF < endChan.nelements()) {
        end[iIF] = endChan(iIF);
        if (end[iIF] <= 0) {
          end[iIF] += cNChan(iIF);
        } else if (end[iIF] > Int(cNChan(iIF))) {
          end[iIF]  = cNChan(iIF);
        }
      } else {
        end[iIF] = cNChan(iIF);
      }
      if (iIF < refChan.nelements()) {
        ref[iIF] = refChan(iIF);
      } else {
        if (start[iIF] <= end[iIF]) {
          ref[iIF] = start[iIF] + (end[iIF] - start[iIF] + 1)/2;
        } else {
          ref[iIF] = start[iIF] - (start[iIF] - end[iIF] + 1)/2;
        }
      }
    }
  }
  cGetSpectra = getSpectra;
  cGetXPol    = getXPol;
  cCoordSys   = coordSys;
  uInt maxNChan = cReader->select(start, end, ref, cGetSpectra, cGetXPol,
                                  cCoordSys);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  delete [] end;
  delete [] ref;
  delete [] start;
  return maxNChan;
}
//--------------------------------------------------- PKSFITSreader::findRange
// Read the TIME column.
Int PKSFITSreader::findRange(
        Int    &nRow,
        Int    &nSel,
        Vector &timeSpan,
        Matrix &positions)
{
  char    dateSpan[2][32];
  double  utcSpan[2];
  double* posns;
  Int status = cReader->findRange(nRow, nSel, dateSpan, utcSpan, posns);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  if (!status) {
    timeSpan.resize(2);
    Int day, month, year;
    sscanf(dateSpan[0], "%4d-%2d-%2d", &year, &month, &day);
    timeSpan(0) = MVTime(year, month, Double(day)).second() + utcSpan[0];
    sscanf(dateSpan[1], "%4d-%2d-%2d", &year, &month, &day);
    timeSpan(1) = MVTime(year, month, Double(day)).second() + utcSpan[1];
    positions.takeStorage(IPosition(2,2,nSel), posns, TAKE_OVER);
  }
  return status;
}
//-------------------------------------------------------- PKSFITSreader::read
// Read the next data record.
Int PKSFITSreader::read(PKSrecord &pksrec)
{
  Int status = cReader->read(cMBrec);
  logMsg(cReader->getMsg());
  cReader->clearMsg();
  if (status) {
    if (status != -1) {
      status = 1;
    }
    return status;
  }
  uInt nChan = cMBrec.nChan[0];
  uInt nPol  = cMBrec.nPol[0];
  pksrec.scanNo  = cMBrec.scanNo;
  pksrec.cycleNo = cMBrec.cycleNo;
  // Extract MJD.
  Int day, month, year;
  sscanf(cMBrec.datobs, "%4d-%2d-%2d", &year, &month, &day);
  pksrec.mjd = MVTime(year, month, Double(day)).day() + cMBrec.utc/86400.0;
  pksrec.interval  = cMBrec.exposure;
  pksrec.fieldName = trim(cMBrec.srcName);
  pksrec.srcName   = pksrec.fieldName;
  pksrec.srcDir.resize(2);
  pksrec.srcDir(0) = cMBrec.srcRA;
  pksrec.srcDir(1) = cMBrec.srcDec;
  pksrec.srcPM.resize(2);
  pksrec.srcPM(0)  = 0.0;
  pksrec.srcPM(1)  = 0.0;
  pksrec.srcVel    = 0.0;
  pksrec.obsType   = trim(cMBrec.obsType);
  pksrec.IFno = cMBrec.IFno[0];
  Double chanWidth = fabs(cMBrec.fqDelt[0]);
  pksrec.refFreq   = cMBrec.fqRefVal[0];
  pksrec.bandwidth = chanWidth * nChan;
  pksrec.freqInc   = cMBrec.fqDelt[0];
  pksrec.restFreq  = cMBrec.restFreq;
  pksrec.tcal.resize(nPol);
  for (uInt ipol = 0; ipol < nPol; ipol++) {
    pksrec.tcal(ipol) = cMBrec.tcal[0][ipol];
  }
  pksrec.tcalTime  = trim(cMBrec.tcalTime);
  pksrec.azimuth   = cMBrec.azimuth;
  pksrec.elevation = cMBrec.elevation;
  pksrec.parAngle  = cMBrec.parAngle;
  pksrec.focusAxi  = cMBrec.focusAxi;
  pksrec.focusTan  = cMBrec.focusTan;
  pksrec.focusRot  = cMBrec.focusRot;
  pksrec.temperature = cMBrec.temp;
  pksrec.pressure    = cMBrec.pressure;
  pksrec.humidity    = cMBrec.humidity;
  pksrec.windSpeed   = cMBrec.windSpeed;
  pksrec.windAz      = cMBrec.windAz;
  pksrec.refBeam = cMBrec.refBeam;
  pksrec.beamNo  = cMBrec.beamNo;
  pksrec.direction.resize(2);
  pksrec.direction(0) = cMBrec.ra;
  pksrec.direction(1) = cMBrec.dec;
  pksrec.pCode        = cMBrec.pCode;
  pksrec.rateAge      = cMBrec.rateAge;
  pksrec.scanRate.resize(2);
  pksrec.scanRate(0)  = cMBrec.raRate;
  pksrec.scanRate(1)  = cMBrec.decRate;
  pksrec.paRate       = cMBrec.paRate;
  pksrec.tsys.resize(nPol);
  pksrec.sigma.resize(nPol);
  pksrec.calFctr.resize(nPol);
  for (uInt ipol = 0; ipol < nPol; ipol++) {
    pksrec.tsys(ipol)  = cMBrec.tsys[0][ipol];
    pksrec.sigma(ipol) = (pksrec.tsys(ipol) / 0.81) /
                            sqrt(pksrec.interval * chanWidth);
    pksrec.calFctr(ipol) = cMBrec.calfctr[0][ipol];
  }
  if (cMBrec.haveBase) {
    pksrec.baseLin.resize(2,nPol);
    pksrec.baseSub.resize(24,nPol);
    for (uInt ipol = 0; ipol < nPol; ipol++) {
      pksrec.baseLin(0,ipol) = cMBrec.baseLin[0][ipol][0];
      pksrec.baseLin(1,ipol) = cMBrec.baseLin[0][ipol][1];
      for (uInt j = 0; j < 24; j++) {
        pksrec.baseSub(j,ipol) = cMBrec.baseSub[0][ipol][j];
      }
    }
  } else {
    pksrec.baseLin.resize(0,0);
    pksrec.baseSub.resize(0,0);
  }
  if (cGetSpectra && cMBrec.haveSpectra) {
    pksrec.spectra.resize(nChan,nPol);
    pksrec.spectra.takeStorage(IPosition(2,nChan,nPol), cMBrec.spectra[0],
      SHARE);
    pksrec.flagged.resize(nChan,nPol);
    pksrec.flagged.takeStorage(IPosition(2,nChan,nPol), cMBrec.flagged[0],
      SHARE);
  } else {
    pksrec.spectra.resize(0,0);
    pksrec.flagged.resize(0,0);
  }
  if (cGetXPol) {
    pksrec.xCalFctr = Complex(cMBrec.xcalfctr[0][0],
                             cMBrec.xcalfctr[0][1]);
    pksrec.xPol.resize(nChan);
    pksrec.xPol.takeStorage(IPosition(1,nChan), (Complex *)cMBrec.xpol[0],
      SHARE);
  }
  return 0;
}
//------------------------------------------------------- PKSFITSreader::close
// Close the FITS file.
void PKSFITSreader::close()
{
  cReader->close();
  logMsg(cReader->getMsg());
  cReader->clearMsg();
}
//-------------------------------------------------------- PKSFITSreader::trim
// Trim trailing blanks from a null-terminated character string.
char* PKSFITSreader::trim(char *string)
{
  int j = 0, k = 0;
  while (string[j] != '\0') {
    if (string[j++] != ' ') {
      k = j;
    }
  }
  string[k] = '\0';
  return string;
}