int smf_fix_metadata_scuba2 ( msglev_t msglev, smfData * data, int have_fixed, int *ncards, int * status ) {

  AstFitsChan * fits = NULL; /* FITS header (FitsChan) */
  struct FitsHeaderStruct fitsvals; /* Quick access Fits header struct */
  smfHead *hdr = NULL;       /* Data header struct */
  AstKeyMap * obsmap = NULL; /* Info from all observations */
  AstKeyMap * objmap = NULL; /* All the object names used */

  if (*status != SAI__OK) return have_fixed;

  /* Validate arguments - need smfFile and smfHead */
  smf_validate_smfData( data, 1, 1, status );
  if (*status != SAI__OK) return have_fixed;

  hdr = data->hdr;
  smf_validate_smfHead( hdr, 1, 1, status );
  if (*status != SAI__OK) return have_fixed;

  fits = hdr->fitshdr;

  if (hdr->instrument != INST__SCUBA2) {
    if (*status != SAI__OK) {
      *status = SAI__ERROR;
      errRep("", " Attempting to fix metadata using SCUBA-2 algorithms but this is not SCUBA-2 data",
             status );
    }
    return have_fixed;
  }

  /* Update units string to something that is FITS standard compliant
     - we used "DAC units" for a while but in FITS land this becomes
     "decacoulomb * units" */
  if ( strncmp( hdr->units, "DAC", 3) == 0 ) {
    one_strlcpy( hdr->units, "adu", SMF__CHARLABEL, status );
  }

  /* Clock jitter and readout efficiencies mean we need to recalculate STEPTIME from the data.
     This is possible because we know that we have a continuous sequence in each file (unlike
     ACSIS). */
  if (hdr->allState) {
    /* it will be odd if it is not there */
    size_t nframes = hdr->nframes;

    size_t istart = 0;
    double start_time = (hdr->allState)[istart].rts_end;
    while( start_time == VAL__BADD && ++istart < nframes ) {
      start_time = (hdr->allState)[istart].rts_end;
    }

    size_t iend = nframes - 1;
    double end_time = (hdr->allState)[iend].rts_end;
    while( end_time == VAL__BADD && iend-- > 0 ) {
      end_time = (hdr->allState)[iend].rts_end;
    }

    double steptime = VAL__BADD;
    double newstep;

    smf_getfitsd( hdr, "STEPTIME", &steptime, status );
    newstep = steptime;

    /* it is possible for a file to contain only one step since
       the DA just dumps every N-steps. We can not recalculate the
       step time in that case. */
    nframes = iend - istart + 1;
    if (nframes > 1) {

      /* duration of file in days */
      newstep = end_time - start_time;

      /* convert to seconds */
      newstep *= SPD;

      /* Convert to step time */
      newstep /= (nframes - 1);
    } else if( nframes > 0 ) {
      /* work it out from RTS_END and TCS_TAI */
      JCMTState * onlystate = &((hdr->allState)[istart]);
      if ( onlystate->tcs_tai != VAL__BADD &&
           onlystate->tcs_tai != onlystate->rts_end) {
        /* TCS_TAI is in the middle of the step */
        newstep = 2.0 * ( onlystate->rts_end - onlystate->tcs_tai ) * SPD;
      }
    } else if( *status == SAI__OK ) {
      *status = SAI__ERROR;
      if( data->file ) {
         smf_smfFile_msg( data->file, "N", 1, "<unknown>" );
         errRep("", "No valid RTS_END values found in NDF '^N'.", status );
      } else {
         errRep("", "No valid RTS_END values found.", status );
      }
    }

    if (steptime != newstep) {
      msgOutiff( msglev, "", INDENT "Recalculated step time as %g sec from JCMTSTATE (was %g sec)",
                 status, newstep, steptime);
      smf_fits_updateD( hdr, "STEPTIME", newstep, NULL, status );
      have_fixed |= SMF__FIXED_FITSHDR;
    }
  }


  /* Read some FITS headers, intialising the struct first */
  fitsvals.utdate = VAL__BADI;
  *(fitsvals.instrume) = '\0';
  smf_getfitsi( hdr, "UTDATE", &(fitsvals.utdate), status );
  smf_getfitss( hdr, "INSTRUME", fitsvals.instrume, sizeof(fitsvals.instrume), status );

  /* Print out summary of this observation - this may get repetitive if multiple files come
     from the same observation in one invocation but it seems better to describe each fix up
     separately and in context. */
  obsmap = astKeyMap( " " );
  objmap = astKeyMap( " " );
  smf_obsmap_fill( data, obsmap, objmap, status );
  smf_obsmap_report( msglev, obsmap, objmap, status );
  obsmap = astAnnul( obsmap );
  objmap = astAnnul( objmap );

  /* First we need to look for a BACKEND header which we do not write to the raw
     data files but CADC would like to see equal to INSTRUME */
  if (!astTestFits( fits, "BACKEND", NULL ) ) {
    have_fixed |= SMF__FIXED_FITSHDR;
    smf_fits_updateS( hdr, "BACKEND", fitsvals.instrume, "Name of the backend", status );
    msgOutif( msglev, "",  INDENT "Setting backend for SCUBA-2 observation.", status);
  }

  /* BASETEMP was reading MUXTEMP for pre-20091101 data */
  if ( fitsvals.utdate < 20091101 ) {
    double muxtemp = 0.0;
    have_fixed |= SMF__FIXED_FITSHDR;
    smf_getfitsd( hdr, "BASETEMP", &muxtemp, status );
    smf_fits_updateU( hdr, "BASETEMP", "[K] Base temperature", status );
    smf_fits_updateD( hdr, "MUXTEMP", muxtemp, "[K] Mux temperature", status );
    msgOutif( msglev, "", INDENT "Mux temperature is being read from BASETEMP header.", status );
  }

  /* Sometime before 20091119 the SHUTTER keyword was written as a string
     OPEN or CLOSED. Rewrite those as numbers */
  if (fitsvals.utdate < 20091119) {
    double shutval = 0.0;
    /* Try to read as a double. */
    smf_fits_getD( hdr, "SHUTTER", &shutval, status );

    /* Old data was a string. Convert to a double */
    if (*status == AST__FTCNV) {
      char shutter[100];
      errAnnul( status );
      smf_fits_getS( hdr, "SHUTTER", shutter, sizeof(shutter), status);
      if (strcmp(shutter, "CLOSED") == 0) {
        shutval = 0.0;
      } else {
        shutval = 1.0;
      }
      /* update the value */
      have_fixed |= SMF__FIXED_FITSHDR;
      smf_fits_updateD( hdr, "SHUTTER", shutval, "shutter position 0-Closed 1-Open", status );
      msgOutif( msglev, "", INDENT "Forcing SHUTTER header to be numeric", status );
    }
  }

  /* Engineering data with just SCUBA2 and no RTS left the RTS_NUM field
     filled with zeroes. Just assume that a zero in RTS_NUM is always
     indicative of a private sequence. */
  if (fitsvals.utdate < 20110401) {
    size_t nframes = hdr->nframes;
    JCMTState * curstate = &((hdr->allState)[0]);
    JCMTState * endstate = &((hdr->allState)[nframes-1]);
    if (curstate->rts_num == 0 && endstate->rts_num == 0) {
      /* have to set the values from the SEQSTART and SEQEND headers
         since those were set correctly (although any value would
         do of course apart from the sanity check in smf_find_science. */
      size_t i;
      int seqnum = 1;
      smf_fits_getI( hdr, "SEQSTART", &seqnum, status );
      for ( i=0; i<nframes; i++) {
        curstate = &((hdr->allState)[i]);
        curstate->rts_num = seqnum;
        seqnum++;
      }
      have_fixed |= SMF__FIXED_JCMTSTATE;
      msgOutif( msglev, "", INDENT "Private RTS sequence. Fixing RTS_NUM.", status );
    }
  }

  /* work out if this is a fast flat observation taken before May 2010 */
  if (fitsvals.utdate > 20100218 && fitsvals.utdate < 20100501) {
    char buff[100];
    /* need to know whether this is a FASTFLAT */
    smf_getfitss( hdr, "SEQ_TYPE", buff, sizeof(buff), status );

    if (strcmp( buff, "FASTFLAT" ) == 0 ) {

      /* Fast flats had incorrect SHUTTER settings for one night */
      if (fitsvals.utdate == 20100223) {
        have_fixed |= SMF__FIXED_FITSHDR;
        smf_fits_updateD( hdr, "SHUTTER", 1.0, "shutter position 0-Closed 1-Open", status );
        msgOutif( msglev, "", INDENT "Shutter was open for fast flatfield ramp. Correcting.", status );
      }

      /* Need to fix up SC2_HEAT ramps */
      /* the problem is that the data were assumed to be taken with 3 measurements
         in each heater setting. What actually happened was that the first 5 were
         done at the reference setting and then the data were grouped in threes
         finishing with a single value at the reference setting again.

         For example the heater values and the actual values look something like

           Stored    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
           Actual    0 0 0 0 0 2 2 2 3 3 3 4 4 4 5

         So we can correct for this by starting at the end and copying in the value
         two slots further down until we get to position #4. Then replacing that with
         the PIXHEAT number.
      */

      {
        size_t i;
        int pixheat = 0;
        size_t nframes = hdr->nframes;
        smf_getfitsi( hdr, "PIXHEAT", &pixheat, status );

        /* shift everything up by 2 */
        for (i=nframes-1; i > 4; i--) {
          JCMTState * curstate = &((hdr->allState)[i]);
          JCMTState * prevstate = &((hdr->allState)[i-2]);
          curstate->sc2_heat = prevstate->sc2_heat;
        }

        /* fill in the first 5 slots with the same value */
        for (i=0; i<5; i++) {
          JCMTState * curstate = &((hdr->allState)[i]);
          curstate->sc2_heat = pixheat;
        }
        have_fixed |= SMF__FIXED_JCMTSTATE;
      }
    }
  }

  /* We always recalculate the WVM start and end tau values so that the header
     reflects something approximating the value that was actually used in the
     extinction correction.

     Note that smf_calc_smoothedwvm can do a better job because it has multiple
     subarrays to get all the values from. We just have to try with what we
     have from a single subarray. We do step into the time series until we
     find something good.

     The header values should mostly agree with the recalculated values if the
     WVM code at the time matches the code in SMURF for that date. This has not
     been true in cases where we have retrospectively realised that there has been
     a calibration error in the WVM. So that we do not have to keep track of those
     times explicitly we currently recalculate every time. If this recalculation
     becomes a problem (smf_calc_wvm has a cache now to minimize this) it should
     be possible to disable this recalculation if the file is less than, say,
     30 minutes old to indicate we are running in near realtime.

     As a special case we do not recalculate the headers for FOCUS observations
     as the WVM reading is somewhat irrelevant and simply slows things down.

  */

  if( *status == SAI__OK ){

    /* Have not parsed header yet to extract type so do it explicitly here */
    char obstype[100];
    smf_getfitss( hdr, "OBS_TYPE", obstype, sizeof(obstype), status );

    if (strcasecmp( obstype, "focus") != 0) {

      size_t i;
      size_t nframes = hdr->nframes;
      double starttau = VAL__BADD;
      double starttime = VAL__BADD;
      double endtau = VAL__BADD;
      double endtime = VAL__BADD;

      /* Create a TimeFrame that can be used to format MJD values into ISO
         date-time strings, including a "T" separator between time and date. */
      AstTimeFrame *tf = astTimeFrame( "Format=iso.0T" );

      for (i=0; i < nframes && *status == SAI__OK; i++) {
        smf__calc_wvm_index( hdr, "AMSTART", i, &starttau, &starttime, status );
        if (starttau != VAL__BADD) break;
        if (*status == SAI__ERROR) errAnnul( status );
      }

      /* if we did not get a start tau we are not going to get an end tau */
      if (starttau != VAL__BADD) {
        for (i=0; i < nframes && *status == SAI__OK; i++) {
          smf__calc_wvm_index( hdr, "AMEND", nframes - 1 - i, &endtau, &endtime, status );
          if (endtau != VAL__BADD) break;
          if (*status == SAI__ERROR) errAnnul( status );
        }
      }

      /* If we could not find any WVM readings then we have a bit of a problem.
         Do we clear the FITS headers or do we leave them untouched? Leave them
         alone for now. */
      if (starttau != VAL__BADD && starttime != VAL__BADD) {
        smf_fits_updateD( hdr, "WVMTAUST", starttau, "186GHz Tau from JCMT WVM at start", status );

        /* Convert starttime MJD to ISO format and update the value in the
           FITS header. */
        smf_fits_updateS( hdr, "WVMDATST", astFormat( tf, 1, starttime ),
                          "Time of WVMTAUST", status );
        have_fixed |= SMF__FIXED_FITSHDR;
      }

      if (endtau != VAL__BADD && endtime != VAL__BADD) {
        smf_fits_updateD( hdr, "WVMTAUEN", endtau, "186GHz Tau from JCMT WVM at end", status );

        /* Convert endtime MJD to ISO format and update the value in the
           FITS header. */
        smf_fits_updateS( hdr, "WVMDATEN", astFormat( tf, 1, endtime ),
                          "Time of WVMTAUEN", status );
        have_fixed |= SMF__FIXED_FITSHDR;
      }

      /* Free the TimeFrame. */
      tf = astAnnul( tf );

    }
  }


  /* SEQ_TYPE header turned up in 20091125. Before that date the SEQ_TYPE only
     had two values. If the shutter was open then SEQ_TYPE is just OBS_TYPE. In the
     dark only a FLATFIELD sometimes finished with a noise but in that case CALCFLAT
     doesn't care so we just call it a flatfield sequence anyhow. We could look at
     the OBSEND flag but I'm not sure it makes a difference. */
  if ( fitsvals.utdate < 20091125 ) {
    char obstype[100];
    char seqtype[100];
    double shutval = 0.0;
    /* need to know what type of observation this is */
    smf_getfitss( hdr, "OBS_TYPE", obstype, sizeof(obstype), status );
    /* and the shutter status */
    smf_fits_getD( hdr, "SHUTTER", &shutval, status );

    if (shutval == 0.0 && strcasecmp( obstype, "flatfield" ) != 0 ) {
      /* flatfield was the only non-noise observation in the dark */
      one_strlcpy( seqtype, "NOISE", sizeof(seqtype), status );
      msgOutif( msglev, "", INDENT "Setting sequence type to NOISE", status );
    } else {
      /* Shutter was open so SEQ_TYPE is just OBS_TYPE */
      one_strlcpy( seqtype, obstype, sizeof(seqtype), status );
      msgOutif( msglev, "",  INDENT "Setting sequence type to obs type", status);
    }
    smf_fits_updateS( hdr, "SEQ_TYPE", seqtype, "Type of sequence", status );
    have_fixed |= SMF__FIXED_FITSHDR;
  }

  /* The telescope goes crazy at the end of observation 56 on 20110530. Null
     the telescope data for subscans 30, 31 and 32 */
  if (fitsvals.utdate == 20110530) {
    char obsid[81];
    smf_getobsidss( hdr->fitshdr, obsid, sizeof(obsid), NULL, 0, status);

    if (strcmp(obsid, "scuba2_00056_20110530T135530") == 0 ) {
      int subscan;
      smf_getfitsi( hdr, "NSUBSCAN", &subscan, status );
      if (subscan == 30 || subscan == 31 || subscan == 32) {
        size_t nframes = hdr->nframes;
        JCMTState * curstate;
        size_t i;
        for ( i=0; i<nframes; i++ ) {
          curstate = &((hdr->allState)[i]);
          curstate->jos_drcontrol |= DRCNTRL__PTCS_BIT;
        }
        msgOutif( msglev, "", INDENT "Blanking telescope data due to extreme excursion", status );
        have_fixed |= SMF__FIXED_JCMTSTATE;
      }
    }
  }

  /* The second half of observation 14 on 20111215 (scuba2_00014_20111215T061536)
     has a elevation pointing shift */
  if (fitsvals.utdate == 20111215) {
    char obsid[81];
    const char fitskey[] = "FIXPCORR";
    smf_getobsidss( hdr->fitshdr, obsid, sizeof(obsid), NULL, 0, status);

    if (strcmp(obsid, "scuba2_00014_20111215T061536") == 0 ) {
      int seqcount;
      smf_getfitsi( hdr, "SEQCOUNT", &seqcount, status );
      if (seqcount == 5) {
        int have_fixed_pntg = 0;
        smf_fits_getL( hdr, fitskey, &have_fixed_pntg, status );
        if (*status == SMF__NOKWRD) {
          have_fixed = 0;
          errAnnul( status );
        }
        if (!have_fixed_pntg) {
          size_t nframes = hdr->nframes;
          size_t i;
          const double dlon = 0.0;
          const double dlat = -16.83; /* From making maps of each half */
          /* Correct the pointing */
          msgOutif( msglev, "", INDENT "Applying pointing anomaly correction", status );
          for (i=0;i<nframes;i++) {
            JCMTState * curstate = &((hdr->allState)[i]);
            /* This is an AZEL correction */
            smf_add_smu_pcorr( curstate, 1, dlon, dlat, status );
          }
          smf_fits_updateL(hdr, fitskey, 1, "Applied internal pointing correction", status);
          have_fixed |= SMF__FIXED_JCMTSTATE;
        }
      }
    }

  }

  /* For POL-2 data prior to 18-JAN-2013, the POL_CRD header was always
     "FPLANE" in reality, even if the POL_CRD value in JCMTSTATE said
     something else. */
  if ( fitsvals.utdate < 20130118 ) {
    char polcrd[80] = "<unset>";
    smf_getfitss( hdr, "POL_CRD", polcrd, sizeof(polcrd), status );
    if (*status == SMF__NOKWRD) {
       errAnnul( status );
    } else if( !strcmp( polcrd, "TRACKING" ) || !strcmp( polcrd, "AZEL" ) ) {
      msgOutiff( msglev, "",  INDENT "Changing POL_CRD from %s to FPLANE", status, polcrd);
      smf_fits_updateS( hdr, "POL_CRD", "FPLANE",
                        "Coordinate system of polarimeter", status );
      have_fixed |= SMF__FIXED_FITSHDR;
    }
  }


  return have_fixed;
}
Exemple #2
0
int smf_fix_data ( msglev_t msglev, smfData * data, int * status ) {

  int have_fixed = 0;        /* Did we fix anything? */
  smfHead *hdr = NULL;       /* Data header struct */
  size_t i;                  /* Loop counter */
  size_t j;                  /* Loop counter */
  size_t k;                  /* Loop counter */
  size_t l;                  /* Loop counter */
  sc2ast_subarray_t subnum;  /* subarray number */
  int was_fixed = 0;         /* Were these data fixed before? */

  if (*status != SAI__OK) return have_fixed;

  /* Validate arguments - need smfData with smfHead including FITS
     and state */
  smf_validate_smfData( data, 1, 0, status );
  if (*status != SAI__OK) return have_fixed;

  hdr = data->hdr;
  smf_validate_smfHead( hdr, 1, 1, status );
  if (*status != SAI__OK) return have_fixed;

  /* Only works on SCUBA-2 time-series data */
  if( (hdr->instrument != INST__SCUBA2) ||
      (data->ndims != 3) ) {
    return have_fixed;
  }

  /* Need to at least have a DATA component. It might be a good idea
     to check if there is a file associated with the data, and check
     to see whether the caller mapped the DATA, VARIANCE, QUALITY. If
     they only mapped a subset of those, we could generate a warning that
     this routine may be introducing an inconsistency */
  if( !data->pntr[0] ) {
    return have_fixed;
  }

  /* Check to see if a FIXDATA FITS keyword exists -- if so, smf_fix_data
     has already been run on the data and we return. */
  smf_getfitsi( hdr, SMFFIXDATA, &was_fixed, status );
  if( *status == SMF__NOKWRD ) {
    errAnnul( status );
  } else {
    return have_fixed;
  }

  /* Fix the May/June 2011 s4a row readout order error. Check the
     subarray first, then get the MJD for the start of the observation
     to decide if we need to proceed. */

  smf_find_subarray( hdr, NULL, 0, &subnum, status );
  if( (*status == SAI__OK) && (subnum == S4A) ) {
    double dateendfix;       /* UTC MJD end date when fix needed */
    double dateobs;          /* UTC MJD observation start */
    double datestartfix;     /* UTS MJD start date when fix needed */
    AstTimeFrame *tf = NULL; /* time frame for date conversion */

    /* Get the MJD for start of the observation */
    smf_find_dateobs( hdr, &dateobs, NULL, status );

    /* Get the MJD for the first and last dates bracketing the period
       during which the row order fix needs to be done */

    tf = astTimeFrame( " " );
    astSet( tf, "TimeScale=UTC" );

    /* From Mike Macintosh Thu, 7 Jul 2011 15:07:51 +0000:

       "I did a scan of the engineering MCE status files and it looks
       the 'missing' row on s4a originated between 09:30 and 10:30 HST
       on 26 May 2011. I haven't checked in detail whether the
       'missing' row was consistently missing after that date but the
       assumption is that it was."
    */

    astSet( tf, "TimeOrigin=%s", "2011-05-26T19:00:00" );
    datestartfix = astGetD( tf, "TimeOrigin" );

    /* From Dan Bintley Wed, 06 Jul 2011 00:23:18 -1000:

       "I have changed the source code in

       /jac_sw/itsroot/scuba2_src/scuba2Da/setup/SG450_M1004D1000/mce_zero.txt"
    */

    astSet( tf, "TimeOrigin=%s", "2011-07-06T10:23:00" );
    dateendfix = astGetD( tf, "TimeOrigin" );

    tf = astAnnul( tf );

    /* Proceed with fix if within date range */
    if( (*status == SAI__OK) && (dateobs >= datestartfix) &&
        (dateobs <= dateendfix) ) {
      size_t targetbolo;
      size_t sourcebolo;
      size_t bstride;
      dim_t ncols;
      dim_t nrows;
      dim_t ntslice;
      size_t tstride;

      msgOutif( msglev, "", INDENT "Reparing row order readout error", status );

      smf_get_dims( data, &nrows, &ncols, NULL, &ntslice, NULL, &bstride,
                    &tstride, status );

      if( (*status==SAI__OK) && (nrows != 40) ) {
        *status = SAI__ERROR;
        errRep( "", FUNC_NAME ": looks like we need to fix the data, but we "
                "don't have 40 rows.", status );
      }

      /* Rows 0--13 are OK, row 14 was not read out
         (FIXDATA_S4A_MISSROW), so we need to move all of rows 14--38
         up to the range 15--39. After doing the shift, overwrite the
         missing row 14 with 0 (or should this be VAL__BAD? in some
         situations?) */

      if( *status==SAI__OK ) {
        /* Row counter */
        for( i=nrows-1; (*status==SAI__OK)&&(i>=(FIXDATA_S4A_MISSROW+1)); i-- ){
          for( j=0; (*status==SAI__OK)&&(j<ncols); j++ ) {
            smf_qual_t *qual = data->qual;

            /* Calculate the bolo index */
            if( SC2STORE__ROW_INDEX ) {
              /* Fastest changing index is column number */
              sourcebolo = (i-1)*ncols + j;
              targetbolo = i*ncols + j;
            } else {
              /* Fastest changing index is row number */
              sourcebolo = (i-1) + j*nrows;
              targetbolo = i + j*nrows;
            }

            /* Loop for DATA / VARIANCE components */
            for( l=0; (*status==SAI__OK)&&(l<2); l++ ) {
              void *buf = data->pntr[l];

              if( buf ) {
                switch( data->dtype ) {
                case SMF__INTEGER:
                  for( k=0; k<ntslice; k++ ) {
                    ((int *)buf)[targetbolo*bstride + k*tstride] =
                      ((int *)buf)[sourcebolo*bstride + k*tstride];
                  }

                  if( i==(FIXDATA_S4A_MISSROW+1) ) {
                    for( k=0; k<ntslice; k++ ) {
                      ((int *)buf)[sourcebolo*bstride + k*tstride] = 0;
                    }
                  }
                  break;
     
                case SMF__USHORT:
                  for( k=0; k<ntslice; k++ ) {
                    ((unsigned short *)buf)[targetbolo*bstride + k*tstride] =
                      ((unsigned short *)buf)[sourcebolo*bstride + k*tstride];
                  }

                  if( i==(FIXDATA_S4A_MISSROW+1) ) {
                    for( k=0; k<ntslice; k++ ) {
                      ((unsigned short *)buf)[sourcebolo*bstride + k*tstride] =
                        0;
                    }
                  }

                  break;
           
                case SMF__DOUBLE:
                  for( k=0; k<ntslice; k++ ) {
                    ((double *)buf)[targetbolo*bstride + k*tstride] =
                      ((double *)buf)[sourcebolo*bstride + k*tstride];
                  }

                  if( i==(FIXDATA_S4A_MISSROW+1) ) {
                    for( k=0; k<ntslice; k++ ) {
                      ((double *)buf)[sourcebolo*bstride + k*tstride] =
                        0;
                    }
                  }

                  break;

                default:
                  *status = SAI__ERROR;
                  errRepf( "", FUNC_NAME
                           ": Don't know how to handle %s type.", status,
                           smf_dtype_str(data->dtype,status) );
                }
              }
            }

            /* Now do quality: Just set SMF__Q_BADDA for the dead
               row. Not sure if I should do something with sidecar
               quality as well? */
            if( qual ) {
              for( k=0; k<ntslice; k++ ) {
                qual[targetbolo*bstride + k*tstride] =
                  qual[sourcebolo*bstride + k*tstride];
              }
              
              if( i==(FIXDATA_S4A_MISSROW+1) ) {
                for( k=0; k<ntslice; k++ ) {
                  qual[sourcebolo*bstride + k*tstride] = SMF__Q_BADDA;
                }
              }
            }



          }
        }
      }

      /* If we get here and status is OK, set return value */
      if( *status == SAI__OK ) {
        have_fixed |= SMF__FIXED_ROWORDER;
      }
    }
  }

  /* Set FITS header so that we know smf_fix_data was run */
  smf_fits_updateL( hdr, SMFFIXDATA, have_fixed,
                    (have_fixed ? "Data have been fixed" : "Data do not require fixing"),
                    status );

  return have_fixed;
}
Exemple #3
0
void smf_fit_qui( ThrWorkForce *wf, smfData *idata, smfData **odataq,
                  smfData **odatau, smfData **odatai, dim_t box, int ipolcrd,
                  int pasign, double paoff, double angrot, int north,
                  int *status ){

/* Local Variables: */
   AstFrameSet *wcs;        /* WCS FrameSet for current time slice */
   JCMTState *instate=NULL; /* Pointer to input JCMTState */
   JCMTState *outstate=NULL;/* Pointer to output JCMTState */
   const char *usesys;      /* Tracking system */
   dim_t *box_starts;       /* Array holding time slice at start of each box */
   dim_t box_size;          /* First time slice in box */
   dim_t intslice;          /* ntslice of idata */
   dim_t istart;            /* Input time index at start of fitting box */
   dim_t itime;             /* Time slice index */
   dim_t nbolo;             /* No. of bolometers */
   dim_t ncol;              /* No. of columns of bolometers in the array */
   dim_t ntime;             /* Time slices to check */
   dim_t ondata;            /* ndata of odata */
   dim_t ontslice;          /* ntslice of odata */
   double scale;            /* how much longer new samples are */
   int bstep;               /* Bolometer step between threads */
   int iworker;             /* Index of a worker thread */
   int nworker;             /* No. of worker threads */
   size_t i;                /* loop counter */
   smfData *indksquid=NULL; /* Pointer to input dksquid data */
   smfFitQUIJobData *job_data = NULL; /* Pointer to all job data */
   smfFitQUIJobData *pdata = NULL;/* Pointer to next job data */
   smfHead *hdr;            /* Pointer to data header this time slice */
   smf_qual_t *qua;         /* Input quality pointer */

/* Check inherited status */
   if( *status != SAI__OK ) return;

/* Check supplied arguments. */
   if( !idata || !odataq || !odatau ) {
      *status = SAI__ERROR;
      errRep( "", "smf_fit_qui: NULL inputs supplied", status );
      return;
   }

   if( idata->ndims != 3 ) {
      *status = SAI__ERROR;
      errRep( "", "smf_fit_qui: idata is not 3-dimensional", status );
      return;
   }

/* Ensure the supplied smfData is time-ordered. So "bstride" is 1 and "tstride"
   is nbolo. */
   smf_dataOrder( wf, idata, 1, status );

/* Dimensions of input. */
   smf_get_dims( idata, NULL, &ncol, &nbolo, &intslice, NULL, NULL, NULL,
                 status );

/* Store a pointer to the quality array for the input smfData. */
   qua = smf_select_qualpntr( idata, NULL, status );;

/* Go through the first thousand POL_ANG values to see if they are in
   units of radians (new data) or arbitrary encoder units (old data).
   They are assumed to be in radians if no POL_ANG value is larger than
   20. This function can only handle new data. */
   hdr = idata->hdr;
   instate = hdr->allState;
   ntime = ( intslice > 1000 ) ? 1000 : intslice;
   for( itime = 0; itime < ntime; itime++,instate++ ) {
      if( instate->pol_ang > 20 ) {
         *status = SAI__ERROR;
         errRep( " ","   POL2 data contains POL_ANG values in encoder "
                 "units - connot fit to such old data.", status );
         break;
      }
   }

/* Find the input time slice at which each fitting box starts, and the
   length of the output time axis (in time-slices). */
   smf1_find_boxes( intslice, hdr->allState, box, &ontslice, &box_starts,
                    status );

/* Time axis scaling factor. */
   scale = (double) intslice / (double) ontslice;

/* First copy everything from input to output except for the data that needs
   to be downsampled */

/* We want to copy everything in the smfHead except for allState. So we
   make a copy of the allState pointer, and then set it to NULL in the
   header before the copy */
   if( idata->hdr ) {
     instate = idata->hdr->allState;
     idata->hdr->allState = NULL;
   }

/* Similarly, we want everything in the smfDa except for the dksquid. */
   if( idata->da ) {
     indksquid = idata->da->dksquid;
     idata->da->dksquid = NULL;
   }

/* Create copies, storing them in the supplied  output smfData
   structures. Omit the header for U and I, as we will be copying the Q
   header into them.  */
   *odataq = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY,
                                0, 0, status );

   *odatau = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY |
                                SMF__NOCREATE_HEAD, 0, 0, status );

   if( odatai ) {
      *odatai = smf_deepcopy_smfData( wf, idata, 0, SMF__NOCREATE_DATA |
                                SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY |
                                SMF__NOCREATE_HEAD, 0, 0, status );
   }

