source: trunk/external/atnf/PKSIO/SDFITSreader.cc @ 1325

Last change on this file since 1325 was 1325, checked in by mar637, 17 years ago

Changes to use casacore instead of casa_asap/aips++\nAdded atnf PKSIO library snapshot to external and linking against this local copy

File size: 50.5 KB
Line 
1//#---------------------------------------------------------------------------
2//# SDFITSreader.cc: ATNF CFITSIO interface class for SDFITS input.
3//#---------------------------------------------------------------------------
4//# Copyright (C) 2000-2006
5//# Associated Universities, Inc. Washington DC, USA.
6//#
7//# This library is free software; you can redistribute it and/or modify it
8//# under the terms of the GNU Library General Public License as published by
9//# the Free Software Foundation; either version 2 of the License, or (at your
10//# option) any later version.
11//#
12//# This library is distributed in the hope that it will be useful, but WITHOUT
13//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
15//# License for more details.
16//#
17//# You should have received a copy of the GNU Library General Public License
18//# along with this library; if not, write to the Free Software Foundation,
19//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
20//#
21//# Correspondence concerning this software should be addressed as follows:
22//#        Internet email: aips2-request@nrao.edu.
23//#        Postal address: AIPS++ Project Office
24//#                        National Radio Astronomy Observatory
25//#                        520 Edgemont Road
26//#                        Charlottesville, VA 22903-2475 USA
27//#
28//# $Id: SDFITSreader.cc,v 19.22 2006/07/12 00:14:26 mcalabre Exp $
29//#---------------------------------------------------------------------------
30//# The SDFITSreader class reads single dish FITS files such as those written
31//# by SDFITSwriter containing Parkes Multibeam data.
32//#
33//# Original: 2000/08/09, Mark Calabretta, ATNF
34//#---------------------------------------------------------------------------
35
36#include <algorithm>
37#include <strings.h>
38
39// AIPS++ includes.
40#include <casa/iostream.h>
41#include <casa/math.h>
42#include <casa/stdio.h>
43
44// ATNF includes.
45#include <atnf/pks/pks_maths.h>
46#include <atnf/PKSIO/PKSMBrecord.h>
47#include <atnf/PKSIO/SDFITSreader.h>
48
49
50class FITSparm
51{
52  public:
53    char *name;         // Keyword or column name.
54    int  type;          // Expected keyvalue or column data type.
55    int  colnum;        // Column number; 0 for keyword; -1 absent.
56    int  coltype;       // Column data type, as found.
57    long nelem;         // Column data repeat count; < 0 for vardim.
58    int  tdimcol;       // TDIM column number; 0 for keyword; -1 absent.
59};
60
61// Numerical constants.
62const double PI  = 3.141592653589793238462643;
63
64// Factor to convert radians to degrees.
65const double D2R = PI / 180.0;
66
67//------------------------------------------------- SDFITSreader::SDFITSreader
68
69SDFITSreader::SDFITSreader()
70{
71  // Default constructor.
72  cSDptr = 0;
73
74  // Allocate space for data descriptors.
75  cData = new FITSparm[NDATA];
76
77  for (int iData = 0; iData < NDATA; iData++) {
78    cData[iData].colnum = -1;
79  }
80
81  // Initialize pointers.
82  cBeams     = 0x0;
83  cIFs       = 0x0;
84  cStartChan = 0x0;
85  cEndChan   = 0x0;
86  cRefChan   = 0x0;
87}
88
89//------------------------------------------------ SDFITSreader::~SDFITSreader
90
91SDFITSreader::~SDFITSreader()
92{
93  close();
94
95  delete [] cData;
96}
97
98//--------------------------------------------------------- SDFITSreader::open
99
100// Open an SDFITS file for reading.
101
102int SDFITSreader::open(
103        char*  sdName,
104        int    &nBeam,
105        int*   &beams,
106        int    &nIF,
107        int*   &IFs,
108        int*   &nChan,
109        int*   &nPol,
110        int*   &haveXPol,
111        int    &haveBase,
112        int    &haveSpectra,
113        int    &extraSysCal)
114{
115  if (cSDptr) {
116    close();
117  }
118
119  // Open the SDFITS file.
120  cStatus = 0;
121  if (fits_open_file(&cSDptr, sdName, READONLY, &cStatus)) {
122    cerr << "Failed to open SDFITS file: " << sdName << endl;
123    reportError();
124    return 1;
125  }
126
127  // Move to the SDFITS extension.
128  cALFA = cALFA_BD = cALFA_CIMA = 0;
129  if (fits_movnam_hdu(cSDptr, BINARY_TBL, "SINGLE DISH", 0, &cStatus)) {
130    // No SDFITS table, look for BDFITS or CIMAFITS.
131    cStatus = 0;
132    if (fits_movnam_hdu(cSDptr, BINARY_TBL, "BDFITS", 0, &cStatus) == 0) {
133      cALFA_BD = 1;
134
135    } else {
136      cStatus = 0;
137      if (fits_movnam_hdu(cSDptr, BINARY_TBL, "CIMAFITS", 0, &cStatus) == 0) {
138        cALFA_CIMA = 1;
139
140      } else {
141        cerr << "Failed to locate SDFITS binary table." << endl;
142        reportError();
143        close();
144        return 1;
145      }
146    }
147
148    // Arecibo ALFA data of some kind.
149    cALFA = 1;
150    for (int iBeam = 0; iBeam < 8; iBeam++) {
151      for (int iPol = 0; iPol < 2; iPol++) {
152        cALFAcalOn[iBeam][iPol]  = 0.0f;
153        cALFAcalOff[iBeam][iPol] = 0.0f;
154
155        // Nominal factor to calibrate spectra in Jy.
156        cALFAcal[iBeam][iPol] = 3.0f;
157      }
158    }
159  }
160
161  // GBT data.
162  char telescope[32];
163  readParm("TELESCOP", TSTRING, telescope);      // Core.
164  cGBT = strncmp(telescope, "GBT", 3) == 0 ||
165         strncmp(telescope, "NRAO_GBT", 8) == 0;
166
167  cRow = 0;
168
169
170  // Check that the DATA array column is present.
171  findData(DATA, "DATA", TFLOAT);
172  haveSpectra = cHaveSpectra = cData[DATA].colnum > 0;
173
174  if (cHaveSpectra) {
175    // Find the number of data axes (must be the same for each IF).
176    cNAxis = 5;
177    if (readDim(DATA, 1, &cNAxis, cNAxes)) {
178      reportError();
179      close();
180      return 1;
181    }
182
183    if (cALFA_BD) {
184      // ALFA BDFITS: variable length arrays don't actually vary and there is
185      // no TDIM (or MAXISn) card; use the LAGS_IN value.
186      cNAxis = 5;
187      readParm("LAGS_IN", TLONG, cNAxes);
188      cNAxes[1] = 1;
189      cNAxes[2] = 1;
190      cNAxes[3] = 1;
191      cNAxes[4] = 1;
192      cData[DATA].nelem = cNAxes[0];
193    }
194
195    if (cNAxis < 4) {
196      // Need at least four axes (for now).
197      cerr << "DATA array contains fewer than four axes." << endl;
198      close();
199      return 1;
200    } else if (cNAxis > 5) {
201      // We support up to five axes.
202      cerr << "DATA array contains more than five axes." << endl;
203      close();
204      return 1;
205    }
206
207    findData(FLAGGED, "FLAGGED", TBYTE);
208
209  } else {
210    // DATA column not present, check for a DATAXED keyword.
211    findData(DATAXED, "DATAXED", TSTRING);
212    if (cData[DATAXED].colnum < 0) {
213      cerr << "DATA array column absent from binary table." << endl;
214      close();
215      return 1;
216    }
217
218    // Determine the number of axes and their length.
219    char dataxed[32];
220    readParm("DATAXED", TSTRING, dataxed);
221
222    for (int iaxis = 0; iaxis < 5; iaxis++) cNAxes[iaxis] = 0;
223    sscanf(dataxed, "(%ld,%ld,%ld,%ld,%ld)", cNAxes, cNAxes+1, cNAxes+2,
224      cNAxes+3, cNAxes+4);
225    for (int iaxis = 4; iaxis > -1; iaxis--) {
226      if (cNAxes[iaxis] == 0) cNAxis = iaxis;
227    }
228  }
229
230  char  *CTYPE[5] = {"CTYPE1", "CTYPE2", "CTYPE3", "CTYPE4", "CTYPE5"};
231  char  *CRPIX[5] = {"CRPIX1", "CRPIX2", "CRPIX3", "CRPIX4", "CRPIX5"};
232  char  *CRVAL[5] = {"CRVAL1", "CRVAL2", "CRVAL3", "CRVAL4", "CRVAL5"};
233  char  *CDELT[5] = {"CDELT1", "CDELT2", "CDELT3", "CDELT4", "CDELT5"};
234
235  // Find required DATA array axes.
236  char ctype[5][72];
237  for (int iaxis = 0; iaxis < cNAxis; iaxis++) {
238    strcpy(ctype[iaxis], "");
239    readParm(CTYPE[iaxis], TSTRING, ctype[iaxis]);      // Core.
240  }
241
242  if (cStatus) {
243    reportError();
244    close();
245    return 1;
246  }
247
248  char *fqCRPIX  = 0;
249  char *fqCRVAL  = 0;
250  char *fqCDELT  = 0;
251  char *raCRVAL  = 0;
252  char *decCRVAL = 0;
253  char *timeCRVAL = 0;
254  char *beamCRVAL = 0;
255
256  for (int iaxis = 0; iaxis < cNAxis; iaxis++) {
257    if (strncmp(ctype[iaxis], "FREQ", 4) == 0) {
258      cReqax[0] = iaxis;
259      fqCRPIX  = CRPIX[iaxis];
260      fqCRVAL  = CRVAL[iaxis];
261      fqCDELT  = CDELT[iaxis];
262
263    } else if (strncmp(ctype[iaxis], "STOKES", 6) == 0) {
264      cReqax[1] = iaxis;
265
266    } else if (strncmp(ctype[iaxis], "RA", 2) == 0) {
267      cReqax[2] = iaxis;
268      raCRVAL  = CRVAL[iaxis];
269
270    } else if (strncmp(ctype[iaxis], "DEC", 3) == 0) {
271      cReqax[3] = iaxis;
272      decCRVAL = CRVAL[iaxis];
273
274    } else if (strcmp(ctype[iaxis], "TIME") == 0) {
275      // TIME (UTC seconds since midnight) can be a keyword or axis type.
276      timeCRVAL = CRVAL[iaxis];
277
278    } else if (strcmp(ctype[iaxis], "BEAM") == 0) {
279      // BEAM can be a keyword or axis type.
280      beamCRVAL = CRVAL[iaxis];
281    }
282  }
283
284  if (cALFA_BD) {
285    // Fixed in ALFA CIMAFITS.
286    cReqax[2] = 2;
287    raCRVAL = "CRVAL2A";
288
289    cReqax[3] = 3;
290    decCRVAL = "CRVAL3A";
291  }
292
293  // Check that all are present.
294  for (int iaxis = 0; iaxis < 4; iaxis++) {
295    if (cReqax[iaxis] < 0) {
296      cerr << "Could not find required DATA array axes." << endl;
297      close();
298      return 1;
299    }
300  }
301
302  // Set up machinery for data retrieval.
303  findData(SCAN,     "SCAN",     TINT);         // Shared.
304  findData(CYCLE,    "CYCLE",    TINT);         // Additional.
305  findData(DATE_OBS, "DATE-OBS", TSTRING);      // Core.
306  findData(TIME,     "TIME",     TDOUBLE);      // Core.
307  findData(EXPOSURE, "EXPOSURE", TFLOAT);       // Core.
308  findData(OBJECT,   "OBJECT",   TSTRING);      // Core.
309  findData(OBJ_RA,   "OBJ-RA",   TDOUBLE);      // Additional.
310  findData(OBJ_DEC,  "OBJ-DEC",  TDOUBLE);      // Additional.
311  findData(RESTFRQ,  "RESTFRQ",  TDOUBLE);      // Additional.
312  findData(OBSMODE,  "OBSMODE",  TSTRING);      // Shared.
313
314  findData(BEAM,     "BEAM",     TSHORT);       // Additional.
315  findData(IF,       "IF",       TSHORT);       // Additional.
316  findData(FqRefPix,  fqCRPIX,   TFLOAT);       // Frequency reference pixel.
317  findData(FqRefVal,  fqCRVAL,   TDOUBLE);      // Frequency reference value.
318  findData(FqDelt,    fqCDELT,   TDOUBLE);      // Frequency increment.
319  findData(RA,        raCRVAL,   TDOUBLE);      // Right ascension.
320  findData(DEC,      decCRVAL,   TDOUBLE);      // Declination.
321  findData(SCANRATE, "SCANRATE", TFLOAT);       // Additional.
322
323  findData(TSYS,     "TSYS",     TFLOAT);       // Core.
324  findData(CALFCTR,  "CALFCTR",  TFLOAT);       // Additional.
325  findData(XCALFCTR, "XCALFCTR", TFLOAT);       // Additional.
326  findData(BASELIN,  "BASELIN",  TFLOAT);       // Additional.
327  findData(BASESUB,  "BASESUB",  TFLOAT);       // Additional.
328  findData(XPOLDATA, "XPOLDATA", TFLOAT);       // Additional.
329
330  findData(REFBEAM,  "REFBEAM",  TSHORT);       // Additional.
331  findData(TCAL,     "TCAL",     TFLOAT);       // Shared.
332  findData(TCALTIME, "TCALTIME", TSTRING);      // Additional.
333  findData(AZIMUTH,  "AZIMUTH",  TFLOAT);       // Shared.
334  findData(ELEVATIO, "ELEVATIO", TFLOAT);       // Shared.
335  findData(PARANGLE, "PARANGLE", TFLOAT);       // Additional.
336  findData(FOCUSAXI, "FOCUSAXI", TFLOAT);       // Additional.
337  findData(FOCUSTAN, "FOCUSTAN", TFLOAT);       // Additional.
338  findData(FOCUSROT, "FOCUSROT", TFLOAT);       // Additional.
339  findData(TAMBIENT, "TAMBIENT", TFLOAT);       // Shared.
340  findData(PRESSURE, "PRESSURE", TFLOAT);       // Shared.
341  findData(HUMIDITY, "HUMIDITY", TFLOAT);       // Shared.
342  findData(WINDSPEE, "WINDSPEE", TFLOAT);       // Shared.
343  findData(WINDDIRE, "WINDDIRE", TFLOAT);       // Shared.
344
345  if (cStatus) {
346    reportError();
347    close();
348    return 1;
349  }
350
351
352  // Check for alternative column names.
353  if (cALFA) {
354    // ALFA data.
355    cALFAscan = 0;
356    cScanNo = 0;
357    if (cALFA_BD) {
358      findData(SCAN,  "SCAN_NUMBER", TINT);
359      findData(CYCLE, "PATTERN_NUMBER", TINT);
360    } else if (cALFA_CIMA) {
361      findData(SCAN,  "SCAN_ID", TINT);
362      findData(CYCLE, "SUBSCAN", TINT);
363    }
364  } else {
365    readData(SCAN, 1, &cFirstScanNo);
366  }
367
368  cCycleNo = 0;
369  cLastUTC = 0.0;
370
371  // Beam number, 1-relative by default.
372  cBeam_1rel = 1;
373  if (cData[BEAM].colnum < 0) {
374    if (beamCRVAL) {
375      // There is a BEAM axis.
376      findData(BEAM, beamCRVAL, TDOUBLE);
377
378    } else {
379      if (cALFA) {
380        // ALFA data, 0-relative.
381        findData(BEAM, "INPUT_ID", TSHORT);
382      } else {
383        // ms2sdfits output, 0-relative "feed" number.
384        findData(BEAM, "MAIN_FEED1", TSHORT);
385      }
386
387      cBeam_1rel = 0;
388    }
389  }
390
391  // IF number, 1-relative by default.
392  cIF_1rel = 1;
393  if (cALFA && cData[IF].colnum < 0) {
394    // ALFA data, 0-relative.
395    findData(IF, "IFVAL", TSHORT);
396    cIF_1rel = 0;
397  }
398
399  if (cData[TIME].colnum < 0) {
400    if (timeCRVAL) {
401      // There is a TIME axis.
402      findData(TIME, timeCRVAL, TDOUBLE);
403    }
404  }
405
406  // ms2sdfits writes a scalar "TSYS" column that averages the polarizations.
407  int colnum;
408  findCol("SYSCAL_TSYS", &colnum);
409  if (colnum > 0) {
410    // This contains the vector Tsys.
411    findData(TSYS, "SYSCAL_TSYS", TFLOAT);
412  }
413
414  // XPOLDATA?
415
416  if (cData[SCANRATE].colnum < 0) {
417    findData(SCANRATE, "FIELD_POINTING_DIR_RATE", TFLOAT);
418  }
419
420  if (cData[RESTFRQ].colnum < 0) {
421    findData(RESTFRQ, "RESTFREQ", TDOUBLE);
422    if (cData[RESTFRQ].colnum < 0) {
423      findData(RESTFRQ, "SPECTRAL_WINDOW_REST_FREQUENCY", TDOUBLE);
424    }
425  }
426
427  if (cData[OBJ_RA].colnum < 0) {
428    findData(OBJ_RA, "SOURCE_DIRECTION", TDOUBLE);
429  }
430  if (cData[OBJ_DEC].colnum < 0) {
431    findData(OBJ_DEC, "SOURCE_DIRECTION", TDOUBLE);
432  }
433
434  // REFBEAM?
435
436  if (cData[TCAL].colnum < 0) {
437    findData(TCAL, "SYSCAL_TCAL", TFLOAT);
438  } else if (cALFA_BD) {
439    // ALFA BDFITS has a different TCAL with 64 elements - kill it!
440    findData(TCAL, "NO NO NO", TFLOAT);
441  }
442
443  if (cALFA_BD) {
444    // ALFA BDFITS.
445    findData(AZIMUTH, "CRVAL2B", TFLOAT);
446    findData(ELEVATIO, "CRVAL3B", TFLOAT);
447  }
448
449  if (cALFA) {
450    // ALFA data.
451    findData(PARANGLE, "PARA_ANG", TFLOAT);
452  }
453
454  if (cData[TAMBIENT].colnum < 0) {
455    findData(TAMBIENT, "WEATHER_TEMPERATURE", TFLOAT);
456  }
457
458  if (cData[PRESSURE].colnum < 0) {
459    findData(PRESSURE, "WEATHER_PRESSURE", TFLOAT);
460  }
461
462  if (cData[HUMIDITY].colnum < 0) {
463    findData(HUMIDITY, "WEATHER_REL_HUMIDITY", TFLOAT);
464  }
465
466  if (cData[WINDSPEE].colnum < 0) {
467    findData(WINDSPEE, "WEATHER_WIND_SPEED", TFLOAT);
468  }
469
470  if (cData[WINDDIRE].colnum < 0) {
471    findData(WINDDIRE, "WEATHER_WIND_DIRECTION", TFLOAT);
472  }
473
474
475  // Find the number of rows.
476  fits_get_num_rows(cSDptr, &cNRow, &cStatus);
477  if (!cNRow) {
478    cerr << "Table contains no entries." << endl;
479    close();
480    return 1;
481  }
482
483
484  // Determine which beams are present in the data.
485  if (cData[BEAM].colnum > 0) {
486    short *beamCol = new short[cNRow];
487    short beamNul = 1;
488    int   anynul;
489    if (fits_read_col(cSDptr, TSHORT, cData[BEAM].colnum, 1, 1, cNRow,
490                      &beamNul, beamCol, &anynul, &cStatus)) {
491      reportError();
492      delete [] beamCol;
493      close();
494      return 1;
495    }
496
497    // Find the maximum beam number.
498    cNBeam = cBeam_1rel - 1;
499    for (int irow = 0; irow < cNRow; irow++) {
500      if (beamCol[irow] > cNBeam) {
501        cNBeam = beamCol[irow];
502      }
503
504      // Check validity.
505      if (beamCol[irow] < cBeam_1rel) {
506        cerr << "SDFITS file contains invalid beam number." << endl;
507        delete [] beamCol;
508        close();
509        return 1;
510      }
511    }
512
513    if (!cBeam_1rel) cNBeam++;
514
515    // Find all beams present in the data.
516    cBeams = new int[cNBeam];
517    for (int ibeam = 0; ibeam < cNBeam; ibeam++) {
518      cBeams[ibeam] = 0;
519    }
520
521    for (int irow = 0; irow < cNRow; irow++) {
522      cBeams[beamCol[irow] - cBeam_1rel] = 1;
523    }
524
525    delete [] beamCol;
526
527  } else {
528    // No BEAM column.
529    cNBeam = 1;
530    cBeams = new int[1];
531    cBeams[0] = 1;
532  }
533
534  // Passing back the address of the array allows PKSFITSreader::select() to
535  // modify its elements directly.
536  nBeam = cNBeam;
537  beams = cBeams;
538
539
540  // Determine which IFs are present in the data.
541  if (cData[IF].colnum > 0) {
542    short *IFCol = new short[cNRow];
543    short IFNul = 1;
544    int   anynul;
545    if (fits_read_col(cSDptr, TSHORT, cData[IF].colnum, 1, 1, cNRow,
546                      &IFNul, IFCol, &anynul, &cStatus)) {
547      reportError();
548      delete [] IFCol;
549      close();
550      return 1;
551    }
552
553    // Find the maximum IF number.
554    cNIF = cIF_1rel - 1;
555    for (int irow = 0; irow < cNRow; irow++) {
556      if (IFCol[irow] > cNIF) {
557        cNIF = IFCol[irow];
558      }
559
560      // Check validity.
561      if (IFCol[irow] < cIF_1rel) {
562        cerr << "SDFITS file contains invalid IF number." << endl;
563        delete [] IFCol;
564        close();
565        return 1;
566      }
567    }
568
569    if (!cIF_1rel) cNIF++;
570
571    // Find all IFs present in the data.
572    cIFs      = new int[cNIF];
573    cNChan    = new int[cNIF];
574    cNPol     = new int[cNIF];
575    cHaveXPol = new int[cNIF];
576    cGetXPol  = 0;
577
578    for (int iIF = 0; iIF < cNIF; iIF++) {
579      cIFs[iIF]   = 0;
580      cNChan[iIF] = 0;
581      cNPol[iIF]  = 0;
582      cHaveXPol[iIF] = 0;
583    }
584
585    for (int irow = 0; irow < cNRow; irow++) {
586      int iIF = IFCol[irow] - cIF_1rel;
587      if (cIFs[iIF] == 0) {
588        cIFs[iIF] = 1;
589
590        // Find the axis lengths.
591        if (cHaveSpectra) {
592          if (cData[DATA].nelem < 0) {
593            // Variable dimension array.
594            if (readDim(DATA, irow+1, &cNAxis, cNAxes)) {
595              reportError();
596              close();
597              return 1;
598            }
599          }
600
601        } else {
602          if (cData[DATAXED].colnum > 0) {
603            char dataxed[32];
604            readParm("DATAXED", TSTRING, dataxed);
605
606            sscanf(dataxed, "(%ld,%ld,%ld,%ld,%ld)", cNAxes, cNAxes+1,
607              cNAxes+2, cNAxes+3, cNAxes+4);
608          }
609        }
610
611        // Number of channels and polarizations.
612        cNChan[iIF]    = cNAxes[cReqax[0]];
613        cNPol[iIF]     = cNAxes[cReqax[1]];
614        cHaveXPol[iIF] = 0;
615
616        // Is cross-polarization data present?
617        if (cData[XPOLDATA].colnum > 0) {
618          // Check that it conforms.
619          int  nAxis;
620          long nAxes[2];
621
622          if (readDim(XPOLDATA, irow+1, &nAxis, nAxes)) {
623            reportError();
624            close();
625            return 1;
626          }
627
628          // Default is to get it if we have it.
629          if (nAxis    == 2 &&
630              nAxes[0] == 2 &&
631              nAxes[1] == cNChan[iIF]) {
632            cGetXPol = cHaveXPol[iIF] = 1;
633          }
634        }
635      }
636    }
637
638    delete [] IFCol;
639
640  } else {
641    // No IF column.
642    cNIF = 1;
643    cIFs = new int[1];
644    cIFs[0] = 1;
645
646    cNChan    = new int[1];
647    cNPol     = new int[1];
648    cHaveXPol = new int[1];
649    cGetXPol  = 0;
650
651    // Number of channels and polarizations.
652    cNChan[0] = cNAxes[cReqax[0]];
653    cNPol[0]  = cNAxes[cReqax[1]];
654    cHaveXPol[0] = 0;
655  }
656
657  if (cALFA) {
658    // ALFA labels each polarization as a separate IF.
659    cNPol[0] = cNIF;
660    cNIF = 1;
661  }
662
663  // Passing back the address of the array allows PKSFITSreader::select() to
664  // modify its elements directly.
665  nIF = cNIF;
666  IFs = cIFs;
667
668  nChan    = cNChan;
669  nPol     = cNPol;
670  haveXPol = cHaveXPol;
671
672
673  // Default channel range selection.
674  cStartChan = new int[cNIF];
675  cEndChan   = new int[cNIF];
676  cRefChan   = new int[cNIF];
677
678  for (int iIF = 0; iIF < cNIF; iIF++) {
679    cStartChan[iIF] = 1;
680    cEndChan[iIF] = cNChan[iIF];
681    cRefChan[iIF] = cNChan[iIF]/2 + 1;
682  }
683
684  // Default is to get it if we have it.
685  cGetSpectra = cHaveSpectra;
686
687
688  // Are baseline parameters present?
689  cHaveBase = 0;
690  if (cData[BASELIN].colnum) {
691    // Check that it conforms.
692    int  nAxis, status = 0;
693    long nAxes[2];
694
695    if (fits_read_tdim(cSDptr, cData[BASELIN].colnum, 2, &nAxis, nAxes,
696                       &status) == 0) {
697      cHaveBase = (nAxis == 2);
698    }
699  }
700  haveBase = cHaveBase;
701
702
703  // Is extra system calibration data available?
704  cExtraSysCal = 0;
705  for (int iparm = REFBEAM; iparm < NDATA; iparm++) {
706    if (cData[iparm].colnum >= 0) {
707      cExtraSysCal = 1;
708      break;
709    }
710  }
711
712  extraSysCal = cExtraSysCal;
713
714  return 0;
715}
716
717//---------------------------------------------------- SDFITSreader::getHeader
718
719// Get parameters describing the data.
720
721int SDFITSreader::getHeader(
722        char   observer[32],
723        char   project[32],
724        char   telescope[32],
725        double antPos[3],
726        char   obsMode[32],
727        float  &equinox,
728        char   radecsys[32],
729        char   dopplerFrame[32],
730        char   datobs[32],
731        double &utc,
732        double &refFreq,
733        double &bandwidth)
734{
735  // Has the file been opened?
736  if (!cSDptr) {
737    return 1;
738  }
739
740  // Read parameter values.
741  readParm("OBSERVER", TSTRING, observer);              // Shared.
742  readParm("PROJID",   TSTRING, project);               // Shared.
743  readParm("TELESCOP", TSTRING, telescope);             // Core.
744
745  antPos[0] = 0.0;
746  antPos[1] = 0.0;
747  antPos[2] = 0.0;
748  if (readParm("ANTENNA_POSITION", TDOUBLE, antPos)) {
749    readParm("OBSGEO-X",  TDOUBLE, antPos);             // Additional.
750    readParm("OBSGEO-Y",  TDOUBLE, antPos + 1);         // Additional.
751    readParm("OBSGEO-Z",  TDOUBLE, antPos + 2);         // Additional.
752  }
753
754  if (antPos[0] == 0.0) {
755    if (strncmp(telescope, "ATPKS", 5) == 0) {
756      // Parkes coordinates.
757      antPos[0] = -4554232.087;
758      antPos[1] =  2816759.046;
759      antPos[2] = -3454035.950;
760    } else if (strncmp(telescope, "ATMOPRA", 7) == 0) {
761      // Mopra coordinates.
762      antPos[0] = -4682768.630;
763      antPos[1] =  2802619.060;
764      antPos[2] = -3291759.900;
765    } else if (strncmp(telescope, "ARECIBO", 7) == 0) {
766      // Arecibo coordinates.
767      antPos[0] =  2390486.900;
768      antPos[1] = -5564731.440;
769      antPos[2] =  1994720.450;
770    }
771  }
772
773  readData(OBSMODE, 1, obsMode);                        // Shared.
774
775  readParm("EQUINOX",  TFLOAT,  &equinox);              // Shared.
776  if (cStatus == 405) {
777    // EQUINOX was written as string value in early versions.
778    cStatus = 0;
779    char strtmp[32];
780    readParm("EQUINOX", TSTRING, strtmp);
781    sscanf(strtmp, "%f", &equinox);
782  }
783
784  if (readParm("RADESYS", TSTRING, radecsys)) {         // Additional.
785    if (readParm("RADECSYS", TSTRING, radecsys)) {      // Additional.
786      strcpy(radecsys, "");
787    }
788  }
789
790  if (readParm("SPECSYS", TSTRING, dopplerFrame)) {     // Additional.
791    // Fallback value.
792    strcpy(dopplerFrame, "TOPOCENT");
793
794    // Look for VELFRAME, written by earlier versions of Livedata.
795    if (readParm("VELFRAME", TSTRING, dopplerFrame)) {  // Additional.
796      // No, try digging it out of the CTYPE card (AIPS convention).
797      char keyw[9], ctype[9];
798      sprintf(keyw, "CTYPE%ld", cReqax[0]+1);
799      readParm(keyw, TSTRING, ctype);
800
801      if (strncmp(ctype, "FREQ-", 5) == 0) {
802        strcpy(dopplerFrame, ctype+5);
803        if (strcmp(dopplerFrame, "LSR") == 0) {
804          // LSR unqualified usually means LSR (kinematic).
805          strcpy(dopplerFrame, "LSRK");
806        } else if (strcmp(dopplerFrame, "HEL") == 0) {
807          // Almost certainly barycentric.
808          strcpy(dopplerFrame, "BARYCENT");
809        }
810      } else {
811        strcpy(dopplerFrame, "");
812      }
813    }
814
815    // Translate to FITS standard names.
816    if (strncmp(dopplerFrame, "TOP", 3) == 0) {
817      strcpy(dopplerFrame, "TOPOCENT");
818    } else if (strncmp(dopplerFrame, "GEO", 3) == 0) {
819      strcpy(dopplerFrame, "GEOCENTR");
820    } else if (strncmp(dopplerFrame, "HEL", 3) == 0) {
821      strcpy(dopplerFrame, "HELIOCEN");
822    } else if (strncmp(dopplerFrame, "BARY", 4) == 0) {
823      strcpy(dopplerFrame, "BARYCENT");
824    }
825  }
826
827  if (cStatus) {
828    reportError();
829    return 1;
830  }
831
832  // Get parameters from first row of table.
833  readData(DATE_OBS, 1, datobs);
834  readData(TIME,     1, &utc);
835  readData(FqRefVal, 1, &refFreq);
836  readParm("BANDWID", TDOUBLE, &bandwidth);             // Core.
837
838  if (cALFA_BD) utc *= 3600.0;
839
840  if (cStatus) {
841    reportError();
842    return 1;
843  }
844
845  // Check DATE-OBS format.
846  if (datobs[2] == '/') {
847    // Translate an old-format DATE-OBS.
848    datobs[9] = datobs[1];
849    datobs[8] = datobs[0];
850    datobs[2] = datobs[6];
851    datobs[5] = datobs[3];
852    datobs[3] = datobs[7];
853    datobs[6] = datobs[4];
854    datobs[7] = '-';
855    datobs[4] = '-';
856    datobs[1] = '9';
857    datobs[0] = '1';
858    datobs[10] = '\0';
859
860  } else if (datobs[10] == 'T' && cData[TIME].colnum < 0) {
861    // Dig UTC out of a new-format DATE-OBS.
862    int   hh, mm;
863    float ss;
864    sscanf(datobs+11, "%d:%d:%f", &hh, &mm, &ss);
865    utc = (hh*60 + mm)*60 + ss;
866    datobs[10] = '\0';
867  }
868
869  return 0;
870}
871
872//-------------------------------------------------- SDFITSreader::getFreqInfo
873
874// Get frequency parameters for each IF.
875
876int SDFITSreader::getFreqInfo(
877        int     &nIF,
878        double* &startFreq,
879        double* &endFreq)
880{
881  float  fqRefPix;
882  double fqDelt, fqRefVal;
883
884  nIF = cNIF;
885  startFreq = new double[nIF];
886  endFreq   = new double[nIF];
887
888  if (cData[IF].colnum > 0) {
889    short *IFCol = new short[cNRow];
890    short IFNul = 1;
891    int   anynul;
892    if (fits_read_col(cSDptr, TSHORT, cData[IF].colnum, 1, 1, cNRow,
893                      &IFNul, IFCol, &anynul, &cStatus)) {
894      reportError();
895      delete [] IFCol;
896      close();
897      return 1;
898    }
899
900    for (int iIF = 0; iIF < nIF; iIF++) {
901      if (cIFs[iIF]) {
902        // Find the first occurrence of this IF in the table.
903        int IFno = iIF + cIF_1rel;
904        for (int irow = 0; irow < cNRow;) {
905          if (IFCol[irow++] == IFno) {
906            readData(FqRefPix, irow, &fqRefPix);
907            readData(FqRefVal, irow, &fqRefVal);
908            readData(FqDelt,   irow, &fqDelt);
909
910            if (cALFA_BD) {
911              unsigned char invert;
912              readData("UPPERSB", TBYTE, irow, &invert);
913
914              if (invert) {
915                fqDelt = -fqDelt;
916              }
917            }
918
919            startFreq[iIF] = fqRefVal + (          1 - fqRefPix) * fqDelt;
920            endFreq[iIF]   = fqRefVal + (cNChan[iIF] - fqRefPix) * fqDelt;
921
922            break;
923          }
924        }
925
926      } else {
927        startFreq[iIF] = 0.0;
928        endFreq[iIF]   = 0.0;
929      }
930    }
931
932    delete [] IFCol;
933
934  } else {
935    // No IF column, read the first table entry.
936    readData(FqRefPix, 1, &fqRefPix);
937    readData(FqRefVal, 1, &fqRefVal);
938    readData(FqDelt,   1, &fqDelt);
939
940    startFreq[0] = fqRefVal + (        1 - fqRefPix) * fqDelt;
941    endFreq[0]   = fqRefVal + (cNChan[0] - fqRefPix) * fqDelt;
942  }
943
944  return cStatus;
945}
946
947//---------------------------------------------------- SDFITSreader::findRange
948
949// Find the range of the data in time and position.
950
951int SDFITSreader::findRange(
952        int    &nRow,
953        int    &nSel,
954        char   dateSpan[2][32],
955        double utcSpan[2],
956        double* &positions)
957{
958  int anynul;
959
960  // Has the file been opened?
961  if (!cSDptr) {
962    return 1;
963  }
964
965  nRow = cNRow;
966
967  // Find the number of rows selected.
968  short *sel = new short[nRow];
969  for (int irow = 0; irow < nRow; irow++) {
970    sel[irow] = 1;
971  }
972
973  if (cData[BEAM].colnum > 0) {
974    short *beamCol = new short[cNRow];
975    short beamNul = 1;
976    if (fits_read_col(cSDptr, TSHORT, cData[BEAM].colnum, 1, 1, cNRow,
977                      &beamNul, beamCol, &anynul, &cStatus)) {
978      reportError();
979      delete [] beamCol;
980      delete [] sel;
981      return 1;
982    }
983
984    for (int irow = 0; irow < nRow; irow++) {
985      if (!cBeams[beamCol[irow]-cBeam_1rel]) {
986        sel[irow] = 0;
987      }
988    }
989
990    delete [] beamCol;
991  }
992
993  if (cData[IF].colnum > 0) {
994    short *IFCol = new short[cNRow];
995    short IFNul = 1;
996    if (fits_read_col(cSDptr, TSHORT, cData[IF].colnum, 1, 1, cNRow,
997                      &IFNul, IFCol, &anynul, &cStatus)) {
998      reportError();
999      delete [] IFCol;
1000      delete [] sel;
1001      return 1;
1002    }
1003
1004    for (int irow = 0; irow < nRow; irow++) {
1005      if (!cIFs[IFCol[irow]-cIF_1rel]) {
1006        sel[irow] = 0;
1007      }
1008    }
1009
1010    delete [] IFCol;
1011  }
1012
1013  nSel = 0;
1014  for (int irow = 0; irow < nRow; irow++) {
1015    nSel += sel[irow];
1016  }
1017
1018
1019  // Find the time range assuming the data is in chronological order.
1020  readData(DATE_OBS, 1,    dateSpan[0]);
1021  readData(DATE_OBS, nRow, dateSpan[1]);
1022  readData(TIME, 1,    utcSpan);
1023  readData(TIME, nRow, utcSpan+1);
1024
1025  if (cALFA_BD) {
1026    utcSpan[0] *= 3600.0;
1027    utcSpan[1] *= 3600.0;
1028  }
1029
1030  // Check DATE-OBS format.
1031  for (int i = 0; i < 2; i++) {
1032    if (dateSpan[0][2] == '/') {
1033      // Translate an old-format DATE-OBS.
1034      dateSpan[i][9] = dateSpan[i][1];
1035      dateSpan[i][8] = dateSpan[i][0];
1036      dateSpan[i][2] = dateSpan[i][6];
1037      dateSpan[i][5] = dateSpan[i][3];
1038      dateSpan[i][3] = dateSpan[i][7];
1039      dateSpan[i][6] = dateSpan[i][4];
1040      dateSpan[i][7] = '-';
1041      dateSpan[i][4] = '-';
1042      dateSpan[i][1] = '9';
1043      dateSpan[i][0] = '1';
1044      dateSpan[i][10] = '\0';
1045    }
1046
1047    if (dateSpan[i][10] == 'T' && cData[TIME].colnum < 0) {
1048      // Dig UTC out of a new-format DATE-OBS.
1049      int   hh, mm;
1050      float ss;
1051      sscanf(dateSpan[i]+11, "%d:%d:%f", &hh, &mm, &ss);
1052      utcSpan[i] = (hh*60 + mm)*60 + ss;
1053    }
1054  }
1055
1056
1057  // Retrieve positions for selected data.
1058  double *ra  = new double[cNRow];
1059  double *dec = new double[cNRow];
1060  fits_read_col(cSDptr, TDOUBLE, cData[RA].colnum,  1, 1, nRow, 0, ra,
1061                &anynul, &cStatus);
1062  fits_read_col(cSDptr, TDOUBLE, cData[DEC].colnum, 1, 1, nRow, 0, dec,
1063                &anynul, &cStatus);
1064
1065  if (cALFA_BD) {
1066    for (int irow = 0; irow < nRow; irow++) {
1067      // Convert hours to degrees.
1068      ra[irow] *= 15.0;
1069    }
1070  }
1071
1072  int isel = 0;
1073  positions = new double[2*nSel];
1074
1075  // Parameters needed to compute feed-plane coordinates.
1076  double *srcRA, *srcDec;
1077  float  *par, *rot;
1078  if (cGetFeedPos) {
1079    srcRA  = new double[cNRow];
1080    srcDec = new double[cNRow];
1081    par    = new float[cNRow];
1082    rot    = new float[cNRow];
1083    fits_read_col(cSDptr, TDOUBLE, cData[OBJ_RA].colnum,   1, 1, nRow, 0,
1084                  srcRA,  &anynul, &cStatus);
1085    fits_read_col(cSDptr, TDOUBLE, cData[OBJ_DEC].colnum,  1, 1, nRow, 0,
1086                  srcDec, &anynul, &cStatus);
1087    fits_read_col(cSDptr, TFLOAT,  cData[PARANGLE].colnum, 1, 1, nRow, 0,
1088                  par,    &anynul, &cStatus);
1089    fits_read_col(cSDptr, TFLOAT,  cData[FOCUSROT].colnum, 1, 1, nRow, 0,
1090                  rot,    &anynul, &cStatus);
1091
1092    for (int irow = 0; irow < nRow; irow++) {
1093      if (sel[irow]) {
1094        // Convert to feed-plane coordinates.
1095        Double dist, pa;
1096        distPA(ra[irow]*D2R, dec[irow]*D2R, srcRA[irow]*D2R, srcDec[irow]*D2R,
1097               dist, pa);
1098
1099        Double spin = (par[irow] + rot[irow])*D2R - pa + PI;
1100        if (spin > 2.0*PI) spin -= 2.0*PI;
1101        Double squint = PI/2.0 - dist;
1102
1103        positions[isel++] = spin;
1104        positions[isel++] = squint;
1105      }
1106    }
1107
1108    delete [] srcRA;
1109    delete [] srcDec;
1110    delete [] par;
1111    delete [] rot;
1112
1113  } else {
1114    for (int irow = 0; irow < nRow; irow++) {
1115      if (sel[irow]) {
1116        positions[isel++] =  ra[irow] * D2R;
1117        positions[isel++] = dec[irow] * D2R;
1118      }
1119    }
1120  }
1121
1122  delete [] sel;
1123  delete [] ra;
1124  delete [] dec;
1125
1126  return cStatus;
1127}
1128
1129
1130//--------------------------------------------------------- SDFITSreader::read
1131
1132// Read the next data record.
1133
1134int SDFITSreader::read(
1135        PKSMBrecord &mbrec)
1136{
1137  // Has the file been opened?
1138  if (!cSDptr) {
1139    return 1;
1140  }
1141
1142  // Find the next selected beam and IF.
1143  short iBeam = 0, iIF = 0;
1144  while (++cRow <= cNRow) {
1145    if (cData[BEAM].colnum > 0) {
1146      readData(BEAM, cRow, &iBeam);
1147
1148      // Convert to 0-relative.
1149      if (cBeam_1rel) iBeam--;
1150    }
1151
1152
1153    if (cBeams[iBeam]) {
1154      if (cData[IF].colnum > 0) {
1155        readData(IF, cRow, &iIF);
1156
1157        // Convert to 0-relative.
1158        if (cIF_1rel) iIF--;
1159      }
1160
1161      if (cIFs[iIF]) {
1162        if (cALFA) {
1163          // ALFA data, check for calibration data.
1164          char chars[32];
1165          readData(OBSMODE, cRow, chars);
1166          if (strcmp(chars, "CAL") == 0) {
1167            // iIF is really the polarization in ALFA data.
1168            alfaCal(iBeam, iIF);
1169            continue;
1170          }
1171        }
1172
1173        break;
1174      }
1175    }
1176  }
1177
1178  // EOF?
1179  if (cRow > cNRow) {
1180    return -1;
1181  }
1182
1183
1184  if (cALFA) {
1185    int scanNo;
1186    readData(SCAN, cRow, &scanNo);
1187    if (scanNo != cALFAscan) {
1188      cScanNo++;
1189      cALFAscan = scanNo;
1190    }
1191    mbrec.scanNo = cScanNo;
1192
1193  } else {
1194    readData(SCAN, cRow, &mbrec.scanNo);
1195
1196    // Ensure that scan number is 1-relative.
1197    mbrec.scanNo -= (cFirstScanNo - 1);
1198  }
1199
1200  // Times.
1201  char datobs[32];
1202  readData(DATE_OBS, cRow,  datobs);
1203  readData(TIME,     cRow, &mbrec.utc);
1204  if (cALFA_BD) mbrec.utc *= 3600.0;
1205
1206  if (datobs[2] == '/') {
1207    // Translate an old-format DATE-OBS.
1208    datobs[9] = datobs[1];
1209    datobs[8] = datobs[0];
1210    datobs[2] = datobs[6];
1211    datobs[5] = datobs[3];
1212    datobs[3] = datobs[7];
1213    datobs[6] = datobs[4];
1214    datobs[7] = '-';
1215    datobs[4] = '-';
1216    datobs[1] = '9';
1217    datobs[0] = '1';
1218
1219  } else if (datobs[10] == 'T' && cData[TIME].colnum < 0) {
1220    // Dig UTC out of a new-format DATE-OBS.
1221    int   hh, mm;
1222    float ss;
1223    sscanf(datobs+11, "%d:%d:%f", &hh, &mm, &ss);
1224    mbrec.utc = (hh*60 + mm)*60 + ss;
1225  }
1226
1227  datobs[10] = '\0';
1228  strcpy(mbrec.datobs, datobs);
1229
1230  if (cData[CYCLE].colnum > 0) {
1231    readData(CYCLE, cRow, &mbrec.cycleNo);
1232    if (cALFA_BD) mbrec.cycleNo++;
1233  } else {
1234    // Cycle number not recorded, must do our own bookkeeping.
1235    if (mbrec.utc != cLastUTC) {
1236      mbrec.cycleNo = ++cCycleNo;
1237      cLastUTC = mbrec.utc;
1238    }
1239  }
1240
1241  readData(EXPOSURE, cRow, &mbrec.exposure);
1242
1243  // Source identification.
1244  readData(OBJECT,  cRow,  mbrec.srcName);
1245
1246  readData(OBJ_RA,  cRow, &mbrec.srcRA);
1247  if (strcmp(cData[OBJ_RA].name, "OBJ-RA") == 0) {
1248    mbrec.srcRA  *= D2R;
1249  }
1250
1251  if (strcmp(cData[OBJ_DEC].name, "OBJ-DEC") == 0) {
1252    readData(OBJ_DEC, cRow, &mbrec.srcDec);
1253    mbrec.srcDec *= D2R;
1254  }
1255
1256  // Line rest frequency (Hz).
1257  readData(RESTFRQ, cRow, &mbrec.restFreq);
1258  if (mbrec.restFreq == 0.0 && cALFA_BD) {
1259    mbrec.restFreq = 1420.40575e6;
1260  }
1261
1262  // Observation mode.
1263  readData(OBSMODE, cRow, mbrec.obsType);
1264
1265  // Beam-dependent parameters.
1266  mbrec.beamNo = iBeam + 1;
1267
1268  readData(RA,  cRow, &mbrec.ra);
1269  readData(DEC, cRow, &mbrec.dec);
1270  mbrec.ra  *= D2R;
1271  mbrec.dec *= D2R;
1272
1273  if (cALFA_BD) mbrec.ra *= 15.0;
1274
1275  float scanrate[2];
1276  readData(SCANRATE, cRow, &scanrate);
1277  if (strcmp(cData[SCANRATE].name, "SCANRATE") == 0) {
1278    mbrec.raRate  = scanrate[0] * D2R;
1279    mbrec.decRate = scanrate[1] * D2R;
1280  }
1281
1282  // IF-dependent parameters.
1283  int startChan = cStartChan[iIF];
1284  int endChan   = cEndChan[iIF];
1285  int refChan   = cRefChan[iIF];
1286
1287  // Allocate data storage.
1288  int nChan = abs(endChan - startChan) + 1;
1289  int nPol = cNPol[iIF];
1290
1291  if (cGetSpectra || cGetXPol) {
1292    int nxpol = cGetXPol ? 2*nChan : 0;
1293    mbrec.allocate(0, nChan*nPol, nxpol);
1294  }
1295
1296  mbrec.nIF = 1;
1297  mbrec.IFno[0]  = iIF + 1;
1298  mbrec.nChan[0] = nChan;
1299  mbrec.nPol[0]  = nPol;
1300
1301  readData(FqRefPix, cRow, mbrec.fqRefPix);
1302  readData(FqRefVal, cRow, mbrec.fqRefVal);
1303  readData(FqDelt,   cRow, mbrec.fqDelt);
1304
1305  if (cALFA_BD) {
1306    unsigned char invert;
1307    int anynul, colnum;
1308    findCol("UPPERSB", &colnum);
1309    fits_read_col(cSDptr, TBYTE, colnum, cRow, 1, 1, 0, &invert, &anynul,
1310                  &cStatus);
1311
1312    if (invert) {
1313      mbrec.fqDelt[0] = -mbrec.fqDelt[0];
1314    }
1315  }
1316
1317  if (cStatus) {
1318    reportError();
1319    return 1;
1320  }
1321
1322  // Adjust for channel selection.
1323  if (mbrec.fqRefPix[0] != refChan) {
1324    mbrec.fqRefVal[0] += (refChan - mbrec.fqRefPix[0]) * mbrec.fqDelt[0];
1325    mbrec.fqRefPix[0]  =  refChan;
1326  }
1327
1328  if (endChan < startChan) {
1329    mbrec.fqDelt[0] = -mbrec.fqDelt[0];
1330  }
1331
1332  // The data may only have a scalar Tsys value.
1333  mbrec.tsys[0][0] = 0.0f;
1334  mbrec.tsys[0][1] = 0.0f;
1335  if (cData[TSYS].nelem >= nPol) {
1336    readData(TSYS, cRow, mbrec.tsys[0]);
1337  }
1338
1339  for (int j = 0; j < 2; j++) {
1340    mbrec.calfctr[0][j] = 0.0f;
1341  }
1342  if (cData[CALFCTR].colnum > 0) {
1343    readData(CALFCTR, cRow, mbrec.calfctr);
1344  }
1345
1346  if (cHaveBase) {
1347    mbrec.haveBase = 1;
1348    readData(BASELIN, cRow, mbrec.baseLin);
1349    readData(BASESUB, cRow, mbrec.baseSub);
1350  } else {
1351    mbrec.haveBase = 0;
1352  }
1353
1354  if (cStatus) {
1355    reportError();
1356    return 1;
1357  }
1358
1359  // Read data, sectioning and transposing it in the process.
1360  long *blc = new long[cNAxis+1];
1361  long *trc = new long[cNAxis+1];
1362  long *inc = new long[cNAxis+1];
1363  for (int iaxis = 0; iaxis <= cNAxis; iaxis++) {
1364    blc[iaxis] = 1;
1365    trc[iaxis] = 1;
1366    inc[iaxis] = 1;
1367  }
1368
1369  blc[cReqax[0]] = std::min(startChan, endChan);
1370  trc[cReqax[0]] = std::max(startChan, endChan);
1371  blc[cNAxis] = cRow;
1372  trc[cNAxis] = cRow;
1373
1374  mbrec.haveSpectra = cGetSpectra;
1375  if (cGetSpectra) {
1376    int  anynul;
1377
1378    for (int ipol = 0; ipol < nPol; ipol++) {
1379      blc[cReqax[1]] = ipol+1;
1380      trc[cReqax[1]] = ipol+1;
1381
1382      if (cALFA) {
1383        // ALFA data: polarizations are stored in successive rows.
1384        blc[cReqax[1]] = 1;
1385        trc[cReqax[1]] = 1;
1386
1387        if (ipol) {
1388          if (++cRow > cNRow) {
1389            return -1;
1390          }
1391
1392          blc[cNAxis] = cRow;
1393          trc[cNAxis] = cRow;
1394        }
1395
1396      } else if (cData[DATA].nelem < 0) {
1397        // Variable dimension array; get axis lengths.
1398        int  naxis = 5, status;
1399
1400        if ((status = readDim(DATA, cRow, &naxis, cNAxes))) {
1401          reportError();
1402
1403        } else if ((status = (naxis != cNAxis))) {
1404          cerr << "DATA array dimensions changed." << endl;
1405        }
1406
1407        if (status) {
1408          delete [] blc;
1409          delete [] trc;
1410          delete [] inc;
1411          return 1;
1412        }
1413      }
1414
1415      if (fits_read_subset_flt(cSDptr, cData[DATA].colnum, cNAxis, cNAxes,
1416          blc, trc, inc, 0, mbrec.spectra[0] + ipol*nChan, &anynul,
1417          &cStatus)) {
1418        reportError();
1419        delete [] blc;
1420        delete [] trc;
1421        delete [] inc;
1422        return 1;
1423      }
1424
1425      if (endChan < startChan) {
1426        // Reverse the spectrum.
1427        float *iptr = mbrec.spectra[0] + ipol*nChan;
1428        float *jptr = iptr + nChan - 1;
1429        float *mid  = iptr + nChan/2;
1430        while (iptr < mid) {
1431          float tmp = *iptr;
1432          *(iptr++) = *jptr;
1433          *(jptr--) = tmp;
1434        }
1435      }
1436
1437      if (cALFA) {
1438        // ALFA data, rescale the spectrum.
1439        float *chan  = mbrec.spectra[0] + ipol*nChan;
1440        float *chanN = chan + nChan;
1441        while (chan < chanN) {
1442          // Approximate conversion to Jy.
1443          *(chan++) *= cALFAcal[iBeam][iIF];
1444        }
1445      }
1446
1447      if (mbrec.tsys[0][ipol] == 0.0) {
1448        // Compute Tsys as the average across the spectrum.
1449        float *chan  = mbrec.spectra[0] + ipol*nChan;
1450        float *chanN = chan + nChan;
1451        float *tsys = mbrec.tsys[0] + ipol;
1452        while (chan < chanN) {
1453          *tsys += *(chan++);
1454        }
1455
1456        *tsys /= nChan;
1457      }
1458
1459      // Read data flags.
1460      if (cData[FLAGGED].colnum > 0) {
1461        if (fits_read_subset_byt(cSDptr, cData[FLAGGED].colnum, cNAxis,
1462            cNAxes, blc, trc, inc, 0, mbrec.flagged[0] + ipol*nChan, &anynul,
1463            &cStatus)) {
1464          reportError();
1465          delete [] blc;
1466          delete [] trc;
1467          delete [] inc;
1468          return 1;
1469        }
1470
1471        if (endChan < startChan) {
1472          // Reverse the flag vector.
1473          unsigned char *iptr = mbrec.flagged[0] + ipol*nChan;
1474          unsigned char *jptr = iptr + nChan - 1;
1475          for (int ichan = 0; ichan < nChan/2; ichan++) {
1476            unsigned char tmp = *iptr;
1477            *(iptr++) = *jptr;
1478            *(jptr--) = tmp;
1479          }
1480        }
1481
1482      } else {
1483        // All channels are unflagged by default.
1484        unsigned char *iptr = mbrec.flagged[0] + ipol*nChan;
1485        for (int ichan = 0; ichan < nChan; ichan++) {
1486          *(iptr++) = 0;
1487        }
1488      }
1489    }
1490  }
1491
1492
1493  // Read cross-polarization data.
1494  if (cGetXPol) {
1495    int anynul;
1496    for (int j = 0; j < 2; j++) {
1497      mbrec.xcalfctr[0][j] = 0.0f;
1498    }
1499    if (cData[XCALFCTR].colnum > 0) {
1500      readData(XCALFCTR, cRow, mbrec.xcalfctr);
1501    }
1502
1503    blc[0] = 1;
1504    trc[0] = 2;
1505    blc[1] = std::min(startChan, endChan);
1506    trc[1] = std::max(startChan, endChan);
1507    blc[2] = cRow;
1508    trc[2] = cRow;
1509
1510    int  nAxis = 2;
1511    long nAxes[] = {2, nChan};
1512
1513    if (fits_read_subset_flt(cSDptr, cData[XPOLDATA].colnum, nAxis, nAxes,
1514        blc, trc, inc, 0, mbrec.xpol[0], &anynul, &cStatus)) {
1515      reportError();
1516      delete [] blc;
1517      delete [] trc;
1518      delete [] inc;
1519      return 1;
1520    }
1521
1522    if (endChan < startChan) {
1523      // Invert the cross-polarization spectrum.
1524      float *iptr = mbrec.xpol[0];
1525      float *jptr = iptr + nChan - 2;
1526      for (int ichan = 0; ichan < nChan/2; ichan++) {
1527        float tmp = *iptr;
1528        *iptr = *jptr;
1529        *jptr = tmp;
1530
1531        tmp = *(iptr+1);
1532        *(iptr+1) = *(jptr+1);
1533        *(jptr+1) = tmp;
1534
1535        iptr += 2;
1536        jptr -= 2;
1537      }
1538    }
1539  }
1540
1541  delete [] blc;
1542  delete [] trc;
1543  delete [] inc;
1544
1545  if (cStatus) {
1546    reportError();
1547    return 1;
1548  }
1549
1550  mbrec.extraSysCal = cExtraSysCal;
1551  readData(REFBEAM,  cRow, &mbrec.refBeam);
1552  readData(TCAL,     cRow, &mbrec.tcal[0]);
1553  readData(TCALTIME, cRow,  mbrec.tcalTime);
1554  readData(AZIMUTH,  cRow, &mbrec.azimuth);
1555  readData(ELEVATIO, cRow, &mbrec.elevation);
1556  readData(PARANGLE, cRow, &mbrec.parAngle);
1557  readData(FOCUSAXI, cRow, &mbrec.focusAxi);
1558  readData(FOCUSTAN, cRow, &mbrec.focusTan);
1559  readData(FOCUSROT, cRow, &mbrec.focusRot);
1560  readData(TAMBIENT, cRow, &mbrec.temp);
1561  readData(PRESSURE, cRow, &mbrec.pressure);
1562  readData(HUMIDITY, cRow, &mbrec.humidity);
1563  readData(WINDSPEE, cRow, &mbrec.windSpeed);
1564  readData(WINDDIRE, cRow, &mbrec.windAz);
1565
1566  if (cALFA_BD) {
1567    // ALFA BDFITS stores zenith angle rather than elevation.
1568    mbrec.elevation = 90.0 - mbrec.elevation;
1569  }
1570
1571  mbrec.azimuth   *= D2R;
1572  mbrec.elevation *= D2R;
1573  mbrec.parAngle  *= D2R;
1574  mbrec.focusRot  *= D2R;
1575  mbrec.windAz    *= D2R;
1576
1577  if (cStatus) {
1578    reportError();
1579    return 1;
1580  }
1581
1582  return 0;
1583}
1584
1585
1586//------------------------------------------------------ SDFITSreader::alfaCal
1587
1588// Process ALFA calibration data.
1589
1590int SDFITSreader::alfaCal(
1591        short iBeam,
1592        short iPol)
1593{
1594  int  calOn;
1595  char chars[32];
1596  if (cALFA_BD) {
1597    readData("OBS_NAME", TSTRING, cRow, chars);
1598  } else {
1599    readData("SCANTYPE", TSTRING, cRow, chars);
1600  }
1601
1602  if (strcmp(chars, "ON") == 0) {
1603    calOn = 1;
1604  } else if (strcmp(chars, "OFF") == 0) {
1605    calOn = 0;
1606  } else {
1607    return 1;
1608  }
1609
1610  // Read cal data.
1611  long *blc = new long[cNAxis+1];
1612  long *trc = new long[cNAxis+1];
1613  long *inc = new long[cNAxis+1];
1614  for (int iaxis = 0; iaxis <= cNAxis; iaxis++) {
1615    blc[iaxis] = 1;
1616    trc[iaxis] = 1;
1617    inc[iaxis] = 1;
1618  }
1619
1620  // User channel selection.
1621  int startChan = cStartChan[0];
1622  int endChan   = cEndChan[0];
1623
1624  blc[cNAxis] = cRow;
1625  trc[cNAxis] = cRow;
1626  blc[cReqax[0]] = std::min(startChan, endChan);
1627  trc[cReqax[0]] = std::max(startChan, endChan);
1628  blc[cReqax[1]] = 1;
1629  trc[cReqax[1]] = 1;
1630
1631  float spectrum[endChan];
1632  int anynul;
1633  if (fits_read_subset_flt(cSDptr, cData[DATA].colnum, cNAxis, cNAxes,
1634      blc, trc, inc, 0, spectrum, &anynul, &cStatus)) {
1635    reportError();
1636    delete [] blc;
1637    delete [] trc;
1638    delete [] inc;
1639    return 1;
1640  }
1641
1642  // Average the spectrum.
1643  float mean = 1e9f;
1644  for (int k = 0; k < 2; k++) {
1645    float discrim = 2.0f * mean;
1646
1647    int nChan = 0;
1648    float sum = 0.0f;
1649
1650    float *chanN = spectrum + abs(endChan - startChan) + 1;
1651    for (float *chan = spectrum; chan < chanN; chan++) {
1652      // Simple discriminant that eliminates strong radar interference.
1653      if (*chan < discrim) {
1654        nChan++;
1655        sum += *chan;
1656      }
1657    }
1658
1659    mean = sum / nChan;
1660  }
1661
1662  if (calOn) {
1663    cALFAcalOn[iBeam][iPol]  += mean;
1664  } else {
1665    cALFAcalOff[iBeam][iPol] += mean;
1666  }
1667
1668  if (cALFAcalOn[iBeam][iPol] != 0.0f &&
1669      cALFAcalOff[iBeam][iPol] != 0.0f) {
1670    // Tcal should come from the TCAL table, it varies weakly with beam,
1671    // polarization, and frequency.  However, TCAL is not written properly.
1672    float Tcal = 12.0f;
1673    cALFAcal[iBeam][iPol] = Tcal / (cALFAcalOn[iBeam][iPol] -
1674                                    cALFAcalOff[iBeam][iPol]);
1675
1676    // Scale from K to Jy; the gain also varies weakly with beam,
1677    // polarization, frequency, and zenith angle.
1678    float fluxCal = 10.0f;
1679    cALFAcal[iBeam][iPol] /= fluxCal;
1680
1681    cALFAcalOn[iBeam][iPol]  = 0.0f;
1682    cALFAcalOff[iBeam][iPol] = 0.0f;
1683  }
1684
1685  return 0;
1686}
1687
1688
1689//-------------------------------------------------- SDFITSreader::reportError
1690
1691// Print the error message corresponding to the input status value and all the
1692// messages on the CFITSIO error stack to stderr.
1693
1694void SDFITSreader::reportError()
1695{
1696  fits_report_error(stderr, cStatus);
1697}
1698
1699//-------------------------------------------------------- SDFITSreader::close
1700
1701// Close the SDFITS file.
1702
1703void SDFITSreader::close()
1704{
1705  if (cSDptr) {
1706    int status = 0;
1707    fits_close_file(cSDptr, &status);
1708    cSDptr = 0;
1709
1710    if (cBeams)     delete [] cBeams;
1711    if (cIFs)       delete [] cIFs;
1712    if (cStartChan) delete [] cStartChan;
1713    if (cEndChan)   delete [] cEndChan;
1714    if (cRefChan)   delete [] cRefChan;
1715  }
1716}
1717
1718//----------------------------------------------------- SDFITSreader::findData
1719
1720// Locate a data item in the SDFITS file.
1721
1722void SDFITSreader::findData(
1723        int  iData,
1724        char *name,
1725        int  type)
1726{
1727  cData[iData].name = name;
1728  cData[iData].type = type;
1729
1730  int colnum;
1731  findCol(name, &colnum);
1732  cData[iData].colnum = colnum;
1733
1734  // Determine the number of data elements.
1735  if (colnum > 0) {
1736    int  coltype;
1737    long nelem, width;
1738    fits_get_coltype(cSDptr, colnum, &coltype, &nelem, &width, &cStatus);
1739
1740    // Look for a TDIMnnn keyword or column.
1741    char tdim[8];
1742    sprintf(tdim, "TDIM%d", colnum);
1743    findCol(tdim, &cData[iData].tdimcol);
1744
1745    if (coltype < 0) {
1746      // CFITSIO returns coltype < 0 for variable length arrays.
1747      cData[iData].coltype = -coltype;
1748      cData[iData].nelem   = -nelem;
1749
1750    } else {
1751      cData[iData].coltype = coltype;
1752
1753      // Is there a TDIMnnn column?
1754      if (cData[iData].tdimcol > 0) {
1755        // Yes, dimensions of the fixed-length array could still vary.
1756        cData[iData].nelem = -nelem;
1757      } else {
1758        cData[iData].nelem =  nelem;
1759      }
1760    }
1761
1762  } else if (colnum == 0) {
1763    // Keyword.
1764    cData[iData].coltype =  0;
1765    cData[iData].nelem   =  1;
1766    cData[iData].tdimcol = -1;
1767  }
1768}
1769
1770//------------------------------------------------------ SDFITSreader::readDim
1771
1772// Determine the dimensions of an array in the SDFITS file.
1773
1774int SDFITSreader::readDim(
1775        int  iData,
1776        long iRow,
1777        int *naxis,
1778        long naxes[])
1779{
1780  int colnum = cData[iData].colnum;
1781  if (colnum <= 0) {
1782    return 1;
1783  }
1784
1785  int maxdim = *naxis;
1786  if (cData[iData].tdimcol < 0) {
1787    // No TDIMnnn column for this array.
1788    if (cData[iData].nelem < 0) {
1789      // Variable length array; read the array descriptor.
1790      *naxis = 1;
1791      long dummy;
1792      if (fits_read_descript(cSDptr, colnum, iRow, naxes, &dummy, &cStatus)) {
1793        return 1;
1794      }
1795
1796    } else {
1797      // Read the repeat count from TFORMnnn.
1798      if (fits_read_tdim(cSDptr, colnum, maxdim, naxis, naxes, &cStatus)) {
1799        return 1;
1800      }
1801    }
1802
1803  } else {
1804    // Read the TDIMnnn value from the header or table.
1805    char tdim[8], tdimval[64];
1806    sprintf(tdim, "TDIM%d", colnum);
1807    readData(tdim, TSTRING, iRow, tdimval);
1808
1809    // fits_decode_tdim() checks that the TDIMnnn value is within the length
1810    // of the array in the specified column number but unfortunately doesn't
1811    // recognize variable-length arrays.  Hence we must decode it here.
1812    char *tp = tdimval;
1813    if (*tp != '(') return 1;
1814
1815    tp++;
1816    *naxis = 0;
1817    for (size_t j = 1; j < strlen(tdimval); j++) {
1818      if (tdimval[j] == ',' || tdimval[j] == ')') {
1819        sscanf(tp, "%ld", naxes + (*naxis)++);
1820        if (tdimval[j] == ')') break;
1821        tp = tdimval + j + 1;
1822      }
1823    }
1824  }
1825
1826  return 0;
1827}
1828
1829//----------------------------------------------------- SDFITSreader::readParm
1830
1831// Read a parameter value from the SDFITS file.
1832
1833int SDFITSreader::readParm(
1834        char *name,
1835        int  type,
1836        void *value)
1837{
1838  return readData(name, type, 1, value);
1839}
1840
1841//----------------------------------------------------- SDFITSreader::readData
1842
1843// Read a data value from the SDFITS file.
1844
1845int SDFITSreader::readData(
1846        char *name,
1847        int  type,
1848        long iRow,
1849        void *value)
1850{
1851  int colnum;
1852  findCol(name, &colnum);
1853
1854  if (colnum > 0) {
1855    // Read the first value from the specified row of the table.
1856    int  coltype;
1857    long nelem, width;
1858    fits_get_coltype(cSDptr, colnum, &coltype, &nelem, &width, &cStatus);
1859
1860    int anynul;
1861    if (type == TSTRING) {
1862      if (nelem) {
1863        fits_read_col(cSDptr, type, colnum, iRow, 1, 1, 0, &value, &anynul,
1864                      &cStatus);
1865      } else {
1866        strcpy((char *)value, "");
1867      }
1868
1869    } else {
1870      if (nelem) {
1871        fits_read_col(cSDptr, type, colnum, iRow, 1, 1, 0, value, &anynul,
1872                      &cStatus);
1873      } else {
1874        if (type == TSHORT) {
1875          *((short *)value) = 0;
1876        } else if (type == TINT) {
1877          *((int *)value) = 0;
1878        } else if (type == TFLOAT) {
1879          *((float *)value) = 0.0f;
1880        } else if (type == TDOUBLE) {
1881          *((double *)value) = 0.0;
1882        }
1883      }
1884    }
1885
1886  } else if (colnum == 0) {
1887    // Read keyword value.
1888    fits_read_key(cSDptr, type, name, value, 0, &cStatus);
1889
1890  } else {
1891    // Not present.
1892    if (type == TSTRING) {
1893      strcpy((char *)value, "");
1894    } else if (type == TSHORT) {
1895      *((short *)value) = 0;
1896    } else if (type == TINT) {
1897      *((int *)value) = 0;
1898    } else if (type == TFLOAT) {
1899      *((float *)value) = 0.0f;
1900    } else if (type == TDOUBLE) {
1901      *((double *)value) = 0.0;
1902    }
1903  }
1904
1905  return colnum < 0;
1906}
1907
1908//----------------------------------------------------- SDFITSreader::readData
1909
1910// Read data from the SDFITS file.
1911
1912int SDFITSreader::readData(
1913        int  iData,
1914        long iRow,
1915        void *value)
1916{
1917  char *name  = cData[iData].name;
1918  int  type   = cData[iData].type;
1919  int  colnum = cData[iData].colnum;
1920  long nelem  = cData[iData].nelem;
1921
1922  if (colnum > 0) {
1923    // Read the required number of values from the specified row of the table.
1924    int anynul;
1925    if (type == TSTRING) {
1926      if (nelem) {
1927        fits_read_col(cSDptr, type, colnum, iRow, 1, 1, 0, &value, &anynul,
1928                      &cStatus);
1929      } else {
1930        strcpy((char *)value, "");
1931      }
1932
1933    } else {
1934      if (nelem) {
1935        fits_read_col(cSDptr, type, colnum, iRow, 1, abs(nelem), 0, value,
1936                      &anynul, &cStatus);
1937      } else {
1938        if (type == TSHORT) {
1939          *((short *)value) = 0;
1940        } else if (type == TINT) {
1941          *((int *)value) = 0;
1942        } else if (type == TFLOAT) {
1943          *((float *)value) = 0.0f;
1944        } else if (type == TDOUBLE) {
1945          *((double *)value) = 0.0;
1946        }
1947      }
1948    }
1949
1950  } else if (colnum == 0) {
1951    // Read keyword value.
1952    fits_read_key(cSDptr, type, name, value, 0, &cStatus);
1953
1954  } else {
1955    // Not present.
1956    if (type == TSTRING) {
1957      strcpy((char *)value, "");
1958    } else if (type == TSHORT) {
1959      *((short *)value) = 0;
1960    } else if (type == TINT) {
1961      *((int *)value) = 0;
1962    } else if (type == TFLOAT) {
1963      *((float *)value) = 0.0f;
1964    } else if (type == TDOUBLE) {
1965      *((double *)value) = 0.0;
1966    }
1967  }
1968
1969  return colnum < 0;
1970}
1971
1972//------------------------------------------------------ SDFITSreader::findCol
1973
1974// Locate a parameter in the SDFITS file.
1975
1976void SDFITSreader::findCol(
1977        char *name,
1978        int *colnum)
1979{
1980  *colnum = 0;
1981  int status = 0;
1982  fits_get_colnum(cSDptr, CASESEN, name, colnum, &status);
1983
1984  if (status) {
1985    // Not a real column - maybe it's virtual.
1986    char card[81];
1987
1988    status = 0;
1989    fits_read_card(cSDptr, name, card, &status);
1990    if (status) {
1991      // Not virtual either.
1992      *colnum = -1;
1993    }
1994
1995    // Clear error messages.
1996    fits_clear_errmsg();
1997  }
1998}
Note: See TracBrowser for help on using the repository browser.