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

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

Update from livedata CVS

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