예제 #1
0
void smurf_sc2fft( int *status ) {

  int avpspec=0;            /* Flag for doing average power spectrum */
  double avpspecthresh=0;   /* Threshold noise for detectors in avpspec */
  Grp * basegrp = NULL;     /* Basis group for output filenames */
  smfArray *bbms = NULL;    /* Bad bolometer masks */
  smfArray *concat=NULL;    /* Pointer to a smfArray */
  size_t contchunk;         /* Continuous chunk counter */
  smfArray *darks = NULL;   /* dark frames */
  int ensureflat;           /* Flag for flatfielding data */
  Grp *fgrp = NULL;         /* Filtered group, no darks */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t gcount=0;          /* Grp index counter */
  size_t i;                 /* Loop counter */
  smfGroup *igroup=NULL;    /* smfGroup corresponding to igrp */
  Grp *igrp = NULL;         /* Input group of files */
  int inverse=0;            /* If set perform inverse transform */
  int isfft=0;              /* Are data fft or real space? */
  dim_t maxconcat=0;        /* Longest continuous chunk length in samples */
  size_t ncontchunks=0;     /* Number continuous chunks outside iter loop */
  smfData *odata=NULL;      /* Pointer to output smfData to be exported */
  Grp *ogrp = NULL;         /* Output group of files */
  size_t outsize;           /* Total number of NDF names in the output group */
  int polar=0;              /* Flag for FFT in polar coordinates */
  int power=0;              /* Flag for squaring amplitude coeffs */
  size_t size;              /* Number of files in input group */
  smfData *tempdata=NULL;   /* Temporary smfData pointer */
  int weightavpspec=0;      /* Flag for 1/noise^2 weighting */
  ThrWorkForce *wf = NULL;  /* Pointer to a pool of worker threads */
  int zerobad;              /* Zero VAL__BADD before taking FFT? */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  /* We now need to combine files from the same subarray and same sequence
     to form a continuous time series */
  smf_grp_related( igrp, size, 1, 0, 0, NULL, NULL, &maxconcat, NULL, &igroup,
                   &basegrp, NULL, status );

  /* Get output file(s) */
  size = grpGrpsz( basegrp, status );
  if( size > 0 ) {
    kpg1Wgndf( "OUT", basegrp, size, size, "More output files required...",
               &ogrp, &outsize, status );
  } else {
    msgOutif(MSG__NORM, " ", TASK_NAME ": All supplied input frames were DARK,"
             " nothing to do", status );
  }

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( "BBM", &bbms, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }
  msgOutiff( MSG__NORM, "", "Found %zu continuous chunk%s", status, ncontchunks,
             (ncontchunks > 1 ? "s" : "") );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Are we doing an inverse transform? */
  parGet0l( "INVERSE", &inverse, status );

  /* Are we using polar coordinates instead of cartesian for the FFT? */
  parGet0l( "POLAR", &polar, status );

  /* Are we going to assume amplitudes are squared? */
  parGet0l( "POWER", &power, status );

  /* Are we going to zero bad values first? */
  parGet0l( "ZEROBAD", &zerobad, status );

  /* Are we calculating the average power spectrum? */
  parGet0l( "AVPSPEC", &avpspec, status );

  if( avpspec ) {
    power = 1;
    parGet0d( "AVPSPECTHRESH", &avpspecthresh, status );

    parGet0l( "WEIGHTAVPSPEC", &weightavpspec, status );
  }

  /* If power is true, we must be in polar form */
  if( power && !polar) {
    msgOutif( MSG__NORM, " ", TASK_NAME
              ": power spectrum requested so setting POLAR=TRUE", status );
    polar = 1;
  }

  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {
    size_t idx;

    /* Concatenate this continuous chunk but forcing a raw data read.
       We will need quality. */
    smf_concat_smfGroup( wf, NULL, igroup, darks, NULL, flatramps, heateffmap,
                         contchunk, ensureflat, 1, NULL, 0, NULL, NULL, 0, 0, 0,
                         &concat, NULL, status );

    /* Now loop over each subarray */
    /* Export concatenated data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      if( concat->sdata[idx] ) {
        smfData * idata = concat->sdata[idx];
        int provid = NDF__NOID;
        dim_t nbolo;                /* Number of detectors  */
        dim_t ndata;                /* Number of data points */

        /* Apply a mask to the quality array and data array */
        smf_apply_mask( idata, bbms, SMF__BBM_QUAL|SMF__BBM_DATA, 0, status );

        smf_get_dims( idata,  NULL, NULL, &nbolo, NULL, &ndata, NULL, NULL,
                      status );


        /* Check for double precision data */
        if( idata->dtype != SMF__DOUBLE ) {
          *status = SAI__ERROR;
          errRep( "", FUNC_NAME ": data are not double precision.", status );
        }

        /* Are we zeroing VAL__BADD? */
        if( (*status==SAI__OK) && zerobad ) {
          double *data= (double *) idata->pntr[0];

          for( i=0; i<ndata; i++ ) {
            if( data[i] == VAL__BADD ) {
              data[i] = 0;
            }
          }
        }

        /* Check whether we need to transform the data at all */
        isfft = smf_isfft(idata,NULL,NULL,NULL,NULL,NULL,status);

        if( isfft && avpspec && (*status == SAI__OK) ) {
          *status = SAI__ERROR;
          errRep( "", FUNC_NAME
                  ": to calculate average power spectrum input data cannot "
                  "be FFT", status );
        }

        if( (*status == SAI__OK) && (isfft == inverse) ) {

          if( avpspec ) {
            /* If calculating average power spectrum do the transforms with
               smf_bolonoise so that we can also measure the noise of
               each detector */

            double *whitenoise=NULL;
            smf_qual_t *bolomask=NULL;
            double mean, sig, freqlo;
            size_t ngood, newgood;

            whitenoise = astCalloc( nbolo, sizeof(*whitenoise) );
            bolomask = astCalloc( nbolo, sizeof(*bolomask) );

	    freqlo = 1. / (idata->hdr->steptime * idata->hdr->nframes);

            smf_bolonoise( wf, idata, 1, freqlo, SMF__F_WHITELO,
                           SMF__F_WHITEHI, 1, 0, whitenoise, NULL, &odata,
                           status );

            /* Initialize quality */
            for( i=0; i<nbolo; i++ ) {
              if( whitenoise[i] == VAL__BADD ) {
                bolomask[i] = SMF__Q_BADB;
              } else {
                /* smf_bolonoise returns a variance, so take sqrt */
                whitenoise[i] = sqrt(whitenoise[i]);
              }
            }

            ngood=-1;
            newgood=0;

            /* Iteratively cut n-sigma noisy outlier detectors */
            while( ngood != newgood ) {
              ngood = newgood;
              smf_stats1D( whitenoise, 1, nbolo, bolomask, 1, SMF__Q_BADB,
                           &mean, &sig, NULL, NULL, status );
              msgOutiff( MSG__DEBUG, "", TASK_NAME
                         ": mean=%lf sig=%lf ngood=%li\n", status,
                         mean, sig, ngood);

              newgood=0;
              for( i=0; i<nbolo; i++ ) {
                if( whitenoise[i] != VAL__BADD ){
                  if( (whitenoise[i] - mean) > avpspecthresh *sig ) {
                    whitenoise[i] = VAL__BADD;
                    bolomask[i] = SMF__Q_BADB;
                  } else {
                    newgood++;
                  }
                }
              }
            }

            msgOutf( "", TASK_NAME
                     ": Calculating average power spectrum of best %li "
                     " bolometers.", status, newgood);

            /* If using 1/noise^2 weights, calculate 1/whitenoise^2 in-place
               to avoid allocating another array */
            if( weightavpspec ) {
              msgOutif( MSG__VERB, "", TASK_NAME ": using 1/noise^2 weights",
                        status );

              for( i=0; i<nbolo; i++ ) {
                if( whitenoise[i] && (whitenoise[i] != VAL__BADD) ) {
                  whitenoise[i] = 1/(whitenoise[i]*whitenoise[i]);
                }
              }
            }

            /* Calculate the average power spectrum of good detectors */
            tempdata = smf_fft_avpspec( odata, bolomask, 1, SMF__Q_BADB,
                                        weightavpspec ? whitenoise : NULL,
                                        status );
            smf_close_file( &odata, status );
            whitenoise = astFree( whitenoise );
            bolomask = astFree( bolomask );
            odata = tempdata;
            tempdata = NULL;
	    /* Store the number of good bolometers */
	    parPut0i( "NGOOD", newgood, status );
          } else {
            /* Otherwise do forward/inverse transforms here as needed */

            /* If inverse transform convert to cartesian representation first */
            if( inverse && polar ) {
              smf_fft_cart2pol( wf, idata, 1, power, status );
            }

            /* Tranform the data */
            odata = smf_fft_data( wf, idata, NULL, inverse, 0, status );
            smf_convert_bad( wf, odata, status );

            if( inverse ) {
              /* If output is time-domain, ensure that it is ICD bolo-ordered */
              smf_dataOrder( odata, 1, status );
            } else if( polar ) {
              /* Store FFT of data in polar form */
              smf_fft_cart2pol( wf, odata, 0, power, status );
            }
          }

          /* open a reference input file for provenance propagation */
          ndgNdfas( basegrp, gcount, "READ", &provid, status );

          /* Export the data to a new file */
          smf_write_smfData( odata, NULL, NULL, ogrp, gcount, provid,
                             MSG__VERB, 0, status );

          /* Free resources */
          ndfAnnul( &provid, status );
          smf_close_file( &odata, status );
        } else {
          msgOutif( MSG__NORM, " ",
                    "Data are already transformed. No output will be produced",
                    status );
        }
      }

      /* Update index into group */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( &concat, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines */
  grpDelet( &igrp, status);
  grpDelet( &ogrp, status);
  if (basegrp) grpDelet( &basegrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if (bbms) smf_close_related( &bbms, status );

  ndfEnd( status );

  /* Ensure that FFTW doesn't have any used memory kicking around */
  fftw_cleanup();
}
예제 #2
0
void smurf_sc2clean( int *status ) {
  smfArray *array = NULL;    /* Data to be cleaned */
  Grp *basegrp=NULL;         /* Grp containing first file each chunk */
  size_t basesize;           /* Number of files in base group */
  smfArray *bbms = NULL;     /* Bad bolometer masks */
  smfArray *concat=NULL;     /* Pointer to a smfArray */
  size_t contchunk;          /* Continuous chunk counter */
  smfArray *darks = NULL;    /* Dark data */
  int ensureflat;            /* Flag for flatfielding data */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  smfData *odata = NULL;     /* Pointer to output data struct */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  size_t gcount=0;           /* Grp index counter */
  size_t idx;                /* Subarray counter */
  Grp *igrp = NULL;          /* Input group of files */
  smfGroup *igroup=NULL;     /* smfGroup corresponding to igrp */
  dim_t maxconcat=0;         /* Longest continuous chunk length in samples */
  double maxlen=0;           /* Constrain maxconcat to this many seconds */
  size_t ncontchunks=0;      /* Number continuous chunks outside iter loop */
  Grp *ogrp = NULL;          /* Output group of files */
  size_t osize;              /* Total number of NDF names in the output group */
  dim_t padStart=0;          /* How many samples padding at start */
  dim_t padEnd=0;            /* How many samples padding at end */
  size_t size;               /* Number of files in input group */
  int temp;                  /* Temporary signed integer */
  int usedarks;              /* flag for using darks */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */
  int writecom;              /* Write COMmon mode to NDF if calculated? */
  int writegai;              /* Write GAIns to NDF if calculated? */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( wf, igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (size == 0) {
    msgOutif(MSG__NORM, " ","All supplied input frames were filtered,"
       " nothing to do", status );
    goto CLEANUP;
  }

  /* --- Parse ADAM parameters ---------------------------------------------- */

  /* Maximum length of a continuous chunk */
  parGdr0d( "MAXLEN", 0, 0, VAL__MAXD, 1, &maxlen, status );

  /* Padding */
  parGdr0i( "PADSTART", 0, 0, VAL__MAXI, 1, &temp, status );
  padStart = (dim_t) temp;

  parGdr0i( "PADEND", 0, 0, VAL__MAXI, 1, &temp, status );
  padEnd = (dim_t) temp;

  /* Are we using darks? */
  parGet0l( "USEDARKS", &usedarks, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Write COM/GAI to NDFs if calculated? */
  parGet0l( "COM", &writecom, status );
  parGet0l( "GAI", &writegai, status );

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( wf, "BBM", &bbms, status );

  /* Group the input files by subarray and continuity ----------------------- */
  smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, NULL,
                   &maxconcat, NULL, &igroup, &basegrp, NULL, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }

  basesize = grpGrpsz( basegrp, status );

  /* Get output file(s) */
  kpg1Wgndf( "OUT", basegrp, basesize, basesize,
             "More output files required...",
             &ogrp, &osize, status );

  /* Loop over continuous chunks and clean -----------------------------------*/
  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {
    AstKeyMap *keymap=NULL;
    int dkclean;
    AstKeyMap *sub_instruments=NULL;

    /* Place cleaning parameters into a keymap and set defaults. Do
       this inside the loop in case we are cleaning files with
       differing sub-instruments.  Note that we use the map-maker
       defaults file here (which loads the sc2clean defaults) so that
       we populate the locked keymap with all the parameters that
       people may come across to allow them to load their map-maker
       config directly into sc2clean.
    */

    sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE,
                                          NULL, igrp,
                                          igroup->subgroups[contchunk][0],
                                          status );

    keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def",
                         sub_instruments, 1, status );
    if( sub_instruments ) sub_instruments = astAnnul( sub_instruments );

    /* Now rerun smf_grp_related to figure out how long each downsampled
       chunk of data will be. */

    if( basegrp ) grpDelet( &basegrp, status );
    if( igroup ) smf_close_smfGroup( &igroup, status );

    smf_grp_related( igrp, size, 1, 0, maxlen-padStart-padEnd, NULL, keymap,
                     &maxconcat, NULL, &igroup, &basegrp, NULL, status );

    /* Concatenate this continuous chunk */
    smf_concat_smfGroup( wf, NULL, igroup, usedarks ? darks:NULL, bbms, flatramps,
                         heateffmap, contchunk, ensureflat, 1, NULL, 0, NULL,
                         NULL, NO_FTS, padStart, padEnd, 0, &concat, NULL, status );

    if( *status == SAI__OK) {
      /* clean the dark squids now since we might need to use them
         to clean the bolometer data */

      smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        &dkclean, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, status );

      for( idx=0; dkclean&&(*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
        odata = concat->sdata[idx];

        if( odata && odata->da && odata->da->dksquid ) {
          smfData *dksquid = odata->da->dksquid;
          AstKeyMap *kmap=NULL;

          msgOut("", TASK_NAME ": cleaning dark squids", status);

          /* fudge the header so that we can get at JCMTState */
          dksquid->hdr = odata->hdr;

          /* clean darks using cleandk.* parameters */
          astMapGet0A( keymap, "CLEANDK", &kmap );
          array = smf_create_smfArray( status );
          smf_addto_smfArray( array, dksquid, status );
          smf_clean_smfArray( wf, array, NULL, NULL, NULL, kmap, status );
          if( array ) {
            array->owndata = 0;
            smf_close_related( wf, &array, status );
          }
          if( kmap ) kmap = astAnnul( kmap );

          /* Unset hdr pointer so that we don't accidentally close it */
          dksquid->hdr = NULL;
        }
      }

      /* Then the main data arrays */
      if( *status == SAI__OK ) {
        smfArray *com = NULL;
        smfArray *gai = NULL;
        char filename[GRP__SZNAM+1];

        msgOut("", TASK_NAME ": cleaning bolometer data", status );
        smf_clean_smfArray( wf, concat, NULL, &com, &gai, keymap, status );

        /* If ADAM parameters for COM or GAI were specified, and the
           common-mode was calculated, export to files here */

        if( writecom && com ) {
          for( idx=0; (*status==SAI__OK)&&(idx<com->ndat); idx++ ) {
            smf_model_createHdr( com->sdata[idx], SMF__COM, concat->sdata[idx],
                                 status );
            smf_stripsuffix( com->sdata[idx]->file->name,
                             SMF__DIMM_SUFFIX, filename, status );

            smf_dataOrder( wf, com->sdata[idx], 1, status );

            smf_write_smfData( wf, com->sdata[idx], NULL, filename, NULL, 0,
                               NDF__NOID, MSG__NORM, 0, NULL, NULL, status );
          }
        }

        if( writegai && gai ) {
          for( idx=0; (*status==SAI__OK)&&(idx<gai->ndat); idx++ ) {
            smf_model_createHdr( gai->sdata[idx], SMF__GAI, concat->sdata[idx],
                                 status );
            smf_stripsuffix( gai->sdata[idx]->file->name,
                             SMF__DIMM_SUFFIX, filename, status );

            smf_dataOrder( wf, gai->sdata[idx], 1, status );
            smf_write_smfData( wf, gai->sdata[idx], NULL, filename, NULL, 0,
                               NDF__NOID, MSG__NORM, 0, NULL, NULL, status );
          }
        }

        /* Close com and gai */
        if( com ) smf_close_related( wf, &com, status );
        if( gai ) smf_close_related( wf, &gai, status );

      }

      /* Report statistics (currently need a smfArray for that) */
      if (*status == SAI__OK) {
        size_t last_qcount[SMF__NQBITS];
        size_t last_nmap = 0;
        smf_qualstats_report( wf, MSG__VERB, SMF__QFAM_TSERIES, 1, concat,
                              last_qcount, &last_nmap, 1, NULL, NULL, status );
      }

      /* Clean up for contchunk loop */
      if( keymap ) keymap = astAnnul( keymap );
    }

    /* Export concatenated/cleaned data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      odata = concat->sdata[idx];

      /* Complete the history information in the output NDF so that it
         includes group parameters accessed since the default history
         information was written to the NDF (in smf_open_and_flatfield). */
      smf_puthistory( odata, "SMURF:SC2CLEAN", status );

      /* Ensure ICD data order */
      smf_dataOrder( wf, odata, 1, status );

      if( odata->file && odata->file->name ) {
        smf_write_smfData( wf, odata, NULL, NULL, ogrp, gcount, NDF__NOID,
                           MSG__VERB, 0, NULL, NULL, status );
      } else {
        *status = SAI__ERROR;
        errRep( FUNC_NAME,
                "Unable to determine file name for concatenated data.",
                status );
      }

      /* Increment the group index counter */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( wf, &concat, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

 CLEANUP:

  /* Tidy up after ourselves: release the resources used by the grp routines */
  if( darks ) smf_close_related( wf, &darks, status );
  if( flatramps ) smf_close_related( wf, &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if( bbms ) smf_close_related( wf, &bbms, status );
  if( igrp ) grpDelet( &igrp, status);
  if( ogrp ) grpDelet( &ogrp, status);
  if( basegrp ) grpDelet( &basegrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );
  fftw_cleanup();
  ndfEnd( status );
}
예제 #3
0
void smurf_sc2pca( int *status ) {

  smfData *amplitudes=NULL;  /* Amplitudes of each component */
  smfArray *bbms=NULL;       /* Bad bolometer masks */
  smfData *components=NULL;  /* Components */
  smfArray *darks=NULL ;     /* Dark data */
  int ensureflat;            /* Flag for flatfielding data */
  smfData *data=NULL;        /* Pointer to input smfData */
  Grp *fgrp=NULL;            /* Filtered group, no darks */
  smfArray *flatramps=NULL;  /* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t i=0;                /* Counter, index */
  Grp *igrp=NULL;            /* Input group of files */
  Grp *outampgrp=NULL;       /* Output amplitude group of files */
  Grp *outcompgrp=NULL;      /* Output component group of files */
  size_t outampsize;         /* Total number of NDF names in ocompgrp */
  size_t outcompsize;        /* Total number of NDF names in ocompgrp */
  size_t size;               /* Number of files in input group */
  ThrWorkForce *wf=NULL;     /* Pointer to a pool of worker threads */

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Filter out useful data (revert to darks if no science data) */
  smf_find_science( wf, igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if( size > 0 ) {
    /* Get output file(s) */
    kpg1Wgndf( "OUTAMP", igrp, size, size, "More output files required...",
               &outampgrp, &outampsize, status );

    kpg1Wgndf( "OUTCOMP", igrp, size, size, "More output files required...",
               &outcompgrp, &outcompsize, status );
  } else {
    msgOutif(MSG__NORM, " ","All supplied input frames were DARK,"
       " nothing to flatfield", status );
  }

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( wf, "BBM", &bbms, status );

  for( i=1; i<=size; i++ ) {

    if( *status != SAI__OK ) break;

    /* Load data, flatfielding and/or opening raw as double as necessary */
    smf_open_asdouble( wf, igrp, i, darks, flatramps, heateffmap, ensureflat, &data, status );

    /* Mask out bad bolometers */
    smf_apply_mask( wf, data, bbms, SMF__BBM_DATA|SMF__BBM_QUAL, 0, status );

    /* Sync quality with bad values */
    smf_update_quality( wf, data, 1, NULL, 0, 0.05, status );

    /* Calculate the PCA */
    smf_clean_pca( wf, data, 0, 0, 0, &components, &amplitudes, 0, 1, NULL,
                   status );

    /* Write out to the new files */
    smf_write_smfData( wf, amplitudes, NULL, NULL, outampgrp, i, 0, MSG__VERB,
                       0, status );
    smf_write_smfData( wf, components, NULL, NULL, outcompgrp, i, 0, MSG__VERB,
                       0, status );

    /* Free resources for output data */
    smf_close_file( wf, &data, status );
    smf_close_file( wf, &amplitudes, status );
    smf_close_file( wf, &components, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && outampgrp ) {
    grpList( "OUTAMPFILES", 0, 0, NULL, outampgrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }
  if( *status == SAI__OK && outcompgrp ) {
    grpList( "OUTCOMPFILES", 0, 0, NULL, outcompgrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines  */
  if( igrp ) grpDelet( &igrp, status);
  if( outampgrp ) grpDelet( &outampgrp, status);
  if( outcompgrp ) grpDelet( &outcompgrp, status);
  if( darks ) smf_close_related( wf, &darks, status );
  if( bbms ) smf_close_related( wf, &bbms, status );
  if( flatramps ) smf_close_related( wf, &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  ndfEnd( status );
}
예제 #4
0
void smurf_calcqu( int *status ) {

    /* Local Variables: */
    AstFitsChan *fc;           /* Holds FITS headers for output NDFs */
    AstKeyMap *config;         /* Holds all cleaning parameters */
    AstKeyMap *dkpars;         /* Holds dark squid cleaning parameters */
    AstKeyMap *heateffmap = NULL; /* Heater efficiency data */
    AstKeyMap *sub_instruments;/* Indicates which instrument is being used */
    Grp *bgrp = NULL;          /* Group of base names for each chunk */
    Grp *igrp = NULL;          /* Group of input files */
    Grp *ogrp = NULL;          /* Group of output files  */
    Grp *sgrp = NULL;          /* Group of science files */
    HDSLoc *loci = NULL;       /* Locator for output I container file */
    HDSLoc *locq = NULL;       /* Locator for output Q container file */
    HDSLoc *locu = NULL;       /* Locator for output U container file */
    NdgProvenance *oprov;      /* Provenance to store in each output NDF */
    ThrWorkForce *wf;          /* Pointer to a pool of worker threads */
    char headval[ 81 ];        /* FITS header value */
    char ndfname[ 30 ];        /* Name of output Q or U NDF */
    char polcrd[ 81 ];         /* FITS 'POL_CRD' header value */
    char subarray[ 10 ];       /* Subarray name (e.g. "s4a", etc) */
    double angrot;             /* Angle from focal plane X axis to fixed analyser */
    double paoff;              /* WPLATE value corresponding to POL_ANG=0.0 */
    float arcerror;            /* Max acceptable error (arcsec) in one block */
    int block_end;             /* Index of last time slice in block */
    int block_start;           /* Index of first time slice in block */
    int dkclean;               /* Clean dark squids? */
    int fix;                   /* Fix the POL-2 triggering issue? */
    int iblock;                /* Index of current block */
    int iplace;                /* NDF placeholder for current block's I image */
    int ipolcrd;               /* Reference direction for waveplate angles */
    int maxsize;               /* Max no. of time slices in a block */
    int minsize;               /* Min no. of time slices in a block */
    int nc;                    /* Number of characters written to a string */
    int pasign;                /* +1 or -1 indicating sense of POL_ANG value */
    int qplace;                /* NDF placeholder for current block's Q image */
    int submean;               /* Subtract mean value from each time slice? */
    int uplace;                /* NDF placeholder for current block's U image */
    size_t ichunk;             /* Continuous chunk counter */
    size_t idx;                /* Subarray counter */
    size_t igroup;             /* Index for group of related input NDFs */
    size_t inidx;              /* Index into group of science input NDFs */
    size_t nchunk;             /* Number continuous chunks outside iter loop */
    size_t ssize;              /* Number of science files in input group */
    smfArray *concat = NULL;   /* Pointer to smfArray holding bolometer data */
    smfArray *darks = NULL;    /* dark frames */
    smfArray *dkarray = NULL;  /* Pointer to smfArray holding dark squid data */
    smfArray *flatramps = NULL;/* Flatfield ramps */
    smfData *data = NULL;      /* Concatenated data for one subarray */
    smfData *dkdata = NULL;    /* Concatenated dark squid data for one subarray */
    smfGroup *sgroup = NULL;   /* smfGroup corresponding to sgrp */

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

    /* Start new AST and NDF contexts. */
    astBegin;
    ndfBegin();

    /* Find the number of cores/processors available and create a work force
       holding the same number of threads. */
    wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

    /* Get a group of input files */
    kpg1Rgndf( "IN", 0, 1, "  Give more NDFs...", &igrp, &ssize, status );

    /* Get a group containing just the files holding science data. */
    smf_find_science( igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks,
                      &flatramps, &heateffmap, NULL, status );

    /* Check we have at least once science file. */
    ssize = grpGrpsz( sgrp, status );
    if( ssize == 0 ) {
        msgOutif( MSG__NORM, " ", "All supplied input frames were DARK.",
                  status );
    } else {

        /* See if a correction should be made for the POL2 triggering issue. */
        parGet0l( "FIX", &fix, status );

        /* Create HDS container files to hold the output NDFs. */
        datCreat( "OUTQ", "CALCQU", 0, 0, status );
        datCreat( "OUTU", "CALCQU", 0, 0, status );

        /* Associate the locators with the structures. */
        datAssoc( "OUTQ", "WRITE", &locq, status );
        datAssoc( "OUTU", "WRITE", &locu, status );

        /* The I images are optional. */
        if( *status == SAI__OK ) {
            datCreat( "OUTI", "CALCQU", 0, 0, status );
            datAssoc( "OUTI", "WRITE", &loci, status );
            if( *status == PAR__NULL ) {
                errAnnul( status );
                loci = NULL;
            }
        }

        /* Group the input files so that all files within a single group have the
           same wavelength and belong to the same subscan of the same observation.
           Also identify chunks of data that are contiguous in time, and
           determine to which such chunk each group belongs. All this information
           is returned in a smfGroup structure ("*sgroup"). */
        smf_grp_related( sgrp, ssize, 1, 1, 0, NULL, NULL, NULL,
                         NULL, &sgroup, &bgrp, NULL, status );

        /* Obtain the number of contiguous chunks. */
        if( *status == SAI__OK ) {
            nchunk = sgroup->chunk[ sgroup->ngroups - 1 ] + 1;
        } else {
            nchunk = 0;
        }

        /* Indicate we have not yet found a value for the ARCERROR parameter. */
        arcerror = 0.0;

        /* Loop over all contiguous chunks */
        for( ichunk = 0; ichunk < nchunk && *status == SAI__OK; ichunk++ ) {

            /* Display the chunk number. */
            if( nchunk > 1 ) {
                msgOutiff( MSG__VERB, "", "   Doing chunk %d of %d.",
                           status, (int) ichunk + 1, (int) nchunk );
            }

            /* Concatenate the data within this contiguous chunk. This produces a
               smfArray ("concat") containing a smfData for each subarray present in
               the chunk. Each smfData holds the concatenated data for a single
               subarray. */
            smf_concat_smfGroup( wf, NULL, sgroup, darks, NULL, flatramps,
                                 heateffmap, ichunk, 1, 1, NULL, 0, NULL, NULL,
                                 0, 0, 0, &concat, NULL, status );

            /* Get a KeyMap holding values for the configuration parameters. Since we
               sorted by wavelength when calling smf_grp_related, we know that all
               smfDatas in the current smfArray (i.e. chunk) will relate to the same
               wavelength. Therefore we can use the same parameters for all smfDatas in
               the current smfArray. */
            sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE,
                                                  concat->sdata[ 0 ], NULL,
                                                  0, status );
            config = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_calcqu.def",
                                 sub_instruments, status );
            sub_instruments = astAnnul( sub_instruments );


            /* Get the CALCQU specific parameters. */
            if( !astMapGet0I( config, "PASIGN", &pasign ) ) pasign = 1;
            msgOutiff( MSG__VERB, "", "PASIGN=%d", status, pasign );
            if( !astMapGet0D( config, "PAOFF", &paoff ) ) paoff = 0.0;
            msgOutiff( MSG__VERB, "", "PAOFF=%g", status, paoff );
            if( !astMapGet0D( config, "ANGROT", &angrot ) ) angrot = 90.0;
            msgOutiff( MSG__VERB, "", "ANGROT=%g", status, angrot );
            if( !astMapGet0I( config, "SUBMEAN", &submean ) ) submean = 0;
            msgOutiff( MSG__VERB, "", "SUBMEAN=%d", status, submean );

            /* See if the dark squids should be cleaned. */
            if( !astMapGet0I( config, "DKCLEAN", &dkclean ) ) dkclean = 0;

            /* If required, clean the dark squids now since we might need to use them to
               clean the bolometer data. */
            if( dkclean ) {

                /* Create a smfArray containing the dark squid data. For each one, store
                   a pointer to the main header so that smf_clean_smfArray can get at the
                   JCMTState information. */
                dkarray = smf_create_smfArray( status );
                for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                    data = concat->sdata[ idx ];
                    if( data && data->da && data->da->dksquid ) {
                        dkdata = data->da->dksquid;
                        dkdata->hdr = data->hdr;
                        smf_addto_smfArray( dkarray, dkdata, status );
                    }
                }

                /* Clean the smfArray containing the dark squid data. Use the "CLEANDK.*"
                   parameters. */
                (void) astMapGet0A( config, "CLEANDK", &dkpars );
                smf_clean_smfArray( wf, dkarray, NULL, NULL, NULL, dkpars, status );
                dkpars = astAnnul( dkpars );

                /* Nullify the header pointers so that we don't accidentally close any. */
                if( dkarray ) {
                    for( idx = 0; idx < dkarray->ndat; idx++ ) {
                        dkdata = dkarray->sdata[ idx ];
                        dkdata->hdr = NULL;
                    }

                    /* Free the smfArray holding the dark squid data, but do not free the
                       individual smfDatas within it. */
                    dkarray->owndata = 0;
                    smf_close_related( &dkarray, status );
                }
            }

            /* Now clean the bolometer data */
            smf_clean_smfArray( wf, concat, NULL, NULL, NULL, config, status );

            /* If required correct for the POL2 triggering issue. */
            if( fix ) smf_fix_pol2( wf, concat, status );

            /* Loop round each sub-array in the current contiguous chunk of data. */
            for( idx = 0; idx < concat->ndat && *status == SAI__OK; idx++ ) {
                data = concat->sdata[ idx ];

                /* Find the name of the subarray that generated the data. */
                smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL,
                                   status );

                /* Display the sub-array. */
                if( concat->ndat > 1 ) {
                    msgOutiff( MSG__VERB, "", "   Doing sub-array %s.",
                               status, subarray );
                }

                /* Create an empty provenance structure. Each input NDF that contributes
                   to the current chunk and array will be added as an ancestor to this
                   structure, which will later be stored in each output NDF created for
                   this chunk and array. */
                oprov = ndgReadProv( NDF__NOID, "SMURF:CALCQU", status );

                /* Indicate we do not yet have any FITS headers for the output NDFs */
                fc = NULL;

                /* Indicate we do not yet know the coordinate reference frame for the
                   half-waveplate angle. */
                polcrd[ 0 ] = 0;
                ipolcrd = 0;

                /* Go through the smfGroup looking for groups of related input NDFs that
                   contribute to the current chunk. */
                for( igroup = 0; igroup < sgroup->ngroups; igroup++ ) {
                    if( sgroup->chunk[ igroup ] == ichunk ) {

                        /* Get the integer index into the GRP group (sgrp) that holds the input NDFs.
                           This index identifies the input NDF that provides the data for the current
                           chunk and subarray. This assumes that the order in which smf_concat_smfGroup
                           stores arrays in the "concat" smfArray matches the order in which
                           smf_grp_related stores arrays within the sgroup->subgroups. */
                        inidx = sgroup->subgroups[ igroup ][ idx ];

                        /* Add this input NDF as an ancestor into the output provenance structure. */
                        smf_accumulate_prov( NULL, sgrp, inidx, NDF__NOID,
                                             "SMURF:CALCQU", &oprov, status );

                        /* Merge the FITS headers from the current input NDF into the FitsChan
                           that holds headers for the output NDFs. The merging retains only those
                           headers which have the same value in all input NDFs. */
                        smf_fits_outhdr( data->hdr->fitshdr, &fc, status );

                        /* Get the polarimetry related FITS headers and check that all input NDFs
                           have usabie values. */
                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POL_MODE", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "CONSTANT" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Input NDF ^N does not contain "
                                    "polarimetry data obtained with a spinning "
                                    "half-waveplate.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLWAVIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Half-waveplate was not in the beam for "
                                    "input NDF ^N.", status );
                        }

                        headval[ 0 ] = 0;
                        smf_getfitss( data->hdr, "POLANLIN", headval,
                                      sizeof(headval), status );
                        if( strcmp( headval, "Y" ) && *status == SAI__OK ) {
                            *status = SAI__ERROR;
                            grpMsg( "N", sgrp, inidx );
                            errRep( " ", "Analyser was not in the beam for input "
                                    "NDF ^N.", status );
                        }

                        if( polcrd[ 0 ] ) {
                            headval[ 0 ] = 0;
                            smf_getfitss( data->hdr, "POL_CRD", headval,
                                          sizeof(headval), status );
                            if( strcmp( headval, polcrd ) && *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                errRep( " ", "Input NDFs have differing values for "
                                        "FITS header 'POL_CRD'.", status );
                            }

                        } else {
                            smf_getfitss( data->hdr, "POL_CRD", polcrd,
                                          sizeof(polcrd), status );
                            if( !strcmp( polcrd, "FPLANE" ) ) {
                                ipolcrd = 0;
                            } else if( !strcmp( polcrd, "AZEL" ) ) {
                                ipolcrd = 1;
                            } else if( !strcmp( polcrd, "TRACKING" ) ) {
                                ipolcrd = 2;
                            } else if( *status == SAI__OK ) {
                                *status = SAI__ERROR;
                                msgSetc( "N", data->file->name );
                                msgSetc( "V", polcrd );
                                errRep( " ", "Input NDF ^N contains unknown value "
                                        "'^V' for FITS header 'POL_CRD'.", status );
                            }
                        }
                    }
                }

                /* If not already done, get the maximum spatial drift (in arc-seconds) that
                   can be tolerated whilst creating a single I/Q/U image. The default value is
                   half the makemap default pixel size. Also get limits on the number of
                   time slices in any block. */
                if( arcerror == 0.0 ) {
                    parDef0d( "ARCERROR", 0.5*smf_calc_telres( data->hdr->fitshdr,
                              status ), status );
                    parGet0r( "ARCERROR", &arcerror, status );

                    parGet0i( "MAXSIZE", &maxsize, status );
                    parGet0i( "MINSIZE", &minsize, status );
                    if( maxsize > 0 && maxsize < minsize && *status == SAI__OK ) {
                        *status = SAI__ERROR;
                        errRepf( "", "Value of parameter MAXSIZE (%d) is less "
                                 "than value of parameter MINSIZE (%d)", status,
                                 maxsize, minsize );
                    }
                }

                /* The algorithm that calculates I, Q and U assumes that all samples for a
                   single bolometer measure flux from the same point on the sky. Due to
                   sky rotation, this will not be the case - each bolometer will drift
                   slowly across the sky. However, since the drift is (or should be)
                   slow we can apply the I/Q/U algorithm to blocks of contiguous data over
                   which the bolometers do not move significantly. We produce a separate
                   I, Q and U image for each such block. The first block starts at the first
                   time slice in the smfData. */
                block_start = 0;

                /* Find the time slice at which the corner bolometers have moved
                   a critical distance (given by parameter ARCERROR) from their
                   positions at the start of the block. Then back off some time slices
                   to ensure that the block holds an integral number of half-waveplate
                   rotations. */
                block_end = smf_block_end( data, block_start, ipolcrd, arcerror,
                                           maxsize, status );

                /* Loop round creating I/Q/U images for each block. Count them. */
                iblock = 0;
                while( block_end >= 0 && *status == SAI__OK ) {

                    /* Skip very short blocks. */
                    if( block_end - block_start > minsize ) {

                        /* Display the start and end of the block. */
                        msgOutiff( MSG__VERB, "", "   Doing time slice block %d "
                                   "-> %d", status, (int) block_start,
                                   (int) block_end );

                        /* Get the name for the Q NDF for this block. Start of with "Q" followed by
                           the block index. */
                        iblock++;
                        nc = sprintf( ndfname, "Q%d", iblock );

                        /* Append the subarray name to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%s", subarray );

                        /* Append the chunk index to the NDF name. */
                        nc += sprintf( ndfname + nc, "_%d", (int) ichunk );

                        /* Get NDF placeholder for the Q NDF. The NDFs are created inside the
                           output container file. */
                        ndfPlace( locq, ndfname, &qplace, status );

                        /* The name of the U NDF is the same except the initial "Q" is changed to
                           "U". */
                        ndfname[ 0 ] = 'U';
                        ndfPlace( locu, ndfname, &uplace, status );

                        /* The name of the I NDF is the same except the initial "Q" is changed to
                           "I". */
                        if( loci ) {
                            ndfname[ 0 ] = 'I';
                            ndfPlace( loci, ndfname, &iplace, status );
                        } else {
                            iplace = NDF__NOPL;
                        }

                        /* Store the chunk and block numbers as FITS headers. */
                        atlPtfti( fc, "POLCHUNK", (int) ichunk, "Chunk index used by CALCQU", status );
                        atlPtfti( fc, "POLBLOCK", iblock, "Block index used by CALCQU", status );

                        /* Create the Q and U images for the current block of time slices from
                           the subarray given by "idx", storing them in the output container
                           file. */
                        smf_calc_iqu( wf, data, block_start, block_end, ipolcrd,
                                      qplace, uplace, iplace, oprov, fc,
                                      pasign, AST__DD2R*paoff, AST__DD2R*angrot,
                                      submean, status );

                        /* Warn about short blocks. */
                    } else {
                        msgOutiff( MSG__VERB, "", "   Skipping short block of %d "
                                   "time slices (parameter MINSIZE=%d).", status,
                                   block_end - block_start - 1, minsize );
                    }

                    /* The next block starts at the first time slice following the previous
                       block. */
                    block_start = block_end + 1;

                    /* Find the time slice at which the corner bolometers have moved
                       a critical distance (given by parameter ARCERROR) from their
                       positions at the start of the block. Then back off some time slices
                       to ensure that the block holds an integral number of half-waveplate
                       rotations. This returns -1 if all time slices have been used. */
                    block_end = smf_block_end( data, block_start, ipolcrd,
                                               arcerror, maxsize, status );
                }

                /* Free resources */
                oprov = ndgFreeProv( oprov, status );
                fc = astAnnul( fc );
            }
            config = astAnnul( config );

            /* Close the smfArray. */
            smf_close_related( &concat, status );
        }

        /* Annul the locators for the output container files. */
        datAnnul( &locq, status );
        datAnnul( &locu, status );
        if( loci ) datAnnul( &loci, status );

        /* The parameter system hangs onto a primary locator for each container
           file, so cancel the parameters to annul these locators. */
        datCancl( "OUTQ", status );
        datCancl( "OUTU", status );
        datCancl( "OUTI", status );
    }

    /* Free resources. */
    smf_close_related( &darks, status );
    smf_close_related( &flatramps, status );

    if( igrp ) grpDelet( &igrp, status);
    if( sgrp ) grpDelet( &sgrp, status);
    if( bgrp ) grpDelet( &bgrp, status );
    if( ogrp ) grpDelet( &ogrp, status );
    if( sgroup ) smf_close_smfGroup( &sgroup, status );
    if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );

    /* End the NDF and AST contexts. */
    ndfEnd( status );
    astEnd;

    /* Issue a status indication.*/
    if( *status == SAI__OK ) {
        msgOutif( MSG__VERB, " ", "CALCQU succeeded.", status);
    } else {
        msgOutif( MSG__VERB, " ", "CALCQU failed.", status);
    }
}
예제 #5
0
void smurf_sc2concat( int *status ) {

  /* Local Variables */
  Grp *basegrp=NULL;         /* Grp containing first file each chunk */
  size_t basesize;           /* Number of files in base group */
  smfArray *concat=NULL;     /* Pointer to a smfArray */
  size_t contchunk;          /* Continuous chunk counter */
  smfArray *darks = NULL;    /* dark frames */
  int ensureflat;            /* Flag for flatfielding data */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  smfArray * flatramps = NULL; /* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t gcount=0;           /* Grp index counter */
  size_t idx;                /* Subarray counter */
  int usedarks;              /* flag for using darks */
  Grp *igrp = NULL;          /* Group of input files */
  smfGroup *igroup=NULL;     /* smfGroup corresponding to igrp */
  size_t isize;              /* Number of files in input group */
  dim_t maxconcat=0;         /* Longest continuous chunk length in samples */
  double maxlen;             /* Constrain maxconcat to this many seconds */
  size_t ncontchunks=0;      /* Number continuous chunks outside iter loop */
  Grp *ogrp = NULL;          /* Output files  */
  size_t osize;              /* Number of files in input group */
  dim_t padStart=0;          /* How many samples padding at start */
  dim_t padEnd=0;            /* How many samples padding at end */
  int temp;                  /* Temporary signed integer */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */

  if (*status != SAI__OK) return;

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &isize, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 1, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  isize = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (isize == 0) {
    msgOutif(MSG__NORM, " ","All supplied input frames were filtered,"
       " nothing to do", status );
    goto CLEANUP;
  }

  /* --- Parse ADAM parameters ------------------------ */

  /* Maximum length of a continuous chunk */
  parGdr0d( "MAXLEN", 0, 0, VAL__MAXD, 1, &maxlen, status );

  /* Padding */
  parGdr0i( "PADSTART", 0, 0, VAL__MAXI, 1, &temp, status );
  padStart = (dim_t) temp;

  parGdr0i( "PADEND", 0, 0, VAL__MAXI, 1, &temp, status );
  padEnd = (dim_t) temp;

  /* Are we using darks? */
  parGet0l( "USEDARKS", &usedarks, status );

  /* Are we flatfielding? */
  parGet0l( "FLAT", &ensureflat, status );

  /* Group the input files by subarray and continuity */
  smf_grp_related( igrp, isize, 1, 0, maxlen-padStart-padEnd, NULL, NULL,
                   &maxconcat, NULL, &igroup, &basegrp, NULL, status );

  /* Obtain the number of continuous chunks and subarrays */
  if( *status == SAI__OK ) {
    ncontchunks = igroup->chunk[igroup->ngroups-1]+1;
  }

  basesize = grpGrpsz( basegrp, status );

  /* Get output file(s) */
  kpg1Wgndf( "OUT", basegrp, basesize, basesize,
             "More output files required...",
             &ogrp, &osize, status );

  /* Loop over continuous chunks */
  gcount = 1;
  for( contchunk=0;(*status==SAI__OK)&&contchunk<ncontchunks; contchunk++ ) {

    /* Concatenate this continuous chunk */
    smf_concat_smfGroup( wf, NULL, igroup, usedarks ? darks:NULL, NULL, flatramps,
                         heateffmap, contchunk, ensureflat, 1, NULL, 0, NULL, NULL,
                         NO_FTS, padStart, padEnd, 0, &concat, NULL, status );

    /* Export concatenated data for each subarray to NDF file */
    for( idx=0; (*status==SAI__OK)&&idx<concat->ndat; idx++ ) {
      if( concat->sdata[idx]->file && concat->sdata[idx]->file->name ) {
        smf_write_smfData( concat->sdata[idx], NULL, NULL, ogrp, gcount,
                           NDF__NOID, MSG__VERB, 0, status );
      } else {
        *status = SAI__ERROR;
        errRep( FUNC_NAME,
                "Unable to determine file name for concatenated data.",
                status );
      }

      /* Increment the group index counter */
      gcount++;
    }

    /* Close the smfArray */
    smf_close_related( &concat, status );

  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

 CLEANUP:
  if( darks ) smf_close_related( &darks, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  if( igrp ) grpDelet( &igrp, status);
  if( basegrp ) grpDelet( &basegrp, status );
  if( ogrp ) grpDelet( &ogrp, status );
  if( igroup ) smf_close_smfGroup( &igroup, status );

  ndfEnd( status );

  if( *status == SAI__OK ) {
    msgOutif(MSG__VERB," ","SC2CONCAT succeeded.", status);
  } else {
    msgOutif(MSG__VERB," ","SC2CONCAT failed.", status);
  }

}
예제 #6
0
void smurf_extinction( int * status ) {

  /* Local Variables */
  smfArray *bbms = NULL;     /* Bad bolometer masks */
  smfArray *darks = NULL;    /* Dark data */
  AstKeyMap *extpars = NULL; /* Tau relation keymap */
  Grp *fgrp = NULL;          /* Filtered group, no darks */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  int has_been_sky_removed = 0;/* Data are sky-removed */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t i;                  /* Loop counter */
  Grp *igrp = NULL;          /* Input group */
  AstKeyMap *keymap=NULL;    /* Keymap for storing parameters */
  smf_tausrc tausrc;         /* enum value of optical depth source */
  smf_extmeth extmeth;       /* Extinction correction method */
  char tausource[LEN__METHOD];  /* String for optical depth source */
  char method[LEN__METHOD];  /* String for extinction airmass method */
  smfData *odata = NULL;     /* Output data struct */
  Grp *ogrp = NULL;          /* Output group */
  size_t outsize;            /* Total number of NDF names in the output group */
  size_t size;               /* Number of files in input group */
  double tau = 0.0;          /* Zenith tau at this wavelength */
  ThrWorkForce *wf = NULL;   /* Pointer to a pool of worker threads */

  if (*status != SAI__OK) return;

  /* Main routine */
  ndfBegin();

  /* Find the number of cores/processors available and create a pool of
     threads of the same size. */
  wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status );

  /* Read the input file */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( igrp, &fgrp, 0, NULL, NULL, 1, 1, SMF__NULL, &darks,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (size > 0) {
    /* Get output file(s) */
    kpg1Wgndf( "OUT", igrp, size, size, "More output files required...",
               &ogrp, &outsize, status );
  } else {
    msgOutif(MSG__NORM, " ","All supplied input frames were DARK,"
             " nothing to extinction correct", status );
  }

  /* Get group of pixel masks and read them into a smfArray */
  smf_request_mask( "BBM", &bbms, status );

  /* Read the tau relations from config file or group. We do not
     allow sub instrument overloading because these are all values
     based on filter name. */
  keymap = kpg1Config( "TAUREL", "$SMURF_DIR/smurf_extinction.def", NULL,
                       1, status );

  /* and we need to use the EXT entry */
  astMapGet0A( keymap, "EXT", &extpars );
  keymap = astAnnul( keymap );

  /* Get tau source */
  parChoic( "TAUSRC", "Auto",
            "Auto,CSOtau,CSOFit, Filtertau, WVMraw", 1,
            tausource, sizeof(tausource), status);

  /* Decide how the correction is to be applied - convert to flag */
  parChoic( "METHOD", "ADAPTIVE",
            "Adaptive,Quick,Full,", 1, method, sizeof(method), status);

  /* Place parameters into a keymap and extract values */
  if( *status == SAI__OK ) {
    keymap = astKeyMap( " " );
    if( astOK ) {
      astMapPut0C( keymap, "TAUSRC", tausource, NULL );
      astMapPut0C( keymap, "TAUMETHOD", method, NULL );
      smf_get_extpar( keymap, &tausrc, &extmeth, NULL, status );
    }
  }

  for (i=1; i<=size && ( *status == SAI__OK ); i++) {

    /* Flatfield - if necessary */
    smf_open_and_flatfield( igrp, ogrp, i, darks, flatramps, heateffmap,
                            &odata, status );

    if (*status != SAI__OK) {
      /* Error flatfielding: tell the user which file it was */
      msgSeti("I",i);
      errRep(TASK_NAME, "Unable to open the ^I th file", status);
    }

    /* Mask out bad pixels - mask data array not quality array */
    smf_apply_mask( odata, bbms, SMF__BBM_DATA, 0, status );

    /* Now check that the data are sky-subtracted */
    if ( !smf_history_check( odata, "smf_subtract_plane", status ) ) {

      /* Should we override remsky check? */
      parGet0l("HASSKYREM", &has_been_sky_removed, status);

      if ( !has_been_sky_removed && *status == SAI__OK ) {
        *status = SAI__ERROR;
        msgSeti("I",i);
        errRep("", "Input data from file ^I are not sky-subtracted", status);
      }
    }

    /* If status is OK, make decisions on source keywords the first
       time through. */
    if ( *status == SAI__OK && i == 1 ) {
      if (tausrc == SMF__TAUSRC_CSOTAU ||
          tausrc == SMF__TAUSRC_AUTO ||
          tausrc == SMF__TAUSRC_TAU) {
        double deftau;
        const char * param = NULL;
        smfHead *ohdr = odata->hdr;

        /* get default CSO tau -- this could be calculated from CSO fits */
        deftau = smf_calc_meantau( ohdr, status );

        /* Now ask for desired CSO tau */
        if ( tausrc == SMF__TAUSRC_CSOTAU || tausrc == SMF__TAUSRC_AUTO) {
          param = "CSOTAU";
        } else if (tausrc == SMF__TAUSRC_TAU) {
          param = "FILTERTAU";
          deftau = smf_cso2filt_tau( ohdr, deftau, extpars, status );
        }
        parGdr0d( param, deftau, 0.0,1.0, 1, &tau, status );
      } else if ( tausrc == SMF__TAUSRC_CSOFIT || tausrc == SMF__TAUSRC_WVMRAW ) {
        /* Defer a message until after extinction correction */
      } else {
        *status = SAI__ERROR;
        errRep("", "Unsupported opacity source. Possible programming error.",
               status);
      }
    }

    /* Apply extinction correction - note that a check is made to
       determine whether the data have already been extinction
       corrected */
    smf_correct_extinction( wf, odata, &tausrc, extmeth, extpars, tau, NULL, NULL, status );

    if ( tausrc == SMF__TAUSRC_WVMRAW ) {
      msgOutif(MSG__VERB," ", "Used Raw WVM data for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_CSOFIT ) {
      msgOutif(MSG__VERB," ", "Used fit to CSO data for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_CSOTAU ) {
      msgOutif(MSG__VERB," ", "Used an explicit CSO tau value for extinction correction", status);
    } else if ( tausrc == SMF__TAUSRC_TAU ) {
      msgOutif(MSG__VERB," ", "Used an explicit filter tau value for extinction correction", status);
    } else {
      if (*status == SAI__OK) {
        const char * taustr = smf_tausrc_str( tausrc, status );
        *status = SAI__ERROR;
        errRepf( "", "Unexpected opacity source used for extinction correction of %s."
                 " Possible programming error.", status, taustr );
      }
    }

    /* Set character labels */
    smf_set_clabels( "Extinction corrected",NULL, NULL, odata->hdr, status);
    smf_write_clabels( odata, status );

    /* Free resources for output data */
    smf_close_file( &odata, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines  */
  if (darks) smf_close_related( &darks, status );
  if (bbms) smf_close_related( &bbms, status );
  if( flatramps ) smf_close_related( &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  grpDelet( &igrp, status);
  grpDelet( &ogrp, status);
  if( keymap ) keymap = astAnnul( keymap );
  if (extpars) extpars = astAnnul( extpars );
  ndfEnd( status );
}
예제 #7
0
void smf_find_science(const Grp * ingrp, Grp **outgrp, int reverttodark,
                      Grp **darkgrp, Grp **flatgrp, int reducedark,
                      int calcflat, smf_dtype darktype, smfArray ** darks,
                      smfArray **fflats, AstKeyMap ** heateffmap,
                      double * meanstep, int * status ) {

  smfSortInfo *alldarks; /* array of sort structs for darks */
  smfSortInfo *allfflats; /* array of fast flat info */
  Grp * dgrp = NULL;  /* Internal dark group */
  double duration_darks = 0.0; /* total duration of all darks */
  double duration_sci = 0.0;  /* Duration of all science observations */
  size_t dkcount = 0; /* Dark counter */
  size_t ffcount = 0; /* Fast flat counter */
  Grp * fgrp = NULL;  /* Fast flat group */
  size_t i;           /* loop counter */
  smfData *infile = NULL; /* input file */
  size_t insize;     /* number of input files */
  size_t nsteps_dark = 0;    /* Total number of steps for darks */
  size_t nsteps_sci = 0;     /* Total number of steps for science */
  AstKeyMap * heatermap = NULL; /* Heater efficiency map */
  AstKeyMap * obsmap = NULL; /* Info from all observations */
  AstKeyMap * objmap = NULL; /* All the object names used */
  AstKeyMap * scimap = NULL; /* All non-flat obs indexed by unique key */
  Grp *ogrp = NULL;   /* local copy of output group */
  size_t sccount = 0; /* Number of accepted science files */
  struct timeval tv1;  /* Timer */
  struct timeval tv2;  /* Timer */

  if (meanstep) *meanstep = VAL__BADD;
  if (outgrp) *outgrp = NULL;
  if (darkgrp) *darkgrp = NULL;
  if (darks) *darks = NULL;
  if (fflats) *fflats = NULL;
  if (heateffmap) *heateffmap = NULL;

  if (*status != SAI__OK) return;

  /* Sanity check to make sure we return some information */
  if ( outgrp == NULL && darkgrp == NULL && darks == NULL && fflats == NULL) {
    *status = SAI__ERROR;
    errRep( " ", FUNC_NAME ": Must have some non-NULL arguments"
            " (possible programming error)", status);
    return;
  }

  /* Start a timer to see how long this takes */
  smf_timerinit( &tv1, &tv2, status );

  /* Create new group for output files */
  ogrp = smf_grp_new( ingrp, "Science", status );

  /* and a new group for darks */
  dgrp =  smf_grp_new( ingrp, "DarkFiles", status );

  /* and for fast flats */
  fgrp =  smf_grp_new( ingrp, "FastFlats", status );

  /* and also create a keymap for the observation description */
  obsmap = astKeyMap( "KeyError=1" );

  /* and an object map */
  objmap = astKeyMap( "KeyError=1" );

  /* This keymap contains the sequence counters for each related
     subarray/obsidss/heater/shutter combination and is used to decide
     if a bad flat is relevant */
  scimap = astKeyMap( "KeyError=1,KeyCase=0" );

  /* This keymap is used to contain relevant heater efficiency data */
  heatermap = astKeyMap( "KeyError=1,KeyCase=0" );

  /* Work out how many input files we have and allocate sufficient sorting
     space */
  insize = grpGrpsz( ingrp, status );
  alldarks = astCalloc( insize, sizeof(*alldarks) );
  allfflats = astCalloc( insize, sizeof(*allfflats) );

  /* check each file in turn */
  for (i = 1; i <= insize; i++) {
    int seqcount = 0;
    char keystr[100];  /* Key for scimap entry */

    /* open the file but just to get the header */
    smf_open_file( ingrp, i, "READ", SMF__NOCREATE_DATA, &infile, status );
    if (*status != SAI__OK) break;

    /* Fill in the keymap with observation details */
    smf_obsmap_fill( infile, obsmap, objmap, status );

    /* Find the heater efficiency map if required */
    if (*status == SAI__OK && heateffmap) {
      char arrayidstr[32];
      smf_fits_getS( infile->hdr, "ARRAYID", arrayidstr, sizeof(arrayidstr),
                     status );
      if (!astMapHasKey( heatermap, arrayidstr ) ) {
        smfData * heateff = NULL;
        dim_t nbolos = 0;
        smf_flat_params( infile, "RESIST", NULL, NULL, NULL, NULL, NULL,
                         NULL, NULL, NULL, NULL, NULL, &heateff, status );
        smf_get_dims( heateff, NULL, NULL, &nbolos, NULL, NULL, NULL, NULL,
                      status );
        if (heateff) astMapPut0P( heatermap, arrayidstr, heateff, NULL );
      }
    }

    /* Get the sequence counter for the file. We do not worry about
       duplicate sequence counters (at the moment) */
    smf_find_seqcount( infile->hdr, &seqcount, status );

    /* The key identifying this subarray/obsidss/heater/shutter combo */
    smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status );

    if (smf_isdark( infile, status )) {
      /* Store the sorting information */
      dkcount = smf__addto_sortinfo( infile, alldarks, i, dkcount, "Dark", status );
      smf__addto_durations( infile, &duration_darks, &nsteps_dark, status );
      astMapPutElemI( scimap, keystr, -1, seqcount );
    } else {
      /* compare sequence type with observation type and drop it (for now)
         if they differ */
      if ( infile->hdr->obstype == infile->hdr->seqtype ) {
        /* Sanity check the header for corruption. Compare RTS_NUM with SEQSTART
           and SEQEND. The first RTS_NUM must either be SEQSTART or else between
           SEQSTART and SEQEND (if someone has giving us a section) */
        int seqstart = 0;
        int seqend = 0;
        int firstnum = 0;
        JCMTState *tmpState = NULL;
        smf_getfitsi( infile->hdr, "SEQSTART", &seqstart, status );
        smf_getfitsi( infile->hdr, "SEQEND", &seqend, status );
        tmpState = infile->hdr->allState;

        if( tmpState ) {
          firstnum = (tmpState[0]).rts_num;
          smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
          if ( firstnum >= seqstart && firstnum <= seqend ) {
            /* store the file in the output group */
            ndgCpsup( ingrp, i, ogrp, status );
            msgOutif(MSG__DEBUG, " ", "Non-dark file: ^F",status);
            smf__addto_durations( infile, &duration_sci, &nsteps_sci, status );
            astMapPutElemI( scimap, keystr, -1, seqcount );
            sccount++;
          } else {
            msgOutif( MSG__QUIET, "",
                      "File ^F has a corrupt FITS header. Ignoring it.",
                      status );
          }
        } else {
          smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
          /* store the file in the output group */
          ndgCpsup( ingrp, i, ogrp, status );
          msgOutif( MSG__DEBUG, " ",
                    "File ^F lacks JCMTState: assuming it is non-dark",status);
          smf__addto_durations( infile, &duration_sci, &nsteps_sci, status );
          astMapPutElemI( scimap, keystr, -1, seqcount );
          sccount++;
        }

      } else if (infile->hdr->seqtype == SMF__TYP_FASTFLAT ) {
        ffcount = smf__addto_sortinfo( infile, allfflats, i, ffcount, "Fast flat", status );
      } else {
        smf_smfFile_msg( infile->file, "F", 1, "<unknown file>");
        msgOutif(MSG__DEBUG, " ", "Sequence type mismatch with observation type: ^F",status);
      }
    }

    /* close the file */
    smf_close_file( &infile, status );
  }

  /* Store output group in return variable or else free it */
  if (outgrp) {
    *outgrp = ogrp;
  } else {
    grpDelet( &ogrp, status );
  }

  /* process flatfields if necessary */
  if (ffcount > 0 && fflats ) {
    smfArray * array = NULL;

    /* sort flats into order */
    qsort( allfflats, ffcount, sizeof(*allfflats), smf_sort_bydouble);

    if (fflats) array = smf_create_smfArray( status );

    /* now open the flats and store them if requested */
    if (*status == SAI__OK && array && ffcount) {
      size_t start_ffcount = ffcount;
      AstKeyMap * flatmap = NULL;

      if (calcflat) {
        /* Use AgeUp so that we get the keys out in the sorted order
           that allfflats used */
        flatmap = astKeyMap( "KeyCase=0,KeyError=1,SortBy=AgeDown" );
      }

      /* Read each flatfield. Calculate a responsivity image and a flatfield
         solution. Store these in a keymap along with related information
         which is itself stored in a keymap indexed by a string made of
         OBSIDSS, reference heater value, shutter and subarray.
      */

      for (i = 0; i < start_ffcount; i++ ) {
        size_t ori_index =  (allfflats[i]).index;
        smfData * outfile = NULL;
        char keystr[100];
        AstKeyMap * infomap = astKeyMap( "KeyError=1" );
        int oplen = 0;
        char thisfile[MSG__SZMSG];
        int seqcount = 0;

        /* read filename from group */
        infile = NULL;
        smf_open_file( ingrp, ori_index, "READ", 0, &infile, status );
        if ( *status != SAI__OK ) {
          /* This should not happen because we have already opened
             the file. If it does happen we abort with error. */
          if (infile) smf_close_file( &infile, status );
          break;
        }

        /* Calculate the key for this observation */
        smf__calc_flatobskey( infile->hdr, keystr, sizeof(keystr), status );

        /* Get the file name for error messages */
        smf_smfFile_msg( infile->file, "F", 1, "<unknown file>" );
        msgLoad( "", "^F", thisfile, sizeof(thisfile), &oplen, status );

        /* And the sequence counter to link against science observations */
        smf_find_seqcount( infile->hdr, &seqcount, status );

        /* Prefill infomap */
        astMapPut0C( infomap, "FILENAME", thisfile, "");
        astMapPut0I( infomap, "SEQCOUNT", seqcount, "");

        /* Collapse it */
        if (*status == SAI__OK) {
          smf_flat_fastflat( infile, &outfile, status );
          if (*status == SMF__BADFLAT) {
            errFlush( status );

            if (calcflat) {
              /* Need to generate an outfile like smf_flat_fastflat
                 and one heater setting will force smf_flat_calcflat to fail */
              smf_flat_malloc( 1, infile, NULL, &outfile, status );
            } else {
              if (outfile) smf_close_file( &outfile, status );
              if (infile) smf_close_file( &infile, status );
              infomap = astAnnul( infomap );
              ffcount--;
              continue;
            }
          }
        }

        if (outfile && *status == SAI__OK) {
          smf_close_file( &infile, status );
          infile = outfile;

          if (calcflat) {
            size_t ngood = 0;
            smfData * curresp = NULL;
            int utdate;

            if (*status == SAI__OK) {
              ngood = smf_flat_calcflat( MSG__VERB, NULL, "RESIST",
                                         "FLATMETH", "FLATORDER", NULL, "RESPMASK",
                                         "FLATSNR", NULL, infile, &curresp, status );
              if (*status != SAI__OK) {
                /* if we failed to calculate a flatfield we continue but force the
                   flatfield to be completely bad. This will force the science data associated
                   with the flatfield to be correctly blanked. We do not annul though
                   if we have a SUBPAR error telling us that we have failed to define
                   our parameters properly. */
                if (*status != SUBPAR__NOPAR) errAnnul(status);

                /* parameters of flatfield */
                ngood = 0;

                /* Generate a blank flatfield and blank responsivity image */
                smf_flat_badflat( infile, &curresp, status );
              }

              /* Retrieve the UT date so we can decide whether to compare
                 flatfields */
              smf_getfitsi( infile->hdr, "UTDATE", &utdate, status );

              /* Store the responsivity data for later on and the processed
                 flatfield until we have vetted it */
              astMapPut0P( infomap, "CALCFLAT", infile, "" );
              astMapPut0P( infomap, "RESP", curresp, "" );
              astMapPut0I( infomap, "UTDATE", utdate, "" );
              astMapPut0I( infomap, "ISGOOD", 1, "" );
              astMapPut0I( infomap, "NGOOD", ngood, "" );
              astMapPut0I( infomap, "GRPINDEX", ori_index, "" );
              astMapPut0I( infomap, "SMFTYP", infile->hdr->obstype, "" );
              astMapPutElemA( flatmap, keystr, -1, infomap );

            }

          } else { /* if (calcflat) */
            /* Store the collapsed flatfield  - the processed flat is not stored here yet */
            smf_addto_smfArray( array, infile, status );

            /* Copy the group info */
            ndgCpsup( ingrp, ori_index, fgrp, status );

          }

        } /* if (outfile) */

        /* Annul the keymap (will be fine if it is has been stored in another keymap) */
        infomap = astAnnul( infomap );

      } /* End loop over flatfields */

      /* Now we have to loop over the related flatfields to disable
         bolometers that are not good and also decide whether we
         need to set status to bad. */
      if (*status == SAI__OK && calcflat ) {
        size_t nkeys = astMapSize( flatmap );
        for (i = 0; i < nkeys; i++ ) {
          const char *key = astMapKey( flatmap, i );
          int nf = 0;
          AstKeyMap ** kmaps = NULL;
          int nelem = astMapLength( flatmap, key );
          kmaps = astMalloc( sizeof(*kmaps) * nelem );
          astMapGet1A( flatmap, key, nelem, &nelem, kmaps );

          for ( nf = 0; nf < nelem && *status == SAI__OK; nf++ ) {
            AstKeyMap * infomap = kmaps[nf];
            int isgood = 0;

            astMapGet0I( infomap, "ISGOOD", &isgood );

            if (isgood) {
              /* The flatfield worked */
              size_t ngood = 0;
              int itemp;
              int utdate = 0;
              int ratioFlats = 0;

              /* Get the UT date - we do not compare flatfields after
                 the time we enabled heater tracking at each sequence. */
              astMapGet0I( infomap, "UTDATE", &utdate );

              /* Get the number of good bolometers at this point */
              astMapGet0I( infomap, "NGOOD", &itemp );
              ngood = itemp;

              /* Decide if we want to do the ratio test. We default to
                 not doing it between 20110901 and 20120827 which is
                 the period when we did mini-heater tracks before each
                 flat. ! indicates that we choose based on date. */
              if (*status == SAI__OK) {
                parGet0l( "FLATUSENEXT", &ratioFlats, status );
                if ( *status == PAR__NULL ) {
                  errAnnul( status );
                  if (utdate >= 20110901 || utdate <= 20120827 ) {
                    ratioFlats = 0;
                  } else {
                    ratioFlats = 1;
                  }
                }
              }

              /* Can we compare with the next flatfield? */
              if (ngood < SMF__MINSTATSAMP || !ratioFlats ) {
                /* no point doing all the ratio checking for this */
              } else if ( nelem - nf >= 2 ) {
                AstKeyMap * nextmap = kmaps[nf+1];
                const char *nextfname = NULL;
                const char *fname = NULL;
                smfData * curresp = NULL;
                smfData * nextresp = NULL;
                smfData * curflat = NULL;
                void *tmpvar = NULL;
                size_t bol = 0;
                smfData * ratio = NULL;
                double *in1 = NULL;
                double *in2 = NULL;
                double mean = VAL__BADD;
                size_t nbolo;
                double *out = NULL;
                double sigma = VAL__BADD;
                float clips[] = { 5.0, 5.0 }; /* 5.0 sigma iterative clip */
                size_t ngoodz = 0;

                astMapGet0C( nextmap, "FILENAME", &nextfname );
                astMapGet0C( infomap, "FILENAME", &fname );

                /* Retrieve the responsivity images from the keymap */
                astMapGet0P( infomap, "RESP", &tmpvar );
                curresp = tmpvar;
                astMapGet0P( nextmap, "RESP", &tmpvar );
                nextresp = tmpvar;
                astMapGet0P( infomap, "CALCFLAT", &tmpvar );
                curflat = tmpvar;

                nbolo = (curresp->dims)[0] * (curresp->dims)[1];

                /* get some memory for the ratio if we have not already.
                   We could get some memory once assuming each flat has the
                   same number of bolometers... */
                ratio = smf_deepcopy_smfData( curresp, 0, 0, 0, 0, status );
                if( *status == SAI__OK ) {

                  /* divide: smf_divide_smfData ? */
                  in1 = (curresp->pntr)[0];
                  in2 = (nextresp->pntr)[0];
                  out = (ratio->pntr)[0];

                  for (bol=0; bol<nbolo;bol++) {
                    if ( in1[bol] != VAL__BADD && in1[bol] != 0.0 &&
                         in2[bol] != VAL__BADD && in2[bol] != 0.0 ) {
                      out[bol] = in1[bol] / in2[bol];
                    } else {
                      out[bol] = VAL__BADD;
                    }
                  }
                }

                /* find some statistics */
                smf_clipped_stats1D( out, 2, clips, 1, nbolo, NULL, 0, 0, &mean,
                                     &sigma, NULL, 0, &ngoodz, status );

                if (*status == SMF__INSMP) {
                  errAnnul(status);
                  msgOutiff( MSG__QUIET, "",
                            "Flatfield ramp ratio of %s with %s had too few bolometers (%zu < %d).",
                             status, fname, nextfname, ngoodz, SMF__MINSTATSAMP );
                  ngood = ngoodz; /* Must be lower or equal to original ngood */

                } else if (*status == SAI__OK && mean != VAL__BADD && sigma != VAL__BADD && curflat->da) {
                  /* Now flag the flatfield as bad for bolometers that have changed
                     more than n%. We expect the variation to be 1+/-a small bit */
                  const double pmrange = 0.10;
                  double thrlo = 1.0 - pmrange;
                  double thrhi = 1.0 + pmrange;
                  size_t nmasked = 0;
                  double *flatcal = curflat->da->flatcal;

                  msgOutiff( MSG__DEBUG, "", "Flatfield fast ramp ratio mean = %g +/- %g (%zu bolometers)",
                             status, mean, sigma, ngood);

                  /* we can just set the first slice of the flatcal to bad. That should
                     be enough to disable the entire bolometer. We have just read these
                     data so they should be in ICD order. */
                  for (bol=0; bol<nbolo;bol++) {
                    if ( out[bol] != VAL__BADD &&
                         (out[bol] < thrlo || out[bol] > thrhi ) ) {
                      flatcal[bol] = VAL__BADD;
                      nmasked++;
                    } else if ( in1[bol] != VAL__BADD && in2[bol] == VAL__BADD ) {
                      /* A bolometer is bad next time but good now so we must set it bad now */
                      flatcal[bol] = VAL__BADD;
                      nmasked++;
                    }
                  }

                  if ( nmasked > 0 ) {
                    msgOutiff( MSG__NORM, "", "Masked %zu bolometers in %s from unstable flatfield",
                               status, nmasked, fname );

                    /* update ngood to take into account the masking */
                    ngood -= nmasked;
                  }

                }

                smf_close_file( &ratio, status );

              } /* End of flatfield responsivity comparison */

              /* if we only have a few bolometers left we now consider this
                 a bad flat unless it is actually an engineering measurement
                 where expect some configurations to give zero bolometers */
              if (ngood < SMF__MINSTATSAMP) {
                const char *fname = NULL;
                void * tmpvar = NULL;
                int smftyp = 0;
                smfData * curflat = NULL;
                astMapGet0I( infomap, "SMFTYP", &smftyp );
                astMapGet0C( infomap, "FILENAME", &fname );
                if (smftyp != SMF__TYP_NEP) {
                  msgOutiff( MSG__QUIET, "",
                            "Flatfield %s has %zu good bolometer%s.%s",
                             status, fname, ngood, (ngood == 1 ? "" : "s"),
                             ( ngood == 0 ? "" : " Keeping none.") );
                  isgood = 0;

                  /* Make sure that everything is blanked. */
                  if (ngood > 0) {
                    astMapGet0P( infomap, "CALCFLAT", &tmpvar );
                    curflat = tmpvar;
                    if (curflat && curflat->da) {
                      size_t bol;
                      size_t nbolo = (curflat->dims)[0] * (curflat->dims)[1];
                      double *flatcal = curflat->da->flatcal;
                      for (bol=0; bol<nbolo; bol++) {
                        /* Just need to set the first element to bad */
                        flatcal[bol] = VAL__BADD;
                      }
                    }
                  }

                } else {
                  msgOutiff( MSG__NORM, "",
                            "Flatfield ramp file %s has %zu good bolometer%s. Eng mode.",
                             status, fname, ngood, (ngood == 1 ? "" : "s") );
                }
              }

              /* We do not need the responsivity image again */
              {
                void *tmpvar = NULL;
                smfData * resp = NULL;
                astMapGet0P( infomap, "RESP", &tmpvar );
                resp = tmpvar;
                if (resp) smf_close_file( &resp, status );
                astMapRemove( infomap, "RESP" );
              }

            } /* End of isgood comparison */

            /* We are storing flats even if they failed. Let the downstream
               software worry about it */
            {
              int ori_index;
              smfData * flatfile = NULL;
              void *tmpvar = NULL;

              /* Store in the output group */
              astMapGet0I( infomap, "GRPINDEX", &ori_index );
              ndgCpsup( ingrp, ori_index, fgrp, status );

              /* And store in the smfArray */
              astMapGet0P( infomap, "CALCFLAT", &tmpvar );
              astMapRemove( infomap, "CALCFLAT" );
              flatfile = tmpvar;
              smf_addto_smfArray( array, flatfile, status );
            }

            /* Free the object as we go */
            kmaps[nf] = astAnnul( kmaps[nf] );
          } /* End of loop over this obsidss/subarray/heater */

          kmaps = astFree( kmaps );

        }
      }

      if (array->ndat) {
        if (fflats) *fflats = array;
      } else {
        smf_close_related(&array, status );
        if (fflats) *fflats = NULL;
      }
    }
  }

  /* no need to do any more if neither darks nor darkgrp are defined or we might
     be wanting to revert to darks. */
  if (dkcount > 0 && (darks || darkgrp || reverttodark ) ) {
    smfArray * array = NULL;

    /* sort darks into order */
    qsort( alldarks, dkcount, sizeof(*alldarks), smf_sort_bydouble);

    if (darks) array = smf_create_smfArray( status );

    /* now open the darks and store them if requested */
    if (*status == SAI__OK) {
      for (i = 0; i < dkcount; i++ ) {
        size_t ori_index =  (alldarks[i]).index;

         /* Store the entry in the output group */
        ndgCpsup( ingrp, ori_index, dgrp, status );

        if (darks) {

          /* read the value from the new group */
          smf_open_file( dgrp, i+1, "READ", 0, &infile, status );

          /* do we have to process these darks? */
          if (reducedark) {
            smfData *outfile = NULL;
            smf_reduce_dark( infile, darktype, &outfile, status );
            if (outfile) {
              smf_close_file( &infile, status );
              infile = outfile;
            }
          }

          smf_addto_smfArray( array, infile, status );
        }
      }
      if (darks) *darks = array;
    }
  }

  /* free memory */
  alldarks = astFree( alldarks );
  allfflats = astFree( allfflats );

  if( reverttodark && outgrp && (grpGrpsz(*outgrp,status)==0) &&
      (grpGrpsz(dgrp,status)>0) ) {
    /* If outgrp requested but no science observations were found, and
       dark observations were found, return darks in outgrp and set
       flatgrp and darkgrp to NULL. This is to handle cases where we
       want to process data taken in the dark like normal science
       data. To activate this behaviour set reverttodark */

    msgOutiff( MSG__NORM, "", "Treating the dark%s as science data",
               status, ( dkcount > 1 ? "s" : "" ) );

    *outgrp = dgrp;

    if( darkgrp ){
      *darkgrp = NULL;
    }

    if( flatgrp ) {
      *flatgrp = NULL;
    }

    grpDelet( &ogrp, status);
    grpDelet( &fgrp, status);

    if (meanstep && nsteps_dark > 0) *meanstep = duration_darks / nsteps_dark;

    /* Have to clear the darks smfArray as well */
    if (darks) smf_close_related( darks, status );

  } else {
    /* Store the output groups in the return variable or free it */
    if (darkgrp) {
      *darkgrp = dgrp;
    } else {
      grpDelet( &dgrp, status);
    }
    if (flatgrp) {
      *flatgrp = fgrp;
    } else {
      grpDelet( &fgrp, status);
    }

    if (meanstep && nsteps_sci > 0) *meanstep = duration_sci / nsteps_sci;
  }

  msgSeti( "ND", sccount );
  msgSeti( "DK", dkcount );
  msgSeti( "FF", ffcount );
  msgSeti( "TOT", insize );
  if ( insize == 1 ) {
    if (dkcount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was a dark",
                status);
    } else if (ffcount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was a fast flatfield",
                status);
    } else if (sccount == 1) {
      msgOutif( MSG__VERB, " ", "Single input file was accepted (observation type same as sequence type)",
                status);
    } else {
      msgOutif( MSG__VERB, " ", "Single input file was not accepted.",
                status);
    }

  } else {
    if (dkcount == 1) {
      msgSetc( "DKTXT", "was a dark");
    } else {
      msgSetc( "DKTXT", "were darks");
    }
    if (ffcount == 1) {
      msgSetc( "FFTXT", "was a fast flat");
    } else {
      msgSetc( "FFTXT", "were fast flats");
    }
    if (sccount == 1) {
      msgSetc( "NDTXT", "was science");
    } else {
      msgSetc( "NDTXT", "were science");
    }

    /* This might be a useful message */
    msgOutif( MSG__NORM, " ", "Out of ^TOT input files, ^DK ^DKTXT, ^FF ^FFTXT "
              "and ^ND ^NDTXT", status );
  }

  if (meanstep && *meanstep != VAL__BADD) {
    msgOutiff( MSG__VERB, "", "Mean step time for input files = %g sec",
             status, *meanstep );
  }

  /* Store the heater efficiency map */
  if (*status != SAI__OK) heatermap = smf_free_effmap( heatermap, status );
  if (heateffmap) *heateffmap = heatermap;

  /* Now report the details of the observation */
  smf_obsmap_report( MSG__NORM, obsmap, objmap, status );

  obsmap = astAnnul( obsmap );
  objmap = astAnnul( objmap );
  scimap = astAnnul( scimap );

  msgOutiff( SMF__TIMER_MSG, "",
             "Took %.3f s to find science observations",
             status, smf_timerupdate( &tv1, &tv2, status ) );

  return;
}
예제 #8
0
void smurf_flatfield( int *status ) {

  smfArray *bbms = NULL;     /* Bad bolometer masks */
  smfData *ffdata = NULL;   /* Pointer to output data struct */
  Grp *fgrp = NULL;         /* Filtered group, no darks */
  smfArray *flatramps = NULL;/* Flatfield ramps */
  AstKeyMap *heateffmap = NULL;    /* Heater efficiency data */
  size_t i = 0;             /* Counter, index */
  Grp *igrp = NULL;         /* Input group of files */
  Grp *ogrp = NULL;         /* Output group of files */
  size_t outsize;           /* Total number of NDF names in the output group */
  size_t size;              /* Number of files in input group */

  /* Main routine */
  ndfBegin();

  /* Get input file(s) */
  kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status );

  /* Filter out darks */
  smf_find_science( NULL, igrp, &fgrp, 0, NULL, NULL, 1, 1, SMF__NULL, NULL,
                    &flatramps, &heateffmap, NULL, status );

  /* input group is now the filtered group so we can use that and
     free the old input group */
  size = grpGrpsz( fgrp, status );
  grpDelet( &igrp, status);
  igrp = fgrp;
  fgrp = NULL;

  if (size > 0) {
    /* Get output file(s) */
    kpg1Wgndf( "OUT", igrp, size, size, "More output files required...",
               &ogrp, &outsize, status );
  } else {
    msgOutif(MSG__NORM, " ","All supplied input frames were DARK,"
       " nothing to flatfield", status );
  }

  /* Get group of bolometer masks and read them into a smfArray */
  smf_request_mask( NULL, "BBM", &bbms, status );

  for (i=1; i<=size; i++ ) {
    int didflat;

    if (*status != SAI__OK) break;

    /* Call flatfield routine */
    didflat = smf_open_and_flatfield( NULL, igrp, ogrp, i, NULL, flatramps,
                                      heateffmap, &ffdata, status);

    /* Report failure by adding a message indicating which file failed */
    msgSeti("I",i);
    if (*status != SAI__OK) {
      msgSeti("N",size);
      errRep(FUNC_NAME,	"Unable to flatfield data from file ^I of ^N", status);
      break;
    }

    /* in verbose mode report whether flatfielding occurred or not */
    if (!didflat) {
      msgOutif(MSG__VERB," ",
	     "Data from file ^I are already flatfielded", status);
    } else {
      msgOutif(MSG__VERB," ", "Flat field applied to file ^I", status);
    }

    /* Mask out bad bolometers - mask data array not quality array */
    smf_apply_mask( NULL, ffdata, bbms, SMF__BBM_DATA, 0, status );

    /* Free resources for output data */
    smf_close_file( NULL, &ffdata, status );
  }

  /* Write out the list of output NDF names, annulling the error if a null
     parameter value is supplied. */
  if( *status == SAI__OK && ogrp ) {
    grpList( "OUTFILES", 0, 0, NULL, ogrp, status );
    if( *status == PAR__NULL ) errAnnul( status );
  }

  /* Tidy up after ourselves: release the resources used by the grp routines  */
  if (igrp) grpDelet( &igrp, status);
  if (ogrp) grpDelet( &ogrp, status);
  if (bbms) smf_close_related( NULL, &bbms, status );
  if( flatramps ) smf_close_related( NULL, &flatramps, status );
  if (heateffmap) heateffmap = smf_free_effmap( heateffmap, status );
  ndfEnd( status );
}