/* Restore values in idata now that we're done */
   if( instate ) idata->hdr->allState = instate;
   if( indksquid ) idata->da->dksquid = indksquid;

/* Store the required length for the output time axis. The time axis is
   axis two because the data is time-ordered. */
   (*odataq)->dims[ 2 ] = ontslice;
   (*odatau)->dims[ 2 ] = ontslice;
   if( odatai) (*odatai)->dims[ 2 ] = ontslice;

/* Get output dimensions - assumed to be the same for all three outputs. */
   ondata = ontslice*idata->dims[0]*idata->dims[1];

/* Allocate the data arrays for the outputs. */
   (*odataq)->pntr[0] = astCalloc( ondata, sizeof(double) );
   (*odatau)->pntr[0] = astCalloc( ondata, sizeof(double) );
   if( odatai ) (*odatai)->pntr[0] = astCalloc( ondata, sizeof(double) );

/* Allocate arrays for the output variances. */
   (*odataq)->pntr[1] = astCalloc( ondata, sizeof(double) );
   (*odatau)->pntr[1] = astCalloc( ondata, sizeof(double) );
   if( odatai ) (*odatai)->pntr[1] = astCalloc( ondata, sizeof(double) );

/* Create structures used to pass information to the worker threads. */
   nworker = wf ? wf->nworker : 1;
   job_data = astMalloc( nworker*sizeof( *job_data ) );
   if( *status == SAI__OK ) {

/* Determine which bolometers are to be processed by which threads. */
      bstep = nbolo/nworker;
      if( bstep < 1 ) bstep = 1;

      for( iworker = 0; iworker < nworker; iworker++ ) {
         pdata = job_data + iworker;
         pdata->b1 = iworker*bstep;
         pdata->b2 = pdata->b1 + bstep - 1;
      }

/* Ensure that the last thread picks up any left-over bolometers */
      pdata->b2 = nbolo - 1;

/* Loop round all output time slices. */
      for( itime = 0; itime < ontslice; itime++ ) {

/* Get the index of the first input time slice that contributes to the
   current output time slice. */
         istart = box_starts[ itime ];

/* Get the number of input time slices that contribute to the output time
   slice. */
         box_size = box_starts[ itime + 1 ] - istart;

/* If we are using north as the reference direction, get the WCS FrameSet
   for the input time slice that is at the middle of the output time
   slice, and set its current Frame to the tracking frame. */
         if( north ) {
            smf_tslice_ast( idata, istart + box_size/2, 1, NO_FTS, status );
            wcs = idata->hdr->wcs;
            usesys = sc2ast_convert_system( (idata->hdr->allState)[0].tcs_tr_sys,
                                            status );
            astSetC( wcs, "System", usesys );
         } else {
            wcs = NULL;
         }

/* Now enter the parellel code in which each thread calculates the values
   for a range of bolometers at the current output slice. */
         for( iworker = 0; iworker < nworker; iworker++ ) {
            pdata = job_data + iworker;

            pdata->dat = ((double *) idata->pntr[0] ) + istart*nbolo;
            pdata->qua = qua + istart*nbolo;
            pdata->allstates = hdr->allState + istart;

            pdata->ipi = odatai ? ( (double*) (*odatai)->pntr[0] ) + itime*nbolo : NULL;
            pdata->ipq = ( (double*) (*odataq)->pntr[0] ) + itime*nbolo;
            pdata->ipu = ( (double*) (*odatau)->pntr[0] ) + itime*nbolo;
            pdata->ipv = ( (double*) (*odataq)->pntr[1] ) + itime*nbolo;

            pdata->nbolo = nbolo;
            pdata->ncol = ncol;
            pdata->box_size = box_size;
            pdata->ipolcrd = ipolcrd;
            pdata->pasign = pasign ? +1: -1;
            pdata->paoff = paoff;
            pdata->angrot = angrot;
            if( wcs ) {
               pdata->wcs = astCopy( wcs );
               astUnlock( pdata->wcs, 1 );
            } else {
               pdata->wcs = NULL;
            }

/* Pass the job to the workforce for execution. */
            thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_fit_qui_job, 0, NULL,
                         status );
         }

/* Wait for the workforce to complete all jobs. */
         thrWait( wf, status );

/* Lock and annul the AST objects used by each thread. */
         if( wcs ) {
            for( iworker = 0; iworker < nworker; iworker++ ) {
               pdata = job_data + iworker;
               astLock( pdata->wcs, 0 );
               pdata->wcs = astAnnul( pdata->wcs );
            }
         }
      }

