source: trunk/external/atnf/PKSIO/MBFITSreader.cc@ 2793

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

Update from livedata CVS repository

File size: 55.7 KB
RevLine 
[1325]1//#---------------------------------------------------------------------------
2//# MBFITSreader.cc: ATNF single-dish RPFITS reader.
3//#---------------------------------------------------------------------------
[1720]4//# livedata - processing pipeline for single-dish, multibeam spectral data.
5//# Copyright (C) 2000-2009, Australia Telescope National Facility, CSIRO
[1325]6//#
[1720]7//# This file is part of livedata.
[1325]8//#
[1720]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
[1325]15//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
[1720]16//# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17//# more details.
[1325]18//#
[1720]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/>.
[1325]21//#
[1720]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
[1325]28//# AUSTRALIA
29//#
[1720]30//# http://www.atnf.csiro.au/computing/software/livedata.html
31//# $Id: MBFITSreader.cc,v 19.57 2009-10-30 06:34:36 cal103 Exp $
[1325]32//#---------------------------------------------------------------------------
33//# The MBFITSreader class reads single dish RPFITS files (such as Parkes
34//# Multibeam MBFITS files).
35//#
36//# Original: 2000/07/28 Mark Calabretta
37//#---------------------------------------------------------------------------
38
[1452]39#include <atnf/pks/pks_maths.h>
[1325]40#include <atnf/PKSIO/MBFITSreader.h>
[1452]41#include <atnf/PKSIO/MBrecord.h>
[1325]42
43#include <casa/math.h>
44#include <casa/iostream.h>
45#include <casa/stdio.h>
46#include <casa/stdlib.h>
47#include <casa/string.h>
48#include <unistd.h>
49
[1452]50#include <RPFITS.h>
51
[1325]52using namespace std;
53
54// Numerical constants.
55const double PI = 3.141592653589793238462643;
56const double TWOPI = 2.0 * PI;
[1452]57const double HALFPI = PI / 2.0;
[1427]58const double R2D = 180.0 / PI;
[1325]59
60//------------------------------------------------- MBFITSreader::MBFITSreader
61
62// Default constructor.
63
64MBFITSreader::MBFITSreader(
65 const int retry,
66 const int interpolate)
67{
68 cRetry = retry;
69 if (cRetry > 10) {
70 cRetry = 10;
71 }
72
73 cInterp = interpolate;
74 if (cInterp < 0 || cInterp > 2) {
75 cInterp = 1;
76 }
77
78 // Initialize pointers.
79 cBeams = 0x0;
80 cIFs = 0x0;
81 cNChan = 0x0;
82 cNPol = 0x0;
83 cHaveXPol = 0x0;
84 cStartChan = 0x0;
85 cEndChan = 0x0;
86 cRefChan = 0x0;
87
88 cVis = 0x0;
89 cWgt = 0x0;
90
91 cBeamSel = 0x0;
92 cIFSel = 0x0;
93 cChanOff = 0x0;
94 cXpolOff = 0x0;
95 cBuffer = 0x0;
96 cPosUTC = 0x0;
97
98 cMBopen = 0;
[1452]99
100 // Tell RPFITSIN not to report errors directly.
101 iostat_.errlun = -1;
102
103 // By default, messages are written to stderr.
104 initMsg();
[1325]105}
106
107//------------------------------------------------ MBFITSreader::~MBFITSreader
108
109// Destructor.
110
111MBFITSreader::~MBFITSreader()
112{
113 close();
114}
115
116//--------------------------------------------------------- MBFITSreader::open
117
118// Open the RPFITS file for reading.
119
120int MBFITSreader::open(
121 char *rpname,
122 int &nBeam,
123 int* &beams,
124 int &nIF,
125 int* &IFs,
126 int* &nChan,
127 int* &nPol,
128 int* &haveXPol,
129 int &haveBase,
130 int &haveSpectra,
131 int &extraSysCal)
132{
[1452]133 // Clear the message stack.
134 clearMsg();
135
[1325]136 if (cMBopen) {
137 close();
138 }
139
140 strcpy(names_.file, rpname);
141
142 // Open the RPFITS file.
143 int jstat = -3;
[1452]144 if (rpfitsin(jstat)) {
[1635]145 sprintf(cMsg, "ERROR: Failed to open MBFITS file\n %s", rpname);
[1452]146 logMsg(cMsg);
[1325]147 return 1;
148 }
149
150 cMBopen = 1;
151
152 // Tell RPFITSIN that we want the OBSTYPE card.
153 int j;
154 param_.ncard = 1;
155 for (j = 0; j < 80; j++) {
156 names_.card[j] = ' ';
157 }
158 strncpy(names_.card, "OBSTYPE", 7);
159
160 // Read the first header.
161 jstat = -1;
[1452]162 if (rpfitsin(jstat)) {
[1635]163 sprintf(cMsg, "ERROR: Failed to read MBFITS header in file\n"
[1452]164 " %s", rpname);
165 logMsg(cMsg);
[1325]166 close();
167 return 1;
168 }
169
170 // Mopra data has some peculiarities.
171 cMopra = strncmp(names_.instrument, "ATMOPRA", 7) == 0;
172
[1372]173 // Non-ATNF data may not store the position in (u,v,w).
174 if (strncmp(names_.sta, "tid", 3) == 0) {
[1452]175 sprintf(cMsg, "WARNING: Found Tidbinbilla data");
[1372]176 cSUpos = 1;
177 } else if (strncmp(names_.sta, "HOB", 3) == 0) {
[1452]178 sprintf(cMsg, "WARNING: Found Hobart data");
[1372]179 cSUpos = 1;
180 } else if (strncmp(names_.sta, "CED", 3) == 0) {
[1452]181 sprintf(cMsg, "WARNING: Found Ceduna data");
[1372]182 cSUpos = 1;
183 } else {
184 cSUpos = 0;
185 }
186
187 if (cSUpos) {
[1452]188 strcat(cMsg, ", using telescope position\n from SU table.");
189 logMsg(cMsg);
[1325]190 cInterp = 0;
191 }
192
[1452]193 // Mean scan rate (for timestamp repairs).
194 cNRate = 0;
195 cAvRate[0] = 0.0;
196 cAvRate[1] = 0.0;
197 cCode5 = 0;
[1325]198
[1452]199
[1325]200 // Find the maximum beam number.
201 cNBeam = 0;
202 for (int iBeam = 0; iBeam < anten_.nant; iBeam++) {
203 if (anten_.ant_num[iBeam] > cNBeam) {
204 cNBeam = anten_.ant_num[iBeam];
205 }
206 }
207
208 if (cNBeam <= 0) {
[1635]209 logMsg("ERROR: Couldn't determine number of beams.");
[1325]210 close();
211 return 1;
212 }
213
214 // Construct the beam mask.
215 cBeams = new int[cNBeam];
216 for (int iBeam = 0; iBeam < cNBeam; iBeam++) {
217 cBeams[iBeam] = 0;
218 }
219
220 // ...beams present in the data.
221 for (int iBeam = 0; iBeam < anten_.nant; iBeam++) {
[1452]222 // Guard against dubious beam numbers, e.g. zeroes in
223 // 1999-09-29_1632_024848p14_071b.hpf and the four scans following.
224 // Note that the actual beam number is decoded from the 'baseline' random
225 // parameter for each spectrum and is only used for beam selection.
226 int beamNo = anten_.ant_num[iBeam];
227 if (beamNo != iBeam+1) {
228 char sta[8];
229 strncpy(sta, names_.sta+(8*iBeam), 8);
230 char *cp = sta + 7;
231 while (*cp == ' ') *(cp--) = '\0';
232
233 sprintf(cMsg,
234 "WARNING: RPFITSIN returned beam number %2d for AN table\n"
235 " entry %2d with name '%.8s'", beamNo, iBeam+1, sta);
236
237 char text[8];
238 sprintf(text, "MB%2.2d", iBeam+1);
239 cp = cMsg + strlen(cMsg);
240 if (strncmp(sta, text, 8) == 0) {
241 beamNo = iBeam + 1;
242 sprintf(cp, "; using beam number %2d.", beamNo);
243 } else {
244 sprintf(cp, ".");
245 }
246
247 logMsg(cMsg);
248 }
249
250 if (0 < beamNo && beamNo <= cNBeam) {
251 cBeams[beamNo-1] = 1;
252 }
[1325]253 }
254
255 // Passing back the address of the array allows PKSFITSreader::select() to
256 // modify its elements directly.
257 nBeam = cNBeam;
258 beams = cBeams;
259
260
261 // Number of IFs.
262 cNIF = if_.n_if;
263 cIFs = new int[cNIF];
264 for (int iIF = 0; iIF < cNIF; iIF++) {
265 cIFs[iIF] = 1;
266 }
267
268 // Passing back the address of the array allows PKSFITSreader::select() to
269 // modify its elements directly.
270 nIF = cNIF;
271 IFs = cIFs;
272
273
274 // Number of channels and polarizations.
275 cNChan = new int[cNIF];
276 cNPol = new int[cNIF];
277 cHaveXPol = new int[cNIF];
278 cGetXPol = 0;
279
280 int maxProd = 0;
281 for (int iIF = 0; iIF < cNIF; iIF++) {
282 cNChan[iIF] = if_.if_nfreq[iIF];
283 cNPol[iIF] = if_.if_nstok[iIF];
284 cNChan[iIF] -= cNChan[iIF]%2;
285
286 // Do we have cross-polarization data?
287 if ((cHaveXPol[iIF] = cNPol[iIF] > 2)) {
288 // Cross-polarization data is handled separately.
289 cNPol[iIF] = 2;
290
291 // Default is to get it if we have it.
292 cGetXPol = 1;
293 }
294
295 // Maximum number of spectral products in any IF.
296 int nProd = if_.if_nfreq[iIF] * if_.if_nstok[iIF];
297 if (maxProd < nProd) maxProd = nProd;
298 }
299
300 // Allocate memory for RPFITSIN subroutine arguments.
301 if (cVis) delete [] cVis;
302 if (cWgt) delete [] cWgt;
303 cVis = new float[2*maxProd];
304 cWgt = new float[maxProd];
305
306 nChan = cNChan;
307 nPol = cNPol;
308 haveXPol = cHaveXPol;
309
310
311 // Default channel range selection.
312 cStartChan = new int[cNIF];
313 cEndChan = new int[cNIF];
314 cRefChan = new int[cNIF];
315
316 for (int iIF = 0; iIF < cNIF; iIF++) {
317 cStartChan[iIF] = 1;
318 cEndChan[iIF] = cNChan[iIF];
319 cRefChan[iIF] = cNChan[iIF]/2 + 1;
320 }
321
322 cGetSpectra = 1;
323
324
325 // No baseline parameters in MBFITS.
326 haveBase = 0;
327
328 // Always have spectra in MBFITS.
329 haveSpectra = cHaveSpectra = 1;
330
331
332 // Integration cycle time (s).
333 cIntTime = param_.intime;
334
335 // Can't deduce binning mode till later.
336 cNBin = 0;
337
338
339 // Read the first syscal record.
340 if (rpget(1, cEOS)) {
[1635]341 logMsg("ERROR: Failed to read first syscal record.");
[1325]342 close();
343 return 1;
344 }
345
346 // Additional information for Parkes Multibeam data?
347 extraSysCal = (sc_.sc_ant > anten_.nant);
348
349
350 cFirst = 1;
351 cEOF = 0;
352 cFlushing = 0;
353
354 return 0;
355}
356
357//---------------------------------------------------- MBFITSreader::getHeader
358
359// Get parameters describing the data.
360
361int MBFITSreader::getHeader(
362 char observer[32],
363 char project[32],
364 char telescope[32],
365 double antPos[3],
366 char obsType[32],
[1399]367 char bunit[32],
[1325]368 float &equinox,
369 char radecsys[32],
370 char dopplerFrame[32],
371 char datobs[32],
372 double &utc,
373 double &refFreq,
374 double &bandwidth)
375{
376 if (!cMBopen) {
[1635]377 logMsg("ERROR: An MBFITS file has not been opened.");
[1325]378 return 1;
379 }
380
381 sprintf(observer, "%-16.16s", names_.rp_observer);
382 sprintf(project, "%-16.16s", names_.object);
383 sprintf(telescope, "%-16.16s", names_.instrument);
384
385 // Observatory coordinates (ITRF), in m.
386 antPos[0] = doubles_.x[0];
387 antPos[1] = doubles_.y[0];
388 antPos[2] = doubles_.z[0];
389
390 // This is the only sure way to identify the telescope, maybe.
391 if (strncmp(names_.sta, "MB0", 3) == 0) {
392 // Parkes Multibeam.
393 sprintf(telescope, "%-16.16s", "ATPKSMB");
394 antPos[0] = -4554232.087;
395 antPos[1] = 2816759.046;
396 antPos[2] = -3454035.950;
[1452]397
[1325]398 } else if (strncmp(names_.sta, "HOH", 3) == 0) {
399 // Parkes HOH receiver.
400 sprintf(telescope, "%-16.16s", "ATPKSHOH");
401 antPos[0] = -4554232.087;
402 antPos[1] = 2816759.046;
403 antPos[2] = -3454035.950;
[1452]404
[1325]405 } else if (strncmp(names_.sta, "CA0", 3) == 0) {
406 // An ATCA antenna, use the array centre position.
407 sprintf(telescope, "%-16.16s", "ATCA");
408 antPos[0] = -4750915.837;
409 antPos[1] = 2792906.182;
410 antPos[2] = -3200483.747;
[1452]411
412 // ATCA-104. Updated position at epoch 2007/06/24 from Chris Phillips.
413 // antPos[0] = -4751640.182; // ± 0.008
414 // antPos[1] = 2791700.322; // ± 0.006
415 // antPos[2] = -3200490.668; // ± 0.007
416 //
[1325]417 } else if (strncmp(names_.sta, "MOP", 3) == 0) {
[1452]418 // Mopra. Updated position at epoch 2007/06/24 from Chris Phillips.
[1325]419 sprintf(telescope, "%-16.16s", "ATMOPRA");
[1452]420 antPos[0] = -4682769.444; // ± 0.009
421 antPos[1] = 2802618.963; // ± 0.006
422 antPos[2] = -3291758.864; // ± 0.008
423
[1325]424 } else if (strncmp(names_.sta, "HOB", 3) == 0) {
425 // Hobart.
426 sprintf(telescope, "%-16.16s", "HOBART");
427 antPos[0] = -3950236.735;
428 antPos[1] = 2522347.567;
429 antPos[2] = -4311562.569;
[1452]430
[1325]431 } else if (strncmp(names_.sta, "CED", 3) == 0) {
[1452]432 // Ceduna. Updated position at epoch 2007/06/24 from Chris Phillips.
[1325]433 sprintf(telescope, "%-16.16s", "CEDUNA");
[1452]434 antPos[0] = -3753443.168; // ± 0.017
435 antPos[1] = 3912709.794; // ± 0.017
436 antPos[2] = -3348067.060; // ± 0.016
437
[1325]438 } else if (strncmp(names_.sta, "tid", 3) == 0) {
439 // DSS.
440 sprintf(telescope, "%-16.16s", "DSS-43");
441 antPos[0] = -4460894.727;
442 antPos[1] = 2682361.530;
443 antPos[2] = -3674748.424;
444 }
445
446 // Observation type.
447 int j;
448 for (j = 0; j < 31; j++) {
449 obsType[j] = names_.card[11+j];
450 if (obsType[j] == '\'') break;
451 }
452 obsType[j] = '\0';
453
[1399]454 // Brightness unit.
455 sprintf(bunit, "%-16.16s", names_.bunit);
456 if (strcmp(bunit, "JY") == 0) {
457 bunit[1] = 'y';
458 } else if (strcmp(bunit, "JY/BEAM") == 0) {
459 strcpy(bunit, "Jy/beam");
460 }
461
[1325]462 // Coordinate frames.
463 equinox = 2000.0f;
464 strcpy(radecsys, "FK5");
465 strcpy(dopplerFrame, "TOPOCENT");
466
467 // Time at start of observation.
468 sprintf(datobs, "%-10.10s", names_.datobs);
469 utc = cUTC;
470
471 // Spectral parameters.
472 refFreq = doubles_.if_freq[0];
473 bandwidth = doubles_.if_bw[0];
474
475 return 0;
476}
477
478//-------------------------------------------------- MBFITSreader::getFreqInfo
479
480// Get frequency parameters for each IF.
481
482int MBFITSreader::getFreqInfo(
483 int &nIF,
484 double* &startFreq,
485 double* &endFreq)
486{
487 // This is RPFITS - can't do it!
488 return 1;
489}
490
491//---------------------------------------------------- MBFITSreader::findRange
492
493// Find the range of the data selected in time and position.
494
495int MBFITSreader::findRange(
496 int &nRow,
497 int &nSel,
498 char dateSpan[2][32],
499 double utcSpan[2],
500 double* &positions)
501{
502 // This is RPFITS - can't do it!
503 return 1;
504}
505
506//--------------------------------------------------------- MBFITSreader::read
507
[1452]508// Read the next data record (if you're feeling lucky).
[1325]509
510int MBFITSreader::read(
[1452]511 MBrecord &MBrec)
[1325]512{
513 int beamNo = -1;
[1452]514 int haveData, pCode = 0, status;
515 double raRate = 0.0, decRate = 0.0, paRate = 0.0;
516 MBrecord *iMBuff = 0x0;
[1325]517
518 if (!cMBopen) {
[1635]519 logMsg("ERROR: An MBFITS file has not been opened.");
[1325]520 return 1;
521 }
522
[1452]523 // Positions recorded in the input records usually do not coincide with the
524 // midpoint of the integration and hence the input must be buffered so that
525 // true positions may be interpolated.
[1325]526 //
527 // On the first call nBeamSel buffers of length nBin, are allocated and
528 // filled, where nBin is the number of time bins.
529 //
530 // The input records for binned, single beam data with multiple simultaneous
531 // IFs are ordered by IF within each integration rather than by bin number
532 // and hence are not in time order. No multibeam data exists with
533 // nBin > 1 but the likelihood that the input records would be in beam/IF
534 // order and the requirement that output records be in time order would
535 // force an elaborate double-buffering system and we do not support it.
536 //
537 // Once all buffers are filled, the next record for each beam pertains to
538 // the next integration and should contain new position information allowing
539 // the proper position for each spectrum in the buffer to be interpolated.
540 // The buffers are then flushed in time order. For single beam data there
541 // is only one buffer and reads from the MBFITS file are suspended while the
542 // flush is in progress. For multibeam data each buffer is of unit length
543 // so the flush completes immediately and the new record takes its place.
544
545 haveData = 0;
546 while (!haveData) {
547 int iBeamSel = -1, iIFSel = -1;
548
549 if (!cFlushing) {
550 if (cEOF) {
551 return -1;
552 }
553
554 // Read the next record.
[1452]555 pCode = 0;
[1325]556 if ((status = rpget(0, cEOS)) == -1) {
557 // EOF.
558 cEOF = 1;
559 cFlushing = 1;
560 cFlushBin = 0;
561 cFlushIF = 0;
562
563#ifdef PKSIO_DEBUG
[1452]564 fprintf(stderr, "\nEnd-of-file detected, flushing last cycle.\n");
[1325]565#endif
566
567 } else if (status) {
568 // IO error.
569 return 1;
570
571 } else {
572 if (cFirst) {
573 // First data; cBeamSel[] stores the buffer index for each beam.
574 cNBeamSel = 0;
575 cBeamSel = new int[cNBeam];
576
577 for (int iBeam = 0; iBeam < cNBeam; iBeam++) {
578 if (cBeams[iBeam]) {
579 // Buffer offset for this beam.
580 cBeamSel[iBeam] = cNBeamSel++;
581 } else {
582 // Signal that the beam is not selected.
583 cBeamSel[iBeam] = -1;
584 }
585 }
586
587 // Set up bookkeeping arrays for IFs.
588 cIFSel = new int[cNIF];
589 cChanOff = new int[cNIF];
590 cXpolOff = new int[cNIF];
591
592 int maxChan = 0;
593 int maxXpol = 0;
594
[1635]595 cSimulIF = 0;
[1325]596 for (int iIF = 0; iIF < cNIF; iIF++) {
597 if (cIFs[iIF]) {
598 // Buffer index for each IF within each simultaneous set.
599 cIFSel[iIF] = 0;
600
601 // Array offsets for each IF within each simultaneous set.
602 cChanOff[iIF] = 0;
603 cXpolOff[iIF] = 0;
604
605 // Look for earlier IFs in the same simultaneous set.
606 for (int jIF = 0; jIF < iIF; jIF++) {
607 if (!cIFs[jIF]) continue;
608
609 if (if_.if_simul[jIF] == if_.if_simul[iIF]) {
610 // Got one, increment indices.
611 cIFSel[iIF]++;
612
613 cChanOff[iIF] += cNChan[jIF] * cNPol[jIF];
614 if (cHaveXPol[jIF]) {
615 cXpolOff[iIF] += 2 * cNChan[jIF];
616 }
617 }
618 }
619
620 // Maximum number of selected IFs in any simultaneous set.
[1635]621 cSimulIF = max(cSimulIF, cIFSel[iIF]+1);
[1325]622
623 // Maximum memory required for any simultaneous set.
624 maxChan = max(maxChan, cChanOff[iIF] + cNChan[iIF]*cNPol[iIF]);
625 if (cHaveXPol[iIF]) {
626 maxXpol = max(maxXpol, cXpolOff[iIF] + 2*cNChan[iIF]);
627 }
628
629 } else {
630 // Signal that the IF is not selected.
631 cIFSel[iIF] = -1;
632 }
633 }
634
635 // Check for binning mode observations.
636 if (param_.intbase > 0.0f) {
637 cNBin = int((cIntTime / param_.intbase) + 0.5);
638
639 // intbase sometimes contains rubbish.
640 if (cNBin == 0) {
641 cNBin = 1;
642 }
643 } else {
644 cNBin = 1;
645 }
646
647 if (cNBin > 1 && cNBeamSel > 1) {
[1635]648 logMsg("ERROR: Cannot handle binning mode for multiple beams.\n"
649 " Select a single beam for input.");
[1325]650 close();
651 return 1;
652 }
653
[1452]654 // Allocate buffer data storage; the MBrecord constructor zeroes
[1325]655 // class members such as cycleNo that are tested in the first pass
656 // below.
657 int nBuff = cNBeamSel * cNBin;
[1452]658 cBuffer = new MBrecord[nBuff];
[1325]659
660 // Allocate memory for spectral arrays.
661 for (int ibuff = 0; ibuff < nBuff; ibuff++) {
[1635]662 cBuffer[ibuff].setNIFs(cSimulIF);
[1325]663 cBuffer[ibuff].allocate(0, maxChan, maxXpol);
[1452]664
665 // Signal that this IF in this buffer has been flushed.
[1635]666 for (int iIF = 0; iIF < cSimulIF; iIF++) {
[1452]667 cBuffer[ibuff].IFno[iIF] = 0;
668 }
[1325]669 }
670
671 cPosUTC = new double[cNBeamSel];
672
673 cFirst = 0;
674 cScanNo = 1;
675 cCycleNo = 0;
[1452]676 cPrevUTC = -1.0;
[1325]677 }
678
679 // Check for end-of-scan.
680 if (cEOS) {
681 cScanNo++;
682 cCycleNo = 0;
[1452]683 cPrevUTC = -1.0;
[1325]684 }
685
[1635]686 // Apply beam and IF selection before the change-of-day test to allow
687 // a single selected beam and IF to be handled in binning-mode.
688 beamNo = int(cBaseline / 256.0);
689 if (beamNo == 1) {
690 // Store the position of beam 1 for grid convergence corrections.
691 cRA0 = cU;
692 cDec0 = cV;
693 }
694 iBeamSel = cBeamSel[beamNo-1];
695 if (iBeamSel < 0) continue;
696
697 // Sanity check (mainly for MOPS).
698 if (cIFno > cNIF) continue;
699
[1720]700 // Apply IF selection; iIFSel == 0 for the first selected IF, == 1
701 // for the second, etc.
[1635]702 iIFSel = cIFSel[cIFno - 1];
703 if (iIFSel < 0) continue;
704
705
706 if (cNBin > 1) {
707 // Binning mode: correct the time.
708 cUTC += param_.intbase * (cBin - (cNBin + 1)/2.0);
709 }
710
[1325]711 // Check for change-of-day.
[1452]712 double cod = 0.0;
713 if ((cUTC + 86400.0) < (cPrevUTC + 600.0)) {
714 // cUTC should continue to increase past 86400 during a single scan.
715 // However, if the RPFITS file contains multiple scans that straddle
716 // midnight then cUTC can jump backwards from the end of one scan to
717 // the start of the next.
718#ifdef PKSIO_DEBUG
[1635]719 fprintf(stderr, "Change-of-day on cUTC: %.1f -> %.1f\n",
[1452]720 cPrevUTC, cUTC);
721#endif
722 // Can't change the recorded value of cUTC directly (without also
723 // changing dateobs) so change-of-day must be recorded separately as
724 // an offset to be applied when comparing integration timestamps.
725 cod = 86400.0;
[1720]726 }
[1452]727
[1720]728 if ((cUTC+cod) < cPrevUTC - 1.0) {
729 if (cBin == 1 && iIFSel) {
730 // Multiple-IF, binning-mode data is only partially time ordered.
731#ifdef PKSIO_DEBUG
732 fprintf(stderr, "New IF in multiple-IF, binning-mode data.\n");
733#endif
734 cCycleNo -= cNBin;
735 cPrevUTC = -1.0;
736
737 } else {
738 // All other data should be fully time ordered.
739 sprintf(cMsg,
740 "WARNING: Cycle %d:%03d-%03d, UTC went backwards from\n"
741 " %.1f to %.1f! Incrementing day number,\n"
742 " positions may be unreliable.", cScanNo, cCycleNo,
743 cCycleNo+1, cPrevUTC, cUTC);
744 logMsg(cMsg);
745 cUTC += 86400.0;
746 }
[1325]747 }
748
749 // New integration cycle?
[1452]750 if ((cUTC+cod) > cPrevUTC) {
[1325]751 cCycleNo++;
752 cPrevUTC = cUTC + 0.0001;
753 }
754
[1399]755 sprintf(cDateObs, "%-10.10s", names_.datobs);
[1452]756 cDateObs[10] = '\0';
[1399]757
[1325]758 // Compute buffer number.
759 iMBuff = cBuffer + iBeamSel;
760 if (cNBin > 1) iMBuff += cNBeamSel*(cBin-1);
761
762 if (cCycleNo < iMBuff->cycleNo) {
763 // Note that if the first beam and IF are not both selected cEOS
764 // will be cleared by rpget() when the next beam/IF is read.
765 cEOS = 1;
766 }
767
768 // Begin flush cycle?
[1452]769 if (cEOS || (iMBuff->nIF && (cUTC+cod) > (iMBuff->utc+0.0001))) {
[1325]770 cFlushing = 1;
771 cFlushBin = 0;
772 cFlushIF = 0;
773 }
774
775#ifdef PKSIO_DEBUG
[1452]776 char rel = '=';
777 double dt = utcDiff(cUTC, cW);
778 if (dt < 0.0) {
779 rel = '<';
780 } else if (dt > 0.0) {
781 rel = '>';
782 }
783
784 fprintf(stderr, "\n In:%4d%4d%3d%3d %.3f %c %.3f (%+.3fs) - "
785 "%sflushing\n", cScanNo, cCycleNo, beamNo, cIFno, cUTC, rel, cW, dt,
786 cFlushing ? "" : "not ");
787 if (cEOS) {
788 fprintf(stderr, "Start of new scan, flushing previous scan.\n");
789 }
[1325]790#endif
791 }
792 }
793
794
795 if (cFlushing) {
796 // Find the oldest integration to flush, noting that the last
797 // integration cycle may be incomplete.
798 beamNo = 0;
799 int cycleNo = 0;
800 for (; cFlushBin < cNBin; cFlushBin++) {
801 for (iBeamSel = 0; iBeamSel < cNBeamSel; iBeamSel++) {
802 iMBuff = cBuffer + iBeamSel + cNBeamSel*cFlushBin;
803
[1635]804 // iMBuff->nIF is decremented (below) and if zero signals that all
805 // IFs in an integration have been flushed.
[1325]806 if (iMBuff->nIF) {
807 if (cycleNo == 0 || iMBuff->cycleNo < cycleNo) {
808 beamNo = iMBuff->beamNo;
809 cycleNo = iMBuff->cycleNo;
810 }
811 }
812 }
813
814 if (beamNo) {
815 // Found an integration to flush.
816 break;
817 }
[1720]818
819 // Start with the first IF in the next bin.
820 cFlushIF = 0;
[1325]821 }
822
823 if (beamNo) {
824 iBeamSel = cBeamSel[beamNo-1];
825 iMBuff = cBuffer + iBeamSel + cNBeamSel*cFlushBin;
826
827 // Find the IF to flush.
[1635]828 for (; cFlushIF < cSimulIF; cFlushIF++) {
[1325]829 if (iMBuff->IFno[cFlushIF]) break;
830 }
831
832 } else {
833 // Flush complete.
834 cFlushing = 0;
835 if (cEOF) {
836 return -1;
837 }
838
839 // The last record read must have been the first of a new cycle.
840 beamNo = int(cBaseline / 256.0);
841 iBeamSel = cBeamSel[beamNo-1];
842
843 // Compute buffer number.
844 iMBuff = cBuffer + iBeamSel;
845 if (cNBin > 1) iMBuff += cNBeamSel*(cBin-1);
846 }
847 }
848
849
[1635]850 if (cInterp && cFlushing == 1) {
[1427]851 // Start of flush cycle, interpolate the beam position.
852 //
853 // The position is measured by the control system at a time returned by
854 // RPFITSIN as the 'w' visibility coordinate. The ra and dec, returned
855 // as the 'u' and 'v' visibility coordinates, must be interpolated to
856 // the integration time which RPFITSIN returns as 'cUTC', this usually
[1452]857 // being a second or two later. The interpolation method used here is
858 // based on the scan rate.
[1427]859 //
860 // "This" RA, Dec, and UTC refers to the position currently stored in
[1452]861 // the buffer marked for output (iMBuff). This position is interpolated
862 // to the midpoint of that integration using either
863 // a) the rate currently sitting in iMBuff, which was computed from
864 // the previous integration, otherwise
865 // b) from the position recorded in the "next" integration which is
866 // currently sitting in the RPFITS commons,
867 // so that the position timestamps straddle the midpoint of the
868 // integration and is thereby interpolated rather than extrapolated.
[1427]869 //
[1452]870 // At the end of a scan, or if the next position has not been updated
871 // or its timestamp does not advance sufficiently, the most recent
872 // determination of the scan rate will be used for extrapolation which
873 // is quantified by the "rate age" measured in seconds beyond the
874 // interval defined by the position timestamps.
[1325]875
[1452]876 // At this point, iMBuff contains cU, cV, cW, parAngle and focusRot
877 // stored from the previous call to rpget() for this beam (i.e. "this"),
878 // and also raRate, decRate and paRate computed from that integration
879 // and the previous one.
[1427]880 double thisRA = iMBuff->ra;
881 double thisDec = iMBuff->dec;
882 double thisUTC = cPosUTC[iBeamSel];
[1452]883 double thisPA = iMBuff->parAngle + iMBuff->focusRot;
[1325]884
[1452]885#ifdef PKSIO_DEBUG
886 fprintf(stderr, "This (%d) ra, dec, UTC: %9.4f %9.4f %10.3f %9.4f\n",
887 iMBuff->cycleNo, thisRA*R2D, thisDec*R2D, thisUTC, thisPA*R2D);
888#endif
889
[1427]890 if (cEOF || cEOS) {
[1452]891 // Use rates from the last cycle.
892 raRate = iMBuff->raRate;
893 decRate = iMBuff->decRate;
894 paRate = iMBuff->paRate;
895
[1427]896 } else {
[1452]897 if (cW == thisUTC) {
898 // The control system at Mopra typically does not update the
899 // positions between successive integration cycles at the end of a
900 // scan (nor are they flagged). In this case we use the previously
901 // computed rates, even if from the previous scan since these are
902 // likely to be a better guess than anything else.
903 raRate = iMBuff->raRate;
904 decRate = iMBuff->decRate;
905 paRate = iMBuff->paRate;
[1325]906
[1452]907 if (cU == thisRA && cV == thisDec) {
908 // Position and timestamp unchanged.
909 pCode = 1;
[1325]910
[1452]911 } else if (fabs(cU-thisRA) < 0.0001 && fabs(cV-thisDec) < 0.0001) {
912 // Allow small rounding errors (seen infrequently).
913 pCode = 1;
[1325]914
915 } else {
[1452]916 // (cU,cV) are probably rubbish (not yet seen in practice).
917 pCode = 2;
918 cU = thisRA;
919 cV = thisDec;
[1325]920 }
921
[1427]922#ifdef PKSIO_DEBUG
[1452]923 fprintf(stderr, "Next (%d) ra, dec, UTC: %9.4f %9.4f %10.3f "
924 "(0.000s)\n", cCycleNo, cU*R2D, cV*R2D, cW);
[1427]925#endif
926
[1452]927 } else {
928 double nextRA = cU;
929 double nextDec = cV;
[1325]930
[1452]931 // Check and, if necessary, repair the position timestamp,
932 // remembering that pCode refers to the NEXT cycle.
933 pCode = fixw(cDateObs, cCycleNo, beamNo, cAvRate, thisRA, thisDec,
934 thisUTC, nextRA, nextDec, cW);
935 if (pCode > 0) pCode += 3;
936 double nextUTC = cW;
[1325]937
[1452]938#ifdef PKSIO_DEBUG
939 fprintf(stderr, "Next (%d) ra, dec, UTC: %9.4f %9.4f %10.3f "
940 "(%+.3fs)\n", cCycleNo, nextRA*R2D, nextDec*R2D, nextUTC,
941 utcDiff(nextUTC, thisUTC));
942#endif
[1325]943
[1452]944 // Compute the scan rate for this beam.
945 double dUTC = utcDiff(nextUTC, thisUTC);
946 if ((0.0 < dUTC) && (dUTC < 600.0)) {
947 scanRate(cRA0, cDec0, thisRA, thisDec, nextRA, nextDec, dUTC,
948 raRate, decRate);
[1325]949
[1452]950 // Update the mean scan rate.
951 cAvRate[0] = (cAvRate[0]*cNRate + raRate) / (cNRate + 1);
952 cAvRate[1] = (cAvRate[1]*cNRate + decRate) / (cNRate + 1);
953 cNRate++;
954
955 // Rate of change of position angle.
956 if (sc_.sc_ant <= anten_.nant) {
957 paRate = 0.0;
958 } else {
959 int iOff = sc_.sc_q * (sc_.sc_ant - 1) - 1;
960 double nextPA = sc_.sc_cal[iOff + 4] + sc_.sc_cal[iOff + 7];
961 double paDiff = nextPA - thisPA;
962 if (paDiff > PI) {
963 paDiff -= TWOPI;
964 } else if (paDiff < -PI) {
965 paDiff += TWOPI;
966 }
967 paRate = paDiff / dUTC;
[1325]968 }
969
[1452]970 if (cInterp == 2) {
971 // Use the same interpolation scheme as the original pksmbfits
972 // client. This incorrectly assumed that (nextUTC - thisUTC) is
973 // equal to the integration time and interpolated by computing a
974 // weighted sum of the positions before and after the required
975 // time.
[1325]976
[1452]977 double utc = iMBuff->utc;
978 double tw1 = 1.0 - utcDiff(utc, thisUTC) / iMBuff->exposure;
979 double tw2 = 1.0 - utcDiff(nextUTC, utc) / iMBuff->exposure;
980 double gamma = (tw2 / (tw1 + tw2)) * dUTC / (utc - thisUTC);
981
982 // Guard against RA cycling through 24h in either direction.
983 if (fabs(nextRA - thisRA) > PI) {
984 if (nextRA < thisRA) {
985 nextRA += TWOPI;
986 } else {
987 nextRA -= TWOPI;
988 }
989 }
990
991 raRate = gamma * (nextRA - thisRA) / dUTC;
992 decRate = gamma * (nextDec - thisDec) / dUTC;
993 }
994
995 } else {
996 if (cCycleNo == 2 && fabs(utcDiff(cUTC,cW)) < 600.0) {
997 // thisUTC (i.e. cW for the first cycle) is rubbish, and
998 // probably the position as well (extremely rare in practice,
999 // e.g. 97-12-19_1029_235708-18_586e.hpf which actually has the
1000 // t/1000 scaling bug in the first cycle).
1001 iMBuff->pCode = 3;
1002 thisRA = cU;
1003 thisDec = cV;
1004 thisUTC = cW;
1005 raRate = 0.0;
1006 decRate = 0.0;
1007 paRate = 0.0;
1008
1009 } else {
1010 // cW is rubbish and probably (cU,cV), and possibly the
1011 // parallactic angle and everything else as well (rarely seen
1012 // in practice, e.g. 97-12-09_0743_235707-58_327c.hpf and
1013 // 97-09-01_0034_123717-42_242b.hpf, the latter with bad
1014 // parallactic angle).
1015 pCode = 3;
1016 cU = thisRA;
1017 cV = thisDec;
1018 cW = thisUTC;
1019 raRate = iMBuff->raRate;
1020 decRate = iMBuff->decRate;
1021 paRate = iMBuff->paRate;
1022 }
[1325]1023 }
[1452]1024 }
1025 }
[1325]1026
[1452]1027
1028 // Choose the closest rate determination.
1029 if (cCycleNo == 1) {
1030 // Scan containing a single integration.
1031 iMBuff->raRate = 0.0;
1032 iMBuff->decRate = 0.0;
1033 iMBuff->paRate = 0.0;
1034
1035 } else {
1036 double dUTC = iMBuff->utc - cPosUTC[iBeamSel];
1037
1038 if (dUTC >= 0.0) {
1039 // In HIPASS/ZOA, the position timestamp, which should always occur
1040 // on the whole second, normally precedes an integration midpoint
1041 // falling on the half-second. Consequently, positive ages are
1042 // always half-integral.
1043 dUTC = utcDiff(iMBuff->utc, cW);
1044 if (dUTC > 0.0) {
1045 iMBuff->rateAge = dUTC;
1046 } else {
1047 iMBuff->rateAge = 0.0f;
1048 }
1049
1050 iMBuff->raRate = raRate;
1051 iMBuff->decRate = decRate;
1052 iMBuff->paRate = paRate;
1053
[1325]1054 } else {
[1452]1055 // In HIPASS/ZOA, negative ages occur when the integration midpoint,
1056 // occurring on the whole second, precedes the position timestamp.
1057 // Thus negative ages are always an integral number of seconds.
1058 // They have only been seen to occur sporadically in the period
1059 // 1999/05/31 to 1999/11/01, e.g. 1999-07-26_1821_005410-74_007c.hpf
1060 //
1061 // In recent (2008/10/07) Mopra data, small negative ages (~10ms,
1062 // occasionally up to ~300ms) seem to be the norm, with both the
1063 // position timestamp and integration midpoint falling close to but
1064 // not on the integral second.
1065 if (cCycleNo == 2) {
1066 // We have to start with something!
1067 iMBuff->rateAge = dUTC;
[1325]1068
1069 } else {
[1452]1070 // Although we did not record the relevant position timestamp
1071 // explicitly, it can easily be deduced.
1072 double w = iMBuff->utc - utcDiff(cUTC, iMBuff->utc) -
1073 iMBuff->rateAge;
1074 dUTC = utcDiff(iMBuff->utc, w);
1075
1076 if (dUTC > 0.0) {
1077 iMBuff->rateAge = 0.0f;
[1325]1078 } else {
[1452]1079 iMBuff->rateAge = dUTC;
[1325]1080 }
1081 }
[1452]1082
1083 iMBuff->raRate = raRate;
1084 iMBuff->decRate = decRate;
1085 iMBuff->paRate = paRate;
[1325]1086 }
1087 }
1088
[1427]1089#ifdef PKSIO_DEBUG
[1452]1090 double avRate = sqrt(cAvRate[0]*cAvRate[0] + cAvRate[1]*cAvRate[1]);
1091 fprintf(stderr, "RA, Dec, Av & PA rates: %8.4f %8.4f %8.4f %8.4f "
1092 "pCode %d\n", raRate*R2D, decRate*R2D, avRate*R2D, paRate*R2D, pCode);
[1427]1093#endif
1094
[1452]1095
[1325]1096 // Compute the position of this beam for all bins.
1097 for (int idx = 0; idx < cNBin; idx++) {
1098 int jbuff = iBeamSel + cNBeamSel*idx;
1099
1100 cBuffer[jbuff].raRate = iMBuff->raRate;
1101 cBuffer[jbuff].decRate = iMBuff->decRate;
[1452]1102 cBuffer[jbuff].paRate = iMBuff->paRate;
[1325]1103
[1452]1104 double dUTC = utcDiff(cBuffer[jbuff].utc, thisUTC);
1105 if (dUTC > 100.0) {
[1325]1106 // Must have cycled through midnight.
[1452]1107 dUTC -= 86400.0;
[1325]1108 }
1109
[1452]1110 applyRate(cRA0, cDec0, thisRA, thisDec,
1111 cBuffer[jbuff].raRate, cBuffer[jbuff].decRate, dUTC,
1112 cBuffer[jbuff].ra, cBuffer[jbuff].dec);
1113
1114#ifdef PKSIO_DEBUG
1115 fprintf(stderr, "Intp (%d) ra, dec, UTC: %9.4f %9.4f %10.3f (pCode, "
1116 "age: %d %.1fs)\n", iMBuff->cycleNo, cBuffer[jbuff].ra*R2D,
1117 cBuffer[jbuff].dec*R2D, cBuffer[jbuff].utc, iMBuff->pCode,
1118 iMBuff->rateAge);
1119#endif
[1325]1120 }
[1635]1121
1122 cFlushing = 2;
[1325]1123 }
1124
1125
1126 if (cFlushing) {
1127 // Copy buffer location out one IF at a time.
1128 MBrec.extract(*iMBuff, cFlushIF);
1129 haveData = 1;
1130
1131#ifdef PKSIO_DEBUG
[1452]1132 fprintf(stderr, "Out:%4d%4d%3d%3d\n", MBrec.scanNo, MBrec.cycleNo,
1133 MBrec.beamNo, MBrec.IFno[0]);
[1325]1134#endif
1135
1136 // Signal that this IF in this buffer location has been flushed.
1137 iMBuff->IFno[cFlushIF] = 0;
1138
[1635]1139 iMBuff->nIF--;
1140 if (iMBuff->nIF == 0) {
1141 // All IFs in this buffer location have been flushed. Stop cEOS
1142 // being set when the next integration is read.
[1325]1143 iMBuff->cycleNo = 0;
1144
1145 } else {
1146 // Carry on flushing the other IFs.
1147 continue;
1148 }
1149
1150 // Has the whole buffer been flushed?
1151 if (cFlushBin == cNBin - 1) {
1152 if (cEOS || cEOF) {
1153 // Carry on flushing other buffers.
1154 cFlushIF = 0;
1155 continue;
1156 }
1157
1158 cFlushing = 0;
1159
1160 beamNo = int(cBaseline / 256.0);
1161 iBeamSel = cBeamSel[beamNo-1];
1162
1163 // Compute buffer number.
1164 iMBuff = cBuffer + iBeamSel;
1165 if (cNBin > 1) iMBuff += cNBeamSel*(cBin-1);
1166 }
1167 }
1168
1169 if (!cFlushing) {
1170 // Buffer this MBrec.
[1399]1171 if ((cScanNo > iMBuff->scanNo) && iMBuff->IFno[0]) {
[1325]1172 // Sanity check on the number of IFs in the new scan.
1173 if (if_.n_if != cNIF) {
[1452]1174 sprintf(cMsg, "WARNING: Scan %d has %d IFs instead of %d, "
1175 "continuing.", cScanNo, if_.n_if, cNIF);
1176 logMsg(cMsg);
[1325]1177 }
1178 }
1179
[1372]1180 // Sanity check on incomplete integrations within a scan.
1181 if (iMBuff->nIF && (iMBuff->cycleNo != cCycleNo)) {
1182 // Force the incomplete integration to be flushed before proceeding.
1183 cFlushing = 1;
1184 continue;
1185 }
1186
[1452]1187#ifdef PKSIO_DEBUG
1188 fprintf(stderr, "Buf:%4d%4d%3d%3d\n", cScanNo, cCycleNo, beamNo, cIFno);
1189#endif
[1325]1190
[1452]1191 // Store IF-independent parameters only for the first IF of a new cycle,
1192 // particularly because this is the only one for which the scan rates
1193 // are computed above.
1194 int firstIF = (iMBuff->nIF == 0);
1195 if (firstIF) {
1196 iMBuff->scanNo = cScanNo;
1197 iMBuff->cycleNo = cCycleNo;
[1325]1198
[1452]1199 // Times.
1200 strcpy(iMBuff->datobs, cDateObs);
1201 iMBuff->utc = cUTC;
1202 iMBuff->exposure = param_.intbase;
[1325]1203
[1452]1204 // Source identification.
1205 sprintf(iMBuff->srcName, "%-16.16s",
1206 names_.su_name + (cSrcNo-1)*16);
1207 iMBuff->srcName[16] = '\0';
1208 iMBuff->srcRA = doubles_.su_ra[cSrcNo-1];
1209 iMBuff->srcDec = doubles_.su_dec[cSrcNo-1];
1210
1211 // Rest frequency of the line of interest.
1212 iMBuff->restFreq = doubles_.rfreq;
1213 if (strncmp(names_.instrument, "ATPKSMB", 7) == 0) {
1214 // Fix the HI rest frequency recorded for Parkes multibeam data.
1215 double reffreq = doubles_.freq;
1216 double restfreq = doubles_.rfreq;
1217 if ((restfreq == 0.0 || fabs(restfreq - reffreq) == 0.0) &&
1218 fabs(reffreq - 1420.405752e6) < 100.0) {
1219 iMBuff->restFreq = 1420.405752e6;
1220 }
[1325]1221 }
1222
[1452]1223 // Observation type.
1224 int j;
1225 for (j = 0; j < 15; j++) {
1226 iMBuff->obsType[j] = names_.card[11+j];
1227 if (iMBuff->obsType[j] == '\'') break;
1228 }
1229 iMBuff->obsType[j] = '\0';
[1325]1230
[1452]1231 // Beam-dependent parameters.
1232 iMBuff->beamNo = beamNo;
[1325]1233
[1452]1234 // Beam position at the specified time.
1235 if (cSUpos) {
1236 // Non-ATNF data that does not store the position in (u,v,w).
1237 iMBuff->ra = doubles_.su_ra[cSrcNo-1];
1238 iMBuff->dec = doubles_.su_dec[cSrcNo-1];
1239 } else {
1240 iMBuff->ra = cU;
1241 iMBuff->dec = cV;
1242 }
1243 cPosUTC[iBeamSel] = cW;
1244 iMBuff->pCode = pCode;
1245
1246 // Store rates for next time.
1247 iMBuff->raRate = raRate;
1248 iMBuff->decRate = decRate;
1249 iMBuff->paRate = paRate;
[1325]1250 }
1251
1252 // IF-dependent parameters.
1253 int iIF = cIFno - 1;
1254 int startChan = cStartChan[iIF];
1255 int endChan = cEndChan[iIF];
1256 int refChan = cRefChan[iIF];
1257
1258 int nChan = abs(endChan - startChan) + 1;
1259
1260 iIFSel = cIFSel[iIF];
[1452]1261 if (iMBuff->IFno[iIFSel] == 0) {
1262 iMBuff->nIF++;
1263 iMBuff->IFno[iIFSel] = cIFno;
1264 } else {
1265 // Integration cycle written to the output file twice (the only known
1266 // example is 1999-05-22_1914_000-031805_03v.hpf).
1267 sprintf(cMsg, "WARNING: Integration cycle %d:%d, beam %2d, \n"
1268 " IF %d was duplicated.", cScanNo, cCycleNo-1,
1269 beamNo, cIFno);
1270 logMsg(cMsg);
1271 }
[1325]1272 iMBuff->nChan[iIFSel] = nChan;
1273 iMBuff->nPol[iIFSel] = cNPol[iIF];
1274
1275 iMBuff->fqRefPix[iIFSel] = doubles_.if_ref[iIF];
1276 iMBuff->fqRefVal[iIFSel] = doubles_.if_freq[iIF];
1277 iMBuff->fqDelt[iIFSel] =
1278 if_.if_invert[iIF] * fabs(doubles_.if_bw[iIF] /
1279 (if_.if_nfreq[iIF] - 1));
1280
1281 // Adjust for channel selection.
1282 if (iMBuff->fqRefPix[iIFSel] != refChan) {
1283 iMBuff->fqRefVal[iIFSel] +=
1284 (refChan - iMBuff->fqRefPix[iIFSel]) *
1285 iMBuff->fqDelt[iIFSel];
1286 iMBuff->fqRefPix[iIFSel] = refChan;
1287 }
1288
1289 if (endChan < startChan) {
1290 iMBuff->fqDelt[iIFSel] = -iMBuff->fqDelt[iIFSel];
1291 }
1292
1293
1294 // System temperature.
1295 int iBeam = beamNo - 1;
1296 int scq = sc_.sc_q;
1297 float TsysPol1 = sc_.sc_cal[scq*iBeam + 3];
1298 float TsysPol2 = sc_.sc_cal[scq*iBeam + 4];
1299 iMBuff->tsys[iIFSel][0] = TsysPol1*TsysPol1;
1300 iMBuff->tsys[iIFSel][1] = TsysPol2*TsysPol2;
1301
1302 // Calibration factor; may be changed later if the data is recalibrated.
1303 if (scq > 14) {
1304 // Will only be present for Parkes Multibeam or LBA data.
1305 iMBuff->calfctr[iIFSel][0] = sc_.sc_cal[scq*iBeam + 14];
1306 iMBuff->calfctr[iIFSel][1] = sc_.sc_cal[scq*iBeam + 15];
1307 } else {
1308 iMBuff->calfctr[iIFSel][0] = 0.0f;
1309 iMBuff->calfctr[iIFSel][1] = 0.0f;
1310 }
1311
1312 // Cross-polarization calibration factor (unknown to MBFITS).
1313 for (int j = 0; j < 2; j++) {
1314 iMBuff->xcalfctr[iIFSel][j] = 0.0f;
1315 }
1316
1317 // Baseline parameters (unknown to MBFITS).
1318 iMBuff->haveBase = 0;
1319
1320 // Data (always present in MBFITS).
1321 iMBuff->haveSpectra = 1;
1322
1323 // Flag: bit 0 set if off source.
1324 // bit 1 set if loss of sync in A polarization.
1325 // bit 2 set if loss of sync in B polarization.
1326 unsigned char rpflag =
1327 (unsigned char)(sc_.sc_cal[scq*iBeam + 12] + 0.5f);
1328
1329 // The baseline flag may be set independently.
1330 if (rpflag == 0) rpflag = cFlag;
1331
1332 // Copy and scale data.
1333 int inc = 2 * if_.if_nstok[iIF];
1334 if (endChan < startChan) inc = -inc;
1335
1336 float TsysF;
1337 iMBuff->spectra[iIFSel] = iMBuff->spectra[0] + cChanOff[iIF];
1338 iMBuff->flagged[iIFSel] = iMBuff->flagged[0] + cChanOff[iIF];
1339
1340 float *spectra = iMBuff->spectra[iIFSel];
1341 unsigned char *flagged = iMBuff->flagged[iIFSel];
1342 for (int ipol = 0; ipol < cNPol[iIF]; ipol++) {
1343 if (sc_.sc_cal[scq*iBeam + 3 + ipol] > 0.0f) {
1344 // The correlator has already applied the calibration.
1345 TsysF = 1.0f;
1346 } else {
1347 // The correlator has normalized cVis[k] to a Tsys of 500K.
1348 TsysF = iMBuff->tsys[iIFSel][ipol] / 500.0f;
1349 }
1350
1351 int k = 2 * (if_.if_nstok[iIF]*(startChan - 1) + ipol);
1352 for (int ichan = 0; ichan < nChan; ichan++) {
1353 *(spectra++) = TsysF * cVis[k];
1354 *(flagged++) = rpflag;
1355 k += inc;
1356 }
1357 }
1358
1359 if (cHaveXPol[iIF]) {
1360 int k = 2 * (3*(startChan - 1) + 2);
1361 iMBuff->xpol[iIFSel] = iMBuff->xpol[0] + cXpolOff[iIF];
1362 float *xpol = iMBuff->xpol[iIFSel];
1363 for (int ichan = 0; ichan < nChan; ichan++) {
1364 *(xpol++) = cVis[k];
1365 *(xpol++) = cVis[k+1];
1366 k += inc;
1367 }
1368 }
1369
1370
1371 // Calibration factor applied to the data by the correlator.
1372 if (scq > 14) {
1373 // Will only be present for Parkes Multibeam or LBA data.
1374 iMBuff->tcal[iIFSel][0] = sc_.sc_cal[scq*iBeam + 14];
1375 iMBuff->tcal[iIFSel][1] = sc_.sc_cal[scq*iBeam + 15];
1376 } else {
1377 iMBuff->tcal[iIFSel][0] = 0.0f;
1378 iMBuff->tcal[iIFSel][1] = 0.0f;
1379 }
1380
[1452]1381 if (firstIF) {
1382 if (sc_.sc_ant <= anten_.nant) {
1383 // No extra syscal information present.
1384 iMBuff->extraSysCal = 0;
1385 iMBuff->azimuth = 0.0f;
1386 iMBuff->elevation = 0.0f;
1387 iMBuff->parAngle = 0.0f;
1388 iMBuff->focusAxi = 0.0f;
1389 iMBuff->focusTan = 0.0f;
1390 iMBuff->focusRot = 0.0f;
1391 iMBuff->temp = 0.0f;
1392 iMBuff->pressure = 0.0f;
1393 iMBuff->humidity = 0.0f;
1394 iMBuff->windSpeed = 0.0f;
1395 iMBuff->windAz = 0.0f;
1396 strcpy(iMBuff->tcalTime, " ");
1397 iMBuff->refBeam = 0;
[1325]1398
[1452]1399 } else {
1400 // Additional information for Parkes Multibeam data.
1401 int iOff = scq*(sc_.sc_ant - 1) - 1;
1402 iMBuff->extraSysCal = 1;
[1325]1403
[1452]1404 iMBuff->azimuth = sc_.sc_cal[iOff + 2];
1405 iMBuff->elevation = sc_.sc_cal[iOff + 3];
1406 iMBuff->parAngle = sc_.sc_cal[iOff + 4];
[1325]1407
[1452]1408 iMBuff->focusAxi = sc_.sc_cal[iOff + 5] * 1e-3;
1409 iMBuff->focusTan = sc_.sc_cal[iOff + 6] * 1e-3;
1410 iMBuff->focusRot = sc_.sc_cal[iOff + 7];
1411
1412 iMBuff->temp = sc_.sc_cal[iOff + 8];
1413 iMBuff->pressure = sc_.sc_cal[iOff + 9];
1414 iMBuff->humidity = sc_.sc_cal[iOff + 10];
1415 iMBuff->windSpeed = sc_.sc_cal[iOff + 11];
1416 iMBuff->windAz = sc_.sc_cal[iOff + 12];
1417
1418 char *tcalTime = iMBuff->tcalTime;
1419 sprintf(tcalTime, "%-16.16s", (char *)(&sc_.sc_cal[iOff+13]));
1420 tcalTime[16] = '\0';
1421
[1325]1422#ifndef AIPS_LITTLE_ENDIAN
[1452]1423 // Do byte swapping on the ASCII date string.
1424 for (int j = 0; j < 16; j += 4) {
1425 char ctmp;
1426 ctmp = tcalTime[j];
1427 tcalTime[j] = tcalTime[j+3];
1428 tcalTime[j+3] = ctmp;
1429 ctmp = tcalTime[j+1];
1430 tcalTime[j+1] = tcalTime[j+2];
1431 tcalTime[j+2] = ctmp;
1432 }
[1325]1433#endif
1434
[1452]1435 // Reference beam number.
1436 float refbeam = sc_.sc_cal[iOff + 17];
1437 if (refbeam > 0.0f || refbeam < 100.0f) {
1438 iMBuff->refBeam = int(refbeam);
1439 } else {
1440 iMBuff->refBeam = 0;
1441 }
[1325]1442 }
1443 }
1444 }
1445 }
1446
1447 return 0;
1448}
1449
1450//-------------------------------------------------------- MBFITSreader::rpget
1451
1452// Read the next data record from the RPFITS file.
1453
1454int MBFITSreader::rpget(int syscalonly, int &EOS)
1455{
1456 EOS = 0;
1457
1458 int retries = 0;
1459
1460 // Allow 10 read errors.
1461 int numErr = 0;
1462
1463 int jstat = 0;
1464 while (numErr < 10) {
1465 int lastjstat = jstat;
1466
[1452]1467 switch(rpfitsin(jstat)) {
[1325]1468 case -1:
1469 // Read failed; retry.
1470 numErr++;
[1452]1471 logMsg("WARNING: RPFITS read failed - retrying.");
[1325]1472 jstat = 0;
1473 break;
1474
1475 case 0:
1476 // Successful read.
1477 if (lastjstat == 0) {
1478 if (cBaseline == -1) {
1479 // Syscal data.
1480 if (syscalonly) {
1481 return 0;
1482 }
1483
1484 } else {
1485 if (!syscalonly) {
1486 return 0;
1487 }
1488 }
1489 }
1490
1491 // Last operation was to read header or FG table; now read data.
1492 break;
1493
1494 case 1:
1495 // Encountered header while trying to read data; read it.
1496 EOS = 1;
1497 jstat = -1;
1498 break;
1499
1500 case 2:
1501 // End of scan; read past it.
1502 jstat = 0;
1503 break;
1504
1505 case 3:
1506 // End-of-file; retry applies to real-time mode.
1507 if (retries++ >= cRetry) {
1508 return -1;
1509 }
1510
1511 sleep(10);
1512 jstat = 0;
1513 break;
1514
1515 case 4:
1516 // Encountered FG table while trying to read data; read it.
1517 jstat = -1;
1518 break;
1519
1520 case 5:
1521 // Illegal data at end of block after close/reopen operation; retry.
1522 jstat = 0;
1523 break;
1524
1525 default:
1526 // Shouldn't reach here.
[1452]1527 sprintf(cMsg, "WARNING: Unrecognized RPFITSIN return code: %d "
1528 "(retrying).", jstat);
1529 logMsg(cMsg);
[1325]1530 jstat = 0;
1531 break;
1532 }
1533 }
1534
[1452]1535 logMsg("ERROR: RPFITS read failed too many times.");
[1325]1536 return 2;
1537}
1538
[1452]1539//----------------------------------------------------- MBFITSreader::rpfitsin
1540
1541// Wrapper around RPFITSIN that reports errors. Returned RPFITSIN subroutine
1542// arguments are captured as MBFITSreader member variables.
1543
1544int MBFITSreader::rpfitsin(int &jstat)
1545
1546{
1547 rpfitsin_(&jstat, cVis, cWgt, &cBaseline, &cUTC, &cU, &cV, &cW, &cFlag,
1548 &cBin, &cIFno, &cSrcNo);
1549
1550 // Handle messages from RPFITSIN.
1551 if (names_.errmsg[0] != ' ') {
1552 int i;
1553 for (i = 80; i > 0; i--) {
1554 if (names_.errmsg[i-1] != ' ') break;
1555 }
1556
1557 sprintf(cMsg, "WARNING: Cycle %d:%03d, RPFITSIN reported -\n"
1558 " %.*s", cScanNo, cCycleNo, i, names_.errmsg);
1559 logMsg(cMsg);
1560 }
1561
1562 return jstat;
1563}
1564
1565//------------------------------------------------------- MBFITSreader::fixPos
1566
1567// Check and, if necessary, repair a position timestamp.
1568//
1569// Problems with the position timestamp manifest themselves via the scan rate:
1570//
1571// 1) Zero scan rate pairs, 1997/02/28 to 1998/01/07
1572//
1573// These occur because the position timestamp for the first integration
1574// of the pair is erroneous; the value recorded is t/1000, where t is the
1575// true value.
1576// Earliest known: 97-02-28_1725_132653-42_258a.hpf
1577// Latest known: 98-01-02_1923_095644-50_165c.hpf
1578// (time range chosen to encompass observing runs).
1579//
1580// 2) Slow-fast scan rate pairs (0.013 - 0.020 deg/s),
1581// 1997/03/28 to 1998/01/07.
1582//
1583// The UTC position timestamp is 1.0s later than it should be (never
1584// earlier), almost certainly arising from an error in the telescope
1585// control system.
1586// Earliest known: 97-03-28_0150_010420-74_008d.hpf
1587// Latest known: 98-01-04_1502_065150-02_177c.hpf
1588// (time range chosen to encompass observing runs).
1589//
1590// 3) Slow-fast scan rate pairs (0.015 - 0.018 deg/s),
1591// 1999/05/20 to 2001/07/12 (HIPASS and ZOA),
1592// 2001/09/02 to 2001/12/04 (HIPASS and ZOA),
1593// 2002/03/28 to 2002/05/13 (ZOA only),
1594// 2003/04/26 to 2003/06/09 (ZOA only).
1595// Earliest known: 1999-05-20_1818_175720-50_297e.hpf
1596// Latest known: 2001-12-04_1814_065531p14_173e.hpf (HIPASS)
1597// 2003-06-09_1924_352-085940_-6c.hpf (ZOA)
1598//
1599// Caused by the Linux signalling NaN problem. IEEE "signalling" NaNs
1600// are silently transformed to "quiet" NaNs during assignment by setting
1601// bit 22. This affected RPFITS because of its use of VAX-format
1602// floating-point numbers which, with their permuted bytes, may sometimes
1603// appear as signalling NaNs.
1604//
1605// The problem arose when the linux correlator came online and was
1606// fixed with a workaround to the RPFITS library (repeated episodes
1607// are probably due to use of an older version of the library). It
1608// should not have affected the data significantly because of the
1609// low relative error, which ranges from 0.0000038 to 0.0000076, but
1610// it is important for the computation of scan rates which requires
1611// taking the difference of two large UTC timestamps, one or other
1612// of which will have 0.5s added to it.
1613//
1614// The return value identifies which, if any, of these problems was repaired.
1615
1616int MBFITSreader::fixw(
1617 const char *datobs,
1618 int cycleNo,
1619 int beamNo,
1620 double avRate[2],
1621 double thisRA,
1622 double thisDec,
1623 double thisUTC,
1624 double nextRA,
1625 double nextDec,
1626 float &nextUTC)
1627{
1628 if (strcmp(datobs, "2003-06-09") > 0) {
1629 return 0;
1630
1631 } else if (strcmp(datobs, "1998-01-07") <= 0) {
1632 if (nextUTC < thisUTC && (nextUTC + 86400.0) > (thisUTC + 600.0)) {
1633 // Possible scaling problem.
1634 double diff = nextUTC*1000.0 - thisUTC;
1635 if (0.0 < diff && diff < 600.0) {
1636 nextUTC *= 1000.0;
1637 return 1;
1638 } else {
1639 // Irreparable.
1640 return -1;
1641 }
1642 }
1643
1644 if (cycleNo > 2) {
1645 if (beamNo == 1) {
1646 // This test is only reliable for beam 1.
1647 double dUTC = nextUTC - thisUTC;
1648 if (dUTC < 0.0) dUTC += 86400.0;
1649
1650 // Guard against RA cycling through 24h in either direction.
1651 if (fabs(nextRA - thisRA) > PI) {
1652 if (nextRA < thisRA) {
1653 nextRA += TWOPI;
1654 } else {
1655 nextRA -= TWOPI;
1656 }
1657 }
1658
1659 double dRA = (nextRA - thisRA) * cos(nextDec);
1660 double dDec = nextDec - thisDec;
1661 double arc = sqrt(dRA*dRA + dDec*dDec);
1662
1663 double averate = sqrt(avRate[0]*avRate[0] + avRate[1]*avRate[1]);
1664 double diff1 = fabs(averate - arc/(dUTC-1.0));
1665 double diff2 = fabs(averate - arc/dUTC);
1666 if ((diff1 < diff2) && (diff1 < 0.05*averate)) {
1667 nextUTC -= 1.0;
1668 cCode5 = cycleNo;
1669 return 2;
1670 } else {
1671 cCode5 = 0;
1672 }
1673
1674 } else {
1675 if (cycleNo == cCode5) {
1676 nextUTC -= 1.0;
1677 return 2;
1678 }
1679 }
1680 }
1681
1682 } else if ((strcmp(datobs, "1999-05-20") >= 0 &&
1683 strcmp(datobs, "2001-07-12") <= 0) ||
1684 (strcmp(datobs, "2001-09-02") >= 0 &&
1685 strcmp(datobs, "2001-12-04") <= 0) ||
1686 (strcmp(datobs, "2002-03-28") >= 0 &&
1687 strcmp(datobs, "2002-05-13") <= 0) ||
1688 (strcmp(datobs, "2003-04-26") >= 0 &&
1689 strcmp(datobs, "2003-06-09") <= 0)) {
1690 // Signalling NaN problem, e.g. 1999-07-26_1839_011106-74_009c.hpf.
1691 // Position timestamps should always be an integral number of seconds.
1692 double resid = nextUTC - int(nextUTC);
1693 if (resid == 0.5) {
1694 nextUTC -= 0.5;
1695 return 3;
1696 }
1697 }
1698
1699 return 0;
1700}
1701
[1325]1702//-------------------------------------------------------- MBFITSreader::close
1703
1704// Close the input file.
1705
1706void MBFITSreader::close(void)
1707{
1708 if (cMBopen) {
1709 int jstat = 1;
1710 rpfitsin_(&jstat, cVis, cWgt, &cBaseline, &cUTC, &cU, &cV, &cW, &cFlag,
1711 &cBin, &cIFno, &cSrcNo);
1712
1713 if (cBeams) delete [] cBeams;
1714 if (cIFs) delete [] cIFs;
1715 if (cNChan) delete [] cNChan;
1716 if (cNPol) delete [] cNPol;
1717 if (cHaveXPol) delete [] cHaveXPol;
1718 if (cStartChan) delete [] cStartChan;
1719 if (cEndChan) delete [] cEndChan;
1720 if (cRefChan) delete [] cRefChan;
1721
1722 if (cVis) delete [] cVis;
1723 if (cWgt) delete [] cWgt;
1724
1725 if (cBeamSel) delete [] cBeamSel;
1726 if (cIFSel) delete [] cIFSel;
1727 if (cChanOff) delete [] cChanOff;
1728 if (cXpolOff) delete [] cXpolOff;
1729 if (cBuffer) delete [] cBuffer;
1730 if (cPosUTC) delete [] cPosUTC;
1731
1732 cMBopen = 0;
1733 }
1734}
[1452]1735
1736//-------------------------------------------------------------------- utcDiff
1737
1738// Subtract two UTCs (s) allowing for any plausible number of cycles through
1739// 86400s, returning a result in the range [-43200, +43200]s.
1740
1741double MBFITSreader::utcDiff(double utc1, double utc2)
1742{
1743 double diff = utc1 - utc2;
1744
1745 if (diff > 43200.0) {
1746 diff -= 86400.0;
1747 while (diff > 43200.0) diff -= 86400.0;
1748 } else if (diff < -43200.0) {
1749 diff += 86400.0;
1750 while (diff < -43200.0) diff += 86400.0;
1751 }
1752
1753 return diff;
1754}
1755
1756//------------------------------------------------------- scanRate & applyRate
1757
1758// Compute and apply the scan rate corrected for grid convergence. (ra0,dec0)
1759// are the coordinates of the central beam, assumed to be the tracking centre.
1760// The rate computed in RA will be a rate of change of angular distance in the
1761// direction of increasing RA at the position of the central beam. Similarly
1762// for declination. Angles in radian, time in s.
1763
1764void MBFITSreader::scanRate(
1765 double ra0,
1766 double dec0,
1767 double ra1,
1768 double dec1,
1769 double ra2,
1770 double dec2,
1771 double dt,
1772 double &raRate,
1773 double &decRate)
1774{
1775 // Transform to a system where the central beam lies on the equator at 12h.
1776 eulerx(ra1, dec1, ra0+HALFPI, -dec0, -HALFPI, ra1, dec1);
1777 eulerx(ra2, dec2, ra0+HALFPI, -dec0, -HALFPI, ra2, dec2);
1778
1779 raRate = (ra2 - ra1) / dt;
1780 decRate = (dec2 - dec1) / dt;
1781}
1782
1783
1784void MBFITSreader::applyRate(
1785 double ra0,
1786 double dec0,
1787 double ra1,
1788 double dec1,
1789 double raRate,
1790 double decRate,
1791 double dt,
1792 double &ra2,
1793 double &dec2)
1794{
1795 // Transform to a system where the central beam lies on the equator at 12h.
1796 eulerx(ra1, dec1, ra0+HALFPI, -dec0, -HALFPI, ra1, dec1);
1797
1798 ra2 = ra1 + (raRate * dt);
1799 dec2 = dec1 + (decRate * dt);
1800
1801 // Transform back.
1802 eulerx(ra2, dec2, -HALFPI, dec0, ra0+HALFPI, ra2, dec2);
1803}
1804
1805//--------------------------------------------------------------------- eulerx
1806
1807void MBFITSreader::eulerx(
1808 double lng0,
1809 double lat0,
1810 double phi0,
1811 double theta,
1812 double phi,
1813 double &lng1,
1814 double &lat1)
1815
1816// Applies the Euler angle based transformation of spherical coordinates.
1817//
1818// phi0 Longitude of the ascending node in the old system, radians. The
1819// ascending node is the point of intersection of the equators of
1820// the two systems such that the equator of the new system crosses
1821// from south to north as viewed in the old system.
1822//
1823// theta Angle between the poles of the two systems, radians. THETA is
1824// positive for a positive rotation about the ascending node.
1825//
1826// phi Longitude of the ascending node in the new system, radians.
1827
1828{
1829 // Compute intermediaries.
1830 double lng0p = lng0 - phi0;
1831 double slng0p = sin(lng0p);
1832 double clng0p = cos(lng0p);
1833 double slat0 = sin(lat0);
1834 double clat0 = cos(lat0);
1835 double ctheta = cos(theta);
1836 double stheta = sin(theta);
1837
1838 double x = clat0*clng0p;
1839 double y = clat0*slng0p*ctheta + slat0*stheta;
1840
1841 // Longitude in the new system.
1842 if (x != 0.0 || y != 0.0) {
1843 lng1 = phi + atan2(y, x);
1844 } else {
1845 // Longitude at the poles in the new system is consistent with that
1846 // specified in the old system.
1847 lng1 = phi + lng0p;
1848 }
1849 lng1 = fmod(lng1, TWOPI);
1850 if (lng1 < 0.0) lng1 += TWOPI;
1851
1852 lat1 = asin(slat0*ctheta - clat0*stheta*slng0p);
1853}
Note: See TracBrowser for help on using the repository browser.