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

Last change on this file since 1686 was 1635, checked in by Malte Marquarding, 15 years ago

Update from livedata CVS

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