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

Last change on this file since 1737 was 1737, checked in by Malte Marquarding, 14 years ago

gcc-4.4 fix to include cstring

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