/* Down-sample the smfHead -------------------------------------------------*/
      smfHead *hdr = (*odataq)->hdr;

      hdr->curframe = (dim_t) (((double) hdr->curframe + 0.5) / scale);
      hdr->nframes = ontslice;
      hdr->steptime *= scale;
      strcpy( hdr->dlabel, "Q" );
      strncpy( hdr->title, "POL-2 Stokes parameter Q", SMF__CHARLABEL );

/* Down-sample all the JCMTState values using nearest neighbours */
      instate = idata->hdr->allState;
      if( instate ) {

         hdr->allState = astCalloc( ontslice, sizeof(*instate) );
         outstate = hdr->allState;

         if( *status == SAI__OK ) {
            size_t frame;  /* index of nearest neighbour JCMTState */

            for( i=0; i<ontslice; i++ ) {
               frame = (size_t) round(((double) i + 0.5)*scale);
               memcpy( outstate + i, instate + frame, sizeof(*instate) );
            }

/* Then go back and properly down-sample the more important fast-changing
   fields like pointing. Note that since there are approximate values there
   already we need to explicitly re-initialize to 0. */

            RESAMPSTATE(instate, outstate, rts_end, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, smu_az_jig_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_jig_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_chop_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_az_chop_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_jig_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_jig_y, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_chop_x, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, smu_tr_chop_y, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_tai, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_airmass, intslice, ontslice, 0);

/* Second coordinates (Dec, El etc) can not wrap 0 to 360 so we do not need
   to test for those cases */
            RESAMPSTATE(instate, outstate, tcs_az_ang, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_ac1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_ac2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_az_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_dc2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_az_bc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_az_bc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_tr_ang, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_ac1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_ac2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_tr_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_dc2, intslice, ontslice, 0);
            RESAMPSTATE(instate, outstate, tcs_tr_bc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_tr_bc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_en_dc1, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_en_dc2, intslice, ontslice, 0);

            RESAMPSTATE(instate, outstate, tcs_dm_abs, intslice, ontslice, 1);
            RESAMPSTATE(instate, outstate, tcs_dm_rel, intslice, ontslice, 0);

/* Wait for all the above smf_downsamp1 jobs to finish. */
            thrWait( wf, status );

         }
      }

