void smurf_calcdark( int *status ) { smfArray *bbms = NULL; /* Bad bolometer masks */ smfArray *darks = NULL; /* set of processed darks */ Grp *dgrp = NULL; /* Group of darks */ size_t i; /* Loop index */ int indf; /* NDF identifier for input file */ 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 non-darks and reduce the darks themselves */ smf_find_science( igrp, NULL, 0, &dgrp, NULL, 1, 0, SMF__DOUBLE, &darks, NULL, NULL, NULL, status ); /* no longer need the input group */ grpDelet( &igrp, status ); /* Get output file(s) */ size = grpGrpsz( dgrp, status ); kpg1Wgndf( "OUT", dgrp, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get group of bolometer masks and read them into a smfArray */ smf_request_mask( "BBM", &bbms, status ); for (i=1; i<=size && *status == SAI__OK; i++ ) { smfData * dark = (darks->sdata)[i-1]; /* This dark */ /* Open input file and create output file. Do not propagate since we do not want to get a large file the wrong size */ ndgNdfas( dgrp, i, "READ", &indf, status ); smf_apply_mask( dark, bbms, SMF__BBM_DATA, 0, status ); smf_write_smfData( dark, NULL, NULL, ogrp, i, indf, MSG__VERB, status ); ndfAnnul( &indf, status); } /* Tidy up after ourselves: release the resources used by the grp routines */ grpDelet( &dgrp, status); grpDelet( &ogrp, status); smf_close_related( &darks, status ); smf_close_related( &bbms, status ); ndfEnd( status ); }
void smf_get_grp_metadata( const Grp *grp, const char *name, char *value, int *status ){ /* Local Variables: */ Grp *slave; /* Slave group */ char buff[ GRP__SZNAM + 1 ]; /* Buffer for group element text */ size_t size; /* Size of slave group */ size_t i; /* Index of current group element */ size_t nlen; /* Length of supplied name */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Get the slave group that is owned by the supplied group. */ slave = grpSlave( grp, status ); /* Return the buffer unchanged if no slave group is available. */ if( slave ) { /* Record the length of the name of the metadata item. */ nlen = strlen( name ); /* Sadly we need to check each element in the group to find any that match the name. */ size = grpGrpsz( slave, status ); for( i = 1; i <= size; i++ ) { /* Get the text of the i'th element from the group. */ grpInfoc( slave, i, "NAME", buff, sizeof( buff ), status ); /* If it is of the form "<name>=..." we have found the required item. */ if( !strncmp( buff, name, nlen ) && buff[ nlen ] == '=' ) { /* Copy the value into the returned buffer. */ strcpy( value, buff + nlen + 1 ); /* Leave the loop. */ break; } } } }
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(); }
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, &litudes, 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, &litudes, 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 ); }
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 ); }
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); } }
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); } }
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 ); }
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; }
void cupidStoreConfig( HDSLoc *loc, AstKeyMap *config, int *status ){ /* *+ * Name: * cupidStoreConfig * Purpose: * Store the configuraton used by CLUMPS in the given CUPID extension. * Language: * Starlink C * Synopsis: * void cupidStoreConfig( HDSLoc *loc, AstKeyMap *config, int *status ) * Description: * This function extracts each keyword/value pair from the given * configuration keymap, and stores them in the CUPID extension. * Parameters: * loc * HDS locator for the CUPID extension. * config * An AST KeyMap holding the configuration parameters. * status * Pointer to the inherited status value. * Copyright: * Copyright (C) 2005 Particle Physics & Astronomy Research Council. * All Rights Reserved. * Licence: * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA * 02110-1301, USA * Authors: * DSB: David S. Berry * TIMJ: Tim Jenness (JAC, Hawaii) * {enter_new_authors_here} * History: * 9-NOV-2005 (DSB): * Original version. * 15-JUL-2008 (TIMJ): * Tweak to GRP C API. * {enter_further_changes_here} * Bugs: * {note_any_bugs_here} *- */ /* Local Variables: */ Grp *grp; /* Pointer to group */ HDSLoc *aloc; /* HDS locator for entire CONFIG array */ HDSLoc *cloc; /* HDS locator for single cell of ONFIG array */ char name[ GRP__SZNAM + 1 ];/* Value extracted from GRP group */ char *pname; /* Pointer to pass to grpGet */ int el; /* Index of element to store */ int i; /* Index of next entry in group */ int n; /* Number of entries in group */ int nc; /* Number of characters in group entry */ int ncmax; /* Max number of characters in any group entry */ int subs[ 1 ]; /* Array containing required cell index */ int there; /* Does component exist?*/ /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return; /* Initialise all HDS locator pointers to NULL since HDS now objects if it receives an uninitialised pointer. */ aloc = NULL; cloc = NULL; /* Create a GRP group containing the required text. */ grp = NULL; kpg1Kygrp( config, &grp, status ); /* Get the number of values in the group. Pass on if it is zero. */ n = grpGrpsz( grp, status ); if( n ) { /* We need to pass a pointer to the "name" variable to grpGet */ pname = name; /* Scan the group to find the length of the longest value. */ ncmax = 0; for( i = 1; i <= n; i++ ) { grpGet( grp, i, 1, &pname, GRP__SZNAM + 1, status ); nc = astChrLen( name ); if( nc > ncmax ) ncmax = nc; } /* Create a suitable array of character strings in the CUPID extension, and get a locator for the whole array. */ datThere( loc, "CONFIG", &there, status ); if( there ) datErase( loc, "CONFIG", status ); datNewC( loc, "CONFIG", ncmax + 1, 1, &n, status ); datFind( loc, "CONFIG", &aloc, status ); /* Store each list item in the new array. */ el = 1; for( i = 0; i < n; i++ ) { subs[ 0 ]= i + 1; cloc = NULL; datCell( aloc, 1, subs, &cloc, status ); grpGet( grp, subs[ 0 ], 1, &pname, GRP__SZNAM + 1, status ); nc = astChrLen( pname ); datPutC( cloc, 0, &el, pname, astChrLen( name ), status ); datAnnul( &cloc, status ); } /* Free resources. */ datAnnul( &aloc, status ); } /* Delete the group */ grpDelet( &grp, status ); }
void smf_flat_write( smf_flatmeth flatmeth, const char * flatname, double refres, const smfData * bolval, const smfData * powref, const smfData * bolref, const smfData * polyfit, const Grp * prvgrp, int * status ) { size_t colsize; /* number of columns */ double *dbuf = NULL; /* input double buffer for mean data */ double *dvar = NULL; /* input double buffer for variance of data */ char fitsrec[SC2STORE__MAXFITS*SZFITSCARD+1]; /* Store for FITS records */ int *ibuf = NULL; /* int buffer for mean data */ int indf = NDF__NOID; /* NDF identifier for output file */ size_t ncards; /* number of fits cards */ size_t numbols; /* number of bolometers */ double *outvar = NULL; /* buffer for variance of data */ int place = NDF__NOPL; /* Dummy placeholder for NDF */ size_t rowsize; /* number of rows */ JCMTState *state = NULL; /* State for this flatfield */ sc2ast_subarray_t subnum; /* subarray number */ AstFrameSet *result, *spacefset; AstLutMap *heatmap; AstFrame *heatfrm; int *dksquid; /* pointer to dummy dark SQUID data */ size_t j; /* loop counter */ int jig_vert[1][2]; /* dummy jiggle vertices */ double jig_path[1][2]; /* dummy jiggle path */ size_t nframes = 0; /* Number of frames in bolval */ int npath = 0; /* size of jiggle path */ int nvert = 0; /* number of jiggle vertices */ char *xmlfile = NULL; /* dummy xmlfile name */ if (*status != SAI__OK) return; if (!bolval->da) { *status = SAI__ERROR; errRep( "", "No flatfield solution provided for writing", status ); return; } if (!bolval->da->heatval) { *status = SAI__ERROR; errRep( "", "Must provide heater values in DA struct to smf_flat_write" " (possible programming error)", status ); return; } /* note that colsize is the number of rows and rowsize is the number of columns */ colsize = (bolval->dims)[SC2STORE__ROW_INDEX]; rowsize = (bolval->dims)[SC2STORE__COL_INDEX]; numbols = colsize * rowsize; nframes = (bolval->dims)[2]; /* Make sure we have a FLAT header that reflects this file as the flatfield solution */ smf_fits_updateS( bolval->hdr, "FLAT", flatname, "Name of flat-field file", status ); /* Create a FITS header for DA */ smf_fits_export2DA( bolval->hdr->fitshdr, &ncards, fitsrec, status ); /* Copy the data as integers so it can be written to data file. To prevent overflow in the variance we store that as doubles */ ibuf = astMalloc( (numbols * nframes)*sizeof(*ibuf) ); outvar = astMalloc( (numbols * nframes)*sizeof(*outvar) ); dbuf = (bolval->pntr)[0]; dvar = (bolval->pntr)[1]; if (*status == SAI__OK) { for (j = 0; j < (nframes * numbols); j++) { /* These started off as integers so the mean value must fit in an integer */ if ( dbuf[j] == VAL__BADD) { ibuf[j] = VAL__BADI; } else { ibuf[j] = (int)dbuf[j]; } /* Same data type so no need to convert bad values */ if (dvar) { outvar[j] = dvar[j]; } else { outvar[j] = VAL__BADD; } } } /* get subarray number */ smf_find_subarray( bolval->hdr, NULL, 0, &subnum, status ); /* Create dummy components for output file */ dksquid = astCalloc ( rowsize* nframes, sizeof(*dksquid) ); jig_vert[0][0] = 0; jig_vert[0][1] = 0; jig_path[0][0] = 0.0; jig_path[0][1] = 0.0; sc2store_setcompflag ( SC2STORE__NONE, status ); sc2store_wrtstream ( flatname, subnum, ncards, fitsrec, colsize, rowsize, nframes, (bolref->dims)[2], refres, 0, smf_flat_methstring( flatmeth, status ), bolval->hdr->allState, NULL, ibuf, dksquid, (bolref->pntr)[0], (powref->pntr)[0], "FLATCAL", NULL, NULL, jig_vert, nvert, jig_path, npath, xmlfile, status ); sc2store_free ( status ); /* To copy in the variance and modify fix up the WCS we need to reopen the file */ ndfOpen( NULL, flatname, "UPDATE", "OLD", &indf, &place, status ); /* make sure that history is not written twice */ ndfHsmod( "SKIP", indf, status ); if (outvar) { void *pntr[3]; int el; ndfStype( "_DOUBLE", indf, "VARIANCE", status ); ndfMap( indf, "VARIANCE", "_DOUBLE", "WRITE", pntr, &el, status ); if (*status == SAI__OK) { memcpy( pntr[0], outvar, sizeof(*outvar)*el ); } } /* For the WCS a time frame is less relevant than heater settings */ astBegin; /* Create frame for focal plane coordinates */ sc2ast_createwcs( subnum, NULL, NULL, NULL, NO_FTS, &spacefset, status ); /* Copy it to make sure we do not mess with the cache */ result = astCopy( spacefset ); /* and switch to BOLO frame which is best for bolometer analysis */ { int frnum = AST__NOFRAME; kpg1Asffr( result, "BOLO", &frnum, status ); if (frnum != AST__NOFRAME) astSetI( result, "CURRENT", frnum ); } /* Create a simple frame for heater settings */ heatfrm = astFrame( 1, "Domain=HEATER,Label(1)=Heater Setting" ); heatmap = astLutMap( nframes, bolval->da->heatval, 1.0, 1.0, " " ); /* Append the heater axis to the spatial frameset */ atlAddWcsAxis( result, (AstMapping *)heatmap, (AstFrame *) heatfrm, NULL, NULL, status ); /* write it to the NDF */ ndfPtwcs( result, indf, status ); /* Write provenance information */ if (prvgrp) { size_t size = grpGrpsz( prvgrp, status ); char prvname[ 2 * PAR__SZNAM + 1]; smf_get_taskname( NULL, prvname, status ); for (j=1; j<=size; j++) { smf_accumulate_prov( NULL, prvgrp, j, indf, prvname, NULL, status ); } } /* Write the polynomial expansion into an extension */ if (polyfit) { char fitfile[GRP__SZNAM+1]; int fndf = NDF__NOID; place = NDF__NOPL; one_strlcpy( fitfile, flatname, sizeof(fitfile), status ); one_strlcat( fitfile, ".MORE.SMURF.FLATFIT", sizeof(fitfile), status ); /* create the file */ smf_write_smfData( polyfit, NULL, fitfile, NULL, 0, NDF__NOID, MSG__VERB, 0, status ); /* Same WCS as the main file */ ndfOpen( NULL, fitfile, "UPDATE", "OLD", &fndf, &place, status ); ndfPtwcs( result, fndf, status ); ndfAnnul( &fndf, status ); } astEnd; ndfAnnul( &indf, status); if (ibuf) ibuf = astFree( ibuf ); if (outvar) outvar = astFree( outvar ); if (dksquid) dksquid = astFree( dksquid ); if (state) state = astFree( state ); }
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 ); }
void smurf_sc2filtermap( int *status ) { Grp *fgrp = NULL; /* Output filter group */ smfFilter *filt=NULL; /* Filter */ double filt_edgehigh=0; /* High-pass filter */ double filt_edgelow=0; /* Low-pass filter */ size_t fsize; /* Number of files in fgrp */ size_t i; /* Loop (grp) counter */ smfData *idata; /* Pointer to input smfData */ Grp *igrp = NULL; /* Input group of files */ int isfft=0; /* Are data fft or real space? */ int *mask=NULL; /* Mask indicating where bad data are */ size_t ndata=0; /* Number of pixels in the map */ size_t ndims; /* Number of real space dimensions */ smfData *odata=NULL; /* Pointer to output smfData to be exported */ Grp *ogrp = NULL; /* Output group of files */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ smfData *wrefmap=NULL; /* Whitening reference map */ int whiten; /* Applying whitening filter? */ Grp *wgrp = NULL; /* Whitening reference map group */ size_t wsize; /* Size of wgrp */ 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 ); size = grpGrpsz( igrp, status ); if (size > 0) { int parstate=0; /* ADAM parameter state */ /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); /* Write out the filter? */ parState( "OUTFILTER", &parstate, status ); if( parstate != PAR__GROUND ) { kpg1Wgndf( "OUTFILTER", igrp, size, size, "More output filter files required...", &fgrp, &fsize, status ); } } /* Are we going to zero bad values first? */ parGet0l( "ZEROBAD", &zerobad, status ); /* High/low-pass filters? */ parGet0d( "FILT_EDGEHIGH", &filt_edgehigh, status ); parGet0d( "FILT_EDGELOW", &filt_edgelow, status ); /* Are we applying a spatial whitening filter? */ parGet0l( "WHITEN", &whiten, status ); if( whiten ) { /* We also need the reference map to measure the whitening filter. We make a deep copy of it so that we can set bad values to 0 etc. */ smfData *tempdata=NULL; kpg1Rgndf( "whiterefmap", 0, 1, "", &wgrp, &wsize, status ); if( (*status == SAI__OK) && (wsize != 1) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": WHITEREFMAP must be a single reference map", status ); } smf_open_file( wgrp, 1, "READ", SMF__NOTTSERIES, &tempdata, status ); wrefmap = smf_deepcopy_smfData( tempdata, 0, 0, 0, 0, status ); smf_close_file( &tempdata, status ); /* Set VAL__BADD to zero if requested */ if( (*status==SAI__OK) && zerobad ) { double *d=NULL; size_t j; ndata=1; for( j=0; j<wrefmap->ndims; j++ ) ndata *= wrefmap->dims[j]; d = wrefmap->pntr[0]; if( d ) { for( j=0; j<ndata; j++ ) { if( d[j] == VAL__BADD ) { d[j] = 0; } } } } } for( i=1;(*status==SAI__OK)&&i<=size; i++ ) { smf_open_file( igrp, i, "READ", SMF__NOTTSERIES, &idata, status ); isfft = smf_isfft( idata, NULL, NULL, NULL, NULL, &ndims, status); if( (*status==SAI__OK) && isfft ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Input data are FFT, not real-space!\n", status ); break; } if( (*status==SAI__OK) && (ndims != 2) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Input data not a 2D map!\n", status ); break; } /* smf_filter_execute operates in-place, so first create the output data as a copy of the input */ odata = smf_deepcopy_smfData( idata, 0, 0, 0, 0, status ); /* Set VAL__BADD to zero if requested */ if( (*status==SAI__OK) && zerobad ) { double *d=NULL; size_t j, k; ndata=1; for( j=0; j<odata->ndims; j++ ) ndata *= odata->dims[j]; mask = astCalloc( ndata, sizeof(*mask) ); /* Do both DATA and VARIANCE */ if( *status == SAI__OK ) { for( k=0; k<2; k++ ) { d = odata->pntr[k]; if( d ) { for( j=0; j<ndata; j++ ) { if( d[j] == VAL__BADD ) { d[j] = 0; mask[j] = 1; } } } } } } /* Measure and apply the whitening filter. We need to do this every time because the dimensions of filt need to match idata (not the wrefmap) and they might be different each time. We could try to be more clever in the future if this is too slow. */ filt = smf_create_smfFilter( idata, status ); /* Set to the identity in case no whitening is applied */ msgOut( "", TASK_NAME ": initializing filter", status ); smf_filter_ident( filt, 0, status ); if( whiten ) { msgOut( "", TASK_NAME ": whitening the filter", status ); smf_filter2d_whiten( wf, filt, wrefmap, 0, 0, 3, status ); } if( filt_edgelow ) { msgOutf( "", TASK_NAME ": applying low-pass at < %lg 1/arcsec", status, filt_edgelow ); smf_filter2d_edge( filt, filt_edgelow, 1, status ); } if( filt_edgehigh ) { msgOutf( "", TASK_NAME ": applying high-pass at >= %lg 1/arcsec", status, filt_edgehigh ); smf_filter2d_edge( filt, filt_edgehigh, 0, status ); } smf_filter_execute( wf, odata, filt, 0, 0, status ); /* Set bad values from the mask */ if( mask ) { double *d=NULL; size_t j, k; /* Do both DATA and VARIANCE */ for( k=0; k<2; k++ ) { d = odata->pntr[k]; if( d ) { for( j=0; j<ndata; j++ ) { if( mask[j] ) { d[j] = VAL__BADD; } } } } } /* Export the data to a new file */ smf_write_smfData( odata, NULL, NULL, ogrp, i, 0, MSG__NORM, status ); /* Write out filters? */ if( fgrp ) smf_write_smfFilter( filt, NULL, fgrp, i, status ); if( filt ) smf_free_smfFilter( filt, status ); } /* Tidy up after ourselves */ if( fgrp ) grpDelet( &fgrp, status); if( igrp ) grpDelet( &igrp, status); if( ogrp ) grpDelet( &ogrp, status); if( wgrp ) grpDelet( &wgrp, status ); if( odata ) smf_close_file( &odata, status ); if( wrefmap ) smf_close_file( &wrefmap, status ); if( mask ) mask = astFree( mask ); ndfEnd( status ); /* Ensure that FFTW doesn't have any used memory kicking around */ fftw_cleanup(); }
void smurf_jsatilelist( int *status ) { /* Local Variables */ AstFitsChan *fc = NULL; AstFrameSet *fs = NULL; AstObject *obj; AstRegion *region; Grp *igrp = NULL; Grp *sgrp = NULL; double vertex_data[ 2*MAXVERT ]; int *tiles = NULL; int i; int indf; int lbnd[2]; int ntile; int nvert_dec; int nvert_ra; int ubnd[2]; size_t size; size_t ssize; smfJSATiling tiling; /* Check inherited status */ if( *status != SAI__OK ) return; /* Start a new AST context. */ astBegin; /* Attempt to to get an AST Region. */ kpg1Gtobj( "IN", "Region", (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion), &obj, status ); region = (AstRegion *) obj; /* If successful, attempt to access the IN parameter as an NDF. If this works, we may be able to determine the instrument by looking at its FITS extension. */ if( *status == SAI__OK && region ) { ndfExist( "IN", "Read", &indf, status ); /* If we got an NDF, get a FitsChan holding the contents of its FITS extension. Annul the error if the NDF has no FITS extension. */ if( indf != NDF__NOID ) { kpgGtfts( indf, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = NULL; } ndfAnnul( &indf, status ); } /* Select a JSA instrument and get the parameters defining the layout of tiles for the selected instrument. */ smf_jsainstrument( "INSTRUMENT", fc, SMF__INST_NONE, &tiling, status ); /* Get the list of identifiers for tiles that overlap the region. */ tiles = smf_jsatiles_region( region, &tiling, &ntile, status ); /* If a null value was supplied for IN, attempt to get the positions of vertices on the sky to define the region. */ } else if( *status == PAR__NULL ) { errAnnul( status ); parGet1d( "VERTEX_RA", MAXVERT, vertex_data, &nvert_ra, status ); parGet1d( "VERTEX_DEC", MAXVERT, vertex_data + MAXVERT, &nvert_dec, status ); if( nvert_ra != nvert_dec && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "Differing numbers of RA (%d) and Dec (%d) vertex values " "supplied.", status, nvert_ra, nvert_dec ); } /* Convert from degrees to radians. */ for( i = 0; i < nvert_ra; i++ ) { vertex_data[ i ] *= AST__DD2R; vertex_data[ MAXVERT + i ] *= AST__DD2R; } /* Select a JSA instrument and get the parameters defining the layout of tiles for the selected instrument. */ smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &tiling, status ); /* Create a frame in which to define the region - we arbitrarily use tile 1. */ smf_jsatile( 1, &tiling, 0, NULL, &fs, NULL, lbnd, ubnd, status ); /* Create the region. */ region = (AstRegion *) astPolygon( fs, nvert_ra, MAXVERT, vertex_data, NULL, " " ); /* If the region is unbounded, it is probably because the vertices were given in the wrong order. Invert the Polyfon to correct this. */ if( !astGetI( region, "bounded" ) ) astNegate( region ); /* Get the list of identifiers for tiles that overlap the region. */ tiles = smf_jsatiles_region( region, &tiling, &ntile, status ); } /* If the IN parameter could not be accessed as a Region, annull any error and get a group of input data files. */ if( !region || *status == SAI__ERROR ) { if( *status != SAI__OK ) errAnnul( status ); kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Get a group containing just the files holding science data. */ smf_find_science( NULL, igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, NULL, NULL, NULL, NULL, status ); /* Check we have at least once science file. */ ssize = grpGrpsz( sgrp, status ); if( ssize == 0 ) { msgOutif( MSG__NORM, " ", "None of the supplied input frames were SCIENCE.", status ); /* Get the list of identifiers for tiles that receive any data. */ } else { tiles = smf_jsatiles_data( sgrp, ssize, &tiling, &ntile, status ); } /* Delete the groups. */ if( igrp ) grpDelet( &igrp, status); if( sgrp ) grpDelet( &sgrp, status); } /* Sort the list of overlapping tiles into ascending order. */ if( *status == SAI__OK ) { qsort( tiles, ntile, sizeof( *tiles ), jsatilelist_icomp ); /* Display the list of overlapping tiles. */ msgBlank( status ); msgOutf( "", " %s tiles touched by supplied data:", status, tiling.name ); msgBlank( status ); for( i = 0; i < ntile; i++ ) { msgSeti( "I", tiles[ i ] ); msgOut( "", " ^I", status ); } msgBlank( status ); /* Write out the list of overlapping tiles to the output parameter. */ parPut1i( "TILES", ntile, tiles, status ); } /* Free resources. */ tiles = astFree( tiles ); /* End the AST context. */ astEnd; /* Issue a status indication.*/ msgBlank( status ); if( *status == SAI__OK ) { msgOutif( MSG__VERB, "", "JSATILELIST succeeded.", status); } else { msgOutif( MSG__VERB, "", "JSATILELIST failed.", status); } }
void smurf_smurfcopy ( int * status ) { smfData * data = NULL; /* input file struct */ size_t dtypsz; /* Number of bytes in data type */ Grp *fgrp = NULL; /* Filtered group, no darks */ size_t i; /* Loop counter */ smfFile * ifile = NULL; /* Input smfFile */ Grp *igrp = NULL; /* Input group */ unsigned char * inptr = NULL; /* Pointer to start of section to copy */ int islice; /* int time slice from parameter */ int lbnd[2]; /* Lower coordinate bounds of output file */ size_t nelem; /* Number of elements to copy */ smfData * odata = NULL; /* output file struct */ size_t offset; /* offset into data array */ smfFile * ofile = NULL; /* output smfFile */ Grp *ogrp = NULL; /* Output group */ size_t outsize; /* Total number of NDF names in the output group */ dim_t slice; /* Time index to extract */ size_t size; /* Number of files in input group */ int ubnd[2]; /* Upper coordinate bounds of output file */ if (*status != SAI__OK) return; ndfBegin(); /* Read the input file */ /* As a proof of concept do not allow multiple input files */ kpg1Rgndf( "IN", 1, 1, "", &igrp, &size, status ); /* Filter out darks */ smf_find_science( igrp, &fgrp, 1, NULL, NULL, 0, 0, SMF__NULL, NULL, NULL, NULL, 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 extract", status ); } /* Allow the user to specify a text file containing a table of pointing corrections. Corresponding Mappings are created form the column data in this table and stored in the "igrp" group as items of metadata. */ smf_pread( igrp, "POINTING", status ); /* Use a loop so that we look like other routines and simplify the change if we support multiple input files */ for (i=1; i<=size; i++) { /* Open the input file using standard routine */ smf_open_and_flatfield( igrp, NULL, i, NULL, NULL, NULL, &data, status ); if (*status != SAI__OK) break; if (*status == SAI__OK) { if (!data->file->isTstream || data->ndims != 3) { smf_close_file( &data, status ); *status = SAI__ERROR; errRep(" ", "Input data do not represent time series", status); break; } } /* get the slice position - knowing the maximum allowed Somewhat problematic in a loop if we want to allow different slices per file. Best bet is to allow multiple slices in a single file but only one file. */ msgSeti( "MAX", (data->dims)[2] ); msgOutif( MSG__NORM, " ", "File has ^MAX slices.", status ); parGdr0i( "SLICE",1, 0, (data->dims)[2], 1, &islice, status); slice = islice; if (slice == 0) slice = (data->dims)[2]; /* construct output bounds */ lbnd[0] = (data->lbnd)[0]; lbnd[1] = (data->lbnd)[1]; ubnd[0] = lbnd[0] + (data->dims)[0] - 1; ubnd[1] = lbnd[1] + (data->dims)[1] - 1; /* Open an output file (losing history) but we do not want to propagate the full NDF size to the output file */ smf_open_newfile( ogrp, i, data->dtype, 2, lbnd, ubnd, 0, &odata, status ); ofile = odata->file; ifile = data->file; /* protect against null pointer smfFile */ if (*status == SAI__OK) { /* sort out provenance */ smf_accumulate_prov( data, igrp, i, ofile->ndfid, "SMURF:SMURFCOPY", NULL, status ); /* copy the slice in */ dtypsz = smf_dtype_size( odata, status ); nelem = (data->dims)[0] * (data->dims)[1]; offset = (slice - 1) * nelem * dtypsz; inptr = (data->pntr)[0]; memcpy( (odata->pntr)[0], inptr + offset, nelem * dtypsz ); /* World coordinates - note the 0 indexing relative to GRID */ smf_tslice_ast( data, slice-1, 1, status ); ndfPtwcs( data->hdr->wcs, ofile->ndfid, status ); /* Write the FITS header */ kpgPtfts( ofile->ndfid, data->hdr->fitshdr, status ); /* JCMTSTATE */ sc2store_writejcmtstate( ofile->ndfid, 1, &((data->hdr->allState)[slice-1]), status ); } /* cleanup */ smf_close_file( &data, status ); smf_close_file( &odata, status ); } /* tidy */ if (igrp) { smf_pread( igrp, NULL, status ); grpDelet( &igrp, status ); } if (ogrp) grpDelet( &ogrp, status ); ndfEnd(status); }