| 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 | 
 | 
|---|
| 51 | class 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.
 | 
|---|
| 64 | const double PI  = 3.141592653589793238462643;
 | 
|---|
| 65 | 
 | 
|---|
| 66 | // Factor to convert radians to degrees.
 | 
|---|
| 67 | const double D2R = PI / 180.0;
 | 
|---|
| 68 | 
 | 
|---|
| 69 | //---------------------------------------------------- SDFITSreader::(statics)
 | 
|---|
| 70 | 
 | 
|---|
| 71 | int SDFITSreader::sInit  = 1;
 | 
|---|
| 72 | int SDFITSreader::sReset = 0;
 | 
|---|
| 73 | int (*SDFITSreader::sALFAcalNon)[2]   = (int (*)[2])(new float[16]);
 | 
|---|
| 74 | int (*SDFITSreader::sALFAcalNoff)[2]  = (int (*)[2])(new float[16]);
 | 
|---|
| 75 | float (*SDFITSreader::sALFAcalOn)[2]  = (float (*)[2])(new float[16]);
 | 
|---|
| 76 | float (*SDFITSreader::sALFAcalOff)[2] = (float (*)[2])(new float[16]);
 | 
|---|
| 77 | float (*SDFITSreader::sALFAcal)[2]    = (float (*)[2])(new float[16]);
 | 
|---|
| 78 | 
 | 
|---|
| 79 | //------------------------------------------------- SDFITSreader::SDFITSreader
 | 
|---|
| 80 | 
 | 
|---|
| 81 | SDFITSreader::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 | 
 | 
|---|
| 106 | SDFITSreader::~SDFITSreader()
 | 
|---|
| 107 | {
 | 
|---|
| 108 |   close();
 | 
|---|
| 109 | 
 | 
|---|
| 110 |   delete [] cData;
 | 
|---|
| 111 | }
 | 
|---|
| 112 | 
 | 
|---|
| 113 | //--------------------------------------------------------- SDFITSreader::open
 | 
|---|
| 114 | 
 | 
|---|
| 115 | // Open an SDFITS file for reading.
 | 
|---|
| 116 | 
 | 
|---|
| 117 | int 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 | 
 | 
|---|
| 793 | int 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 | 
 | 
|---|
| 935 | int 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 | 
 | 
|---|
| 1010 | int 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 | 
 | 
|---|
| 1237 | cleanup:
 | 
|---|
| 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 | 
 | 
|---|
| 1256 | int 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 | 
 | 
|---|
| 1743 | void 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 | 
 | 
|---|
| 1763 | void 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 | 
 | 
|---|
| 1781 | void 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 | 
 | 
|---|
| 1835 | void 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 | 
 | 
|---|
| 1863 | int 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 | 
 | 
|---|
| 1922 | int 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 | 
 | 
|---|
| 1934 | int 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 | 
 | 
|---|
| 2001 | int 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 | 
 | 
|---|
| 2065 | int 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 | 
 | 
|---|
| 2100 | int 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 | 
 | 
|---|
| 2150 | int 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 | 
 | 
|---|
| 2274 | float 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 | 
 | 
|---|