/* Add a keyword to the Q header indicating the polarimetric reference
   direction. */
      smf_fits_updateL( (*odataq)->hdr, "POLNORTH", north,
                        north ? "Pol ref dir is tracking north" :
                                "Pol ref dir is focal plane Y", status );

/* Copy the Q header to the other outputs. */
      hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status );
      (*odatau)->hdr = hdr;
      if( *status == SAI__OK ) {
         strcpy( hdr->dlabel, "U" );
         strncpy( hdr->title, "POL-2 Stokes parameter U", SMF__CHARLABEL );
      }

      if( odatai ) {
         hdr = smf_deepcopy_smfHead( (*odataq)->hdr, status );
         (*odatai)->hdr = hdr;
         if( *status == SAI__OK ) {
            strcpy( hdr->dlabel, "I" );
            strncpy( hdr->title, "POL-2 Stokes parameter I", SMF__CHARLABEL );
         }
      }
   }

/* Copy the variances from the Q smfData into the U and (and I) smfData. */
   if( *odataq && *status == SAI__OK ) {
      if( *odatau ) {
         memcpy( (*odatau)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double));
      }
      if( odatai && *odatai ) {
         memcpy( (*odatai)->pntr[1], (*odataq)->pntr[1], ondata*sizeof(double));
      }
   }

/* Ensure all smfDatas are time-ordered. */
   smf_dataOrder( wf, idata, 1, status );
   if( odatai && *odatai ) smf_dataOrder( wf, *odatai, 1, status );
   if( *odataq ) smf_dataOrder( wf, *odataq, 1, status );
   if( *odatau ) smf_dataOrder( wf, *odatau, 1, status );

/* Free resources. */
   job_data = astFree( job_data );
   box_starts = astFree( box_starts );
}