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_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_flat_params( const smfData * refdata, const char resistpar[], const char methpar[], const char orderpar[], const char snrminpar[], double * refohms, double **resistance, int * outrows, int * outcols, smf_flatmeth *flatmeth, int * order, double * snrmin, smfData ** heateff, int * status ) { dim_t datarows = 0; /* Number of rows in refdata */ dim_t datacols = 0; /* Number of columns in refdata */ size_t j = 0; /* Counter, index */ char method[SC2STORE_FLATLEN]; /* flatfield method string */ size_t nbols; /* Number of bolometers */ double refohmsval = 0.0; /* Internal version of refohms */ AstKeyMap * resmap = NULL; /* Resistor map */ AstKeyMap * subarrays = NULL; /* Subarray lookup table */ char thissub[32]; /* This sub-instrument string */ if (resistance) *resistance = NULL; if (*status != SAI__OK) return; if (!refdata) { *status = SAI__ERROR; errRep( "", "Must provide reference data file to calculate flatfield parameters" " (possible programming error)", status ); return; } /* Based on refdata we now need to calculate the default reference resistance and retrieve the correct heater efficiency file for each array. We need the unique subarray string so that we can set up a look up keymap. There is no code in SMURF to return all the known subarrays but we need to know all the options in order to use kpg1Config. */ subarrays = astKeyMap( " " ); astMapPut0I( subarrays, "CG450MK2_M0907D0501", 0, NULL ); astMapPut0I( subarrays, "CG850MK2_M0904D0503", 0, NULL ); astMapPut0I( subarrays, "SG850_M0906D1005", 0, NULL ); astMapPut0I( subarrays, "SG850_M1002D1006", 0, NULL ); astMapPut0I( subarrays, "SG850_M1005D1007", 0, NULL ); astMapPut0I( subarrays, "SG850_M1003D1004", 0, NULL ); astMapPut0I( subarrays, "SG450_M1004D1000", 0, NULL ); astMapPut0I( subarrays, "SG450_M1007D1002", 0, NULL ); astMapPut0I( subarrays, "SG450_M1006D1003", 0, NULL ); astMapPut0I( subarrays, "SG450_M1009D1008", 0, NULL ); /* and indicate which subarray we are interested in (uppercased) */ smf_fits_getS( refdata->hdr, "ARRAYID", thissub, sizeof(thissub), status ); { /* need to uppercase */ size_t l = strlen(thissub); for (j=0;j<l;j++) { thissub[j] = toupper(thissub[j]); } } astMapPut0I( subarrays, thissub, 1, NULL ); /* Read the config file */ resmap = kpg1Config( resistpar, "$SMURF_DIR/smurf_calcflat.def", subarrays, 1, status ); subarrays = astAnnul( subarrays ); if (*status != SAI__OK) goto CLEANUP; /* Read the reference resistance */ astMapGet0D( resmap, "REFRES", &refohmsval ); if (refohms && *status == SAI__OK) { *refohms = refohmsval; msgOutiff(MSG__VERB, "", "Read reference resistance for subarray %s of %g ohms\n", status, thissub, *refohms ); } /* We no longer want to read per-bolometer resistor values from the config file. To retain backwards compatibility with the current implementation of smf_flat_standardpow we simply fill the per-bol resistance array with the reference resistance which effectively disables smf_flat_standardpow */ smf_get_dims( refdata, &datarows, &datacols, NULL, NULL, NULL, NULL, NULL, status ); nbols = datacols * datarows; if (*status == SAI__OK && resistance ) { *resistance = astMalloc( nbols*sizeof(**resistance) ); for (j = 0; j < (size_t)nbols; j++) { (*resistance)[j] = refohmsval; } } /* Get the heater efficiency file */ if (heateff && astMapHasKey( resmap, "HEATEFF" ) ) { const char * heateffstr = NULL; if (astMapGet0C( resmap, "HEATEFF", &heateffstr )) { Grp * heateffgrp = NULL; smfData * heatefftmp = NULL; heateffgrp = grpNew( "heateff", status ); grpPut1( heateffgrp, heateffstr, 0, status ); smf_open_file( NULL, heateffgrp, 1, "READ", SMF__NOTTSERIES|SMF__NOFIX_METADATA, &heatefftmp, status ); /* Divorce the smfData from the underlying file. This file stays open for the entire duration of the data processing and can some times lead to issues when we attempt to close it an hour after we opened it (it's usually on an NFS disk) */ if (*status == SAI__OK) { *heateff = smf_deepcopy_smfData( NULL, heatefftmp, 0, SMF__NOCREATE_FILE | SMF__NOCREATE_FTS | SMF__NOCREATE_DA, 0, 0, status ); smf_close_file(NULL, &heatefftmp, status); } /* Check the dimensions */ if (*status == SAI__OK) { dim_t heatrows = 0; dim_t heatcols = 0; smf_get_dims( *heateff, &heatrows, &heatcols, NULL, NULL, NULL, NULL, NULL, status ); if (*status == SAI__OK) { if ( datarows != heatrows || datacols != heatcols ) { *status = SAI__ERROR; errRepf( "", "Dimensions of heater efficiency file %s are (%zu, %zu)" " but flatfield has dimensions (%zu, %zu)", status, heateffstr, (size_t)heatrows, (size_t)heatcols, (size_t)datarows, (size_t)datacols); } } if (*status == SAI__OK) { smf_dtype_check_fatal( *heateff, NULL, SMF__DOUBLE, status ); if (*status == SMF__BDTYP) { errRepf("", "Heater efficiency data in %s should be double precision", status, heateffstr); } } if (*status == SAI__OK) { char heateffarrid[32]; smf_fits_getS( refdata->hdr, "ARRAYID", heateffarrid, sizeof(heateffarrid), status ); if (*status != SAI__OK) errAnnul( status ); if (strcasecmp( thissub, heateffarrid ) != 0 ) { if (*status == SAI__OK) { *status = SAI__ERROR; errRepf("", "Subarray associated with heater efficiency image (%s)" " does not match that of the data to be flatfielded (%s)", status, heateffarrid, thissub ); } } } } if (heateffgrp) grpDelet( &heateffgrp, status ); } } if (methpar && flatmeth) { /* See if we want to use TABLE or POLYNOMIAL mode */ parChoic( methpar, "POLYNOMIAL", "POLYNOMIAL, TABLE", 1, method, sizeof(method), status ); *flatmeth = smf_flat_methcode( method, status ); if (*flatmeth == SMF__FLATMETH_POLY) { /* need an order for the polynomial */ if (order && orderpar) { parGdr0i( orderpar, 1, 1, 3, 1, order, status ); /* and if the order is 1 then we can ask for the snr min */ if (snrminpar && *order == 1) { parGet0d( snrminpar, snrmin, status ); } } } else { /* need an snr min for table mode responsivities */ if (snrminpar) parGet0d( snrminpar, snrmin, status ); } } if (outrows) *outrows = datarows; if (outcols) *outcols = datacols; CLEANUP: resmap = astAnnul( resmap ); if (*status != SAI__OK) { if (resistance && *resistance) *resistance = astFree( *resistance ); if (heateff && *heateff) smf_close_file( NULL, heateff, status ); } return; }
/* Main entry */ void smurf_fixsteps( int *status ) { /* Local Variables */ AstKeyMap *keymap; /* Default config parameter values */ AstKeyMap *sub_instruments; /* Info about sub-instruments */ FILE *fd = NULL; /* File descriptor */ Grp *igrp = NULL; /* Input group of files */ Grp *ogrp = NULL; /* Output group of files */ dim_t dcfitbox; /* DCFITBOX config parameter */ dim_t dcsmooth; /* DCSMOOTH config parameter */ dim_t nx; /* Length of first pixel axis */ double dcthresh; /* DCTHRESH config parameter */ double sizetol; /* Tolerance allowed on step height */ int changed; /* Have any step fixes changed? */ int dclimcorr; /* DCLIMCORR config parameter */ int dcmaxsteps; /* DCMAXSTEPS config parameter */ int first; /* Index of first change to report */ int itemp; /* Intermediate value */ int meanshift; /* Use a mean shift filter? */ int nnew; /* Number of new step fixes */ int nold; /* Number of old step fixes */ size_t nrej; /* Number of rejected bolometers */ size_t outsize; /* Total number of NDF names in the output group */ size_t size; /* Number of files in input group */ smfData *data = NULL; /* Output smfData */ smfData *indata = NULL; /* Input smfData */ smfStepFix *newsteps = NULL; /* New step fix descriptions */ smfStepFix *oldsteps = NULL; /* Old step fix descriptions */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ /* Check inherited status */ if (*status != SAI__OK) return; /* begin an NDF context. */ ndfBegin(); /* Get the name of the input NDF. */ kpg1Rgndf( "IN", 1, 1, "", &igrp, &size, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, 0, "More output files required...", &ogrp, &outsize, status ); /* Open the input data file, read-only. */ smf_open_file( igrp, 1, "Read", 0, &indata, status ); /* Since we will be modifying the data values, we need a deep copy. */ data = smf_deepcopy_smfData( indata, 0, 0, 0, 0, status ); /* Place cleaning parameters into a keymap and set defaults. Note that we use the map-maker defaults file here 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 this application. */ sub_instruments = smf_subinst_keymap( SMF__SUBINST_NONE, data, NULL, 0, status ); keymap = kpg1Config( "CONFIG", "$SMURF_DIR/smurf_makemap.def", sub_instruments, status ); sub_instruments = astAnnul( sub_instruments ); /* Set the default for each of the step fixing config parameters. */ astMapGet0I( keymap, "DCSMOOTH", &itemp ); parDef0i( "DCSMOOTH", itemp, status ); astMapGet0I( keymap, "DCFITBOX", &itemp ); parDef0i( "DCFITBOX", itemp, status ); astMapGet0I( keymap, "DCMAXSTEPS", &itemp ); parDef0i( "DCMAXSTEPS", itemp, status ); astMapGet0I( keymap, "DCLIMCORR", &itemp ); parDef0i( "DCLIMCORR", itemp, status ); astMapGet0D( keymap, "DCTHRESH", &dcthresh ); parDef0d( "DCTHRESH", dcthresh, status ); /* Get values for the config params */ parGet0i( "DCSMOOTH", &itemp, status ); dcsmooth = itemp; parGet0i( "DCFITBOX", &itemp, status ); dcfitbox = itemp; parGet0i( "DCMAXSTEPS", &itemp, status ); dcmaxsteps = itemp; parGet0i( "DCLIMCORR", &itemp, status ); dclimcorr = itemp; parGet0d( "DCTHRESH", &dcthresh, status ); parGet0l( "MEANSHIFT", &meanshift, status ); /* Find the number of cores/processors available and create a pool of threads of the same size. */ wf = thrGetWorkforce( thrGetNThread( SMF__THREADS, status ), status ); /* Fix the steps. */ smf_fix_steps( wf, data, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, meanshift, &nrej, &newsteps, &nnew, status ); /* Display a summary of what was done by the step fixer. */ msgBlank( status ); if( nrej == 0 ) { msgOut( "", "No bolometers were rejected", status ); } else if( nrej == 1 ) { msgOut( "", "One bolometer was rejected", status ); } else { msgSeti( "NREJ", nrej ); msgOut( "", "^NREJ bolometers were rejected", status ); } parPut0i( "NREJECTED", nrej, status ); if( nnew == 0 ) { msgOut( "", "No steps were fixed", status ); } else if( nnew == 1 ) { msgOut( "", "One step was fixed", status ); } else { msgSeti( "NNEW", nnew ); msgOut( "", "^NNEW steps were fixed", status ); } parPut0i( "NFIXED", nnew, status ); /* If required, write out to a text file details of the steps that were fixed. */ fd = smf_open_textfile( "NEWSTEPS", "w", "<none>", status ); if( fd ) { smf1_write_steps( fd, indata, nnew, newsteps, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, nrej, status ); fclose( fd ); } /* If required, create the output NDF. */ if( outsize > 0 && indata && indata->file ) { smf_write_smfData( data, NULL, NULL, ogrp, 1, indata->file->ndfid, MSG__VERB, 0, status ); } /* Save the length of the first pixel axis. */ nx = data ? data->dims[ 0 ] : 0; /* Close the NDFs. */ smf_close_file( &data, status ); smf_close_file( &indata, status ); /* Attempt to open a file containing descriptions of steps fixed by a previous invocation of this program. */ fd = smf_open_textfile( "OLDSTEPS", "r", "<none>", status ); if( fd ) { /* Get SIZETOL - the minimum significant fractional error in step sizes. */ parGet0d( "SIZETOL", &sizetol, status ); /* Read the contents of the file, issuing a warning if the global properties read from the file (e.g. parameters used, no. of steps found, etc) differ from those of the current invocation. */ msgBlank( status ); oldsteps = smf1_read_steps( fd, dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, nrej, nnew, &nold, status ); /* Get the index of the first change to report. */ parGet0i( "FIRST", &first, status ); /* Compare the new step fixes with the old step fixes, issuing a warning for the first step fix that has changed. */ changed = smf1_check_steps( "CONTINUE", first, nx, sizetol, nold, nnew, oldsteps, newsteps, status ); /* Store a flag indicating if any sstep fixes have chnaged. */ parPut0l( "CHANGED", changed, status ); /* Tell the user if nothing has changed. */ if( ! changed ) { msgOut( "", "There are no significant differences " "between old and new step fixes.", status ); } msgBlank( status ); /* Close the old steps file, and free the memory holding the old step descriptions. */ fclose( fd ); oldsteps = astFree( oldsteps ); } /* Free resources. */ newsteps = astFree( newsteps ); grpDelet( &igrp, status ); grpDelet( &ogrp, status ); /* End the NDF context. */ ndfEnd( status ); /* If anything went wrong issue a context message. */ if( *status != SAI__OK ) msgOutif( MSG__VERB, " ", "FIXSTEPS failed.", status ); }
F77_SUBROUTINE(configecho)( INTEGER(STATUS) ){ /* *+ * Name: * CONFIGECHO * Purpose: * Displays one or more configuration parameters. * Language: * C (designed to be called from Fortran) * Type of Module: * ADAM A-task * Invocation: * CALL CONFIGECHO( STATUS ) * Arguments: * STATUS = INTEGER (Given and Returned) * The global status. * Description: * This application displays the name and value of one or all * configuration parameters, specified using Parameters CONFIG or * NDF. If a single parameter is displayed, its value is also * written to an output parameter. If the parameter value is not * specified by the CONFIG, NDF or DEFAULTS parameter, then the * value supplied for DEFVAL is displayed. * * If an input NDF is supplied then configuration parameters * are read from its history (see Parameters NDF and APPLICATION). * * If values are supplied for both CONFIG and NDF, then the * differences between the two sets of configuration parameters * are displayed (see Parameter NDF). * Usage: * configecho name config [defaults] [select] [defval] * ADAM Parameters: * APPLICATION = LITERAL (Read) * When reading configuration parameters from the history * of an NDF, this parameter specifies the name of the application * to find in the history. There must be a history component * corresponding to the value of this parameter, and it must * include a CONFIG group. [current value] * CONFIG = GROUP (Read) * Specifies values for the configuration parameters. If the string * "def" (case-insensitive) or a null (!) value is supplied, the * configuration parameters are obtained using Parameter NDF. If * a null value is also supplied for NDF, a set of default * configuration parameter values will be used, as specified by * Parameter DEFAULTS. * * The supplied value should be either a comma-separated list of * strings or the name of a text file preceded by an up-arrow * character "^", containing one or more comma-separated lists of * strings. Each string is either a "keyword=value" setting, or the * name of a text file preceded by an up-arrow character "^". Such * text files should contain further comma-separated lists which * will be read and interpreted in the same manner (any blank lines * or lines beginning with "#" are ignored). Within a text file, * newlines can be used as delimiters, as well as commas. Settings * are applied in the order in which they occur within the list, * with later settings overriding any earlier settings given for * the same keyword. * * Each individual setting should be of the form "<keyword>=<value>". * If a non-null value is supplied for Parameter DEFAULTS, an error * will be reported if CONFIG includes values for any parameters * that are not included in DEFAULTS. * DEFAULTS = LITERAL (Read) * The path to a file containing the default value for every * allowed configuration parameter. If null (!) is supplied, no * defaults will be supplied for parameters that are not specified * by CONFIG, and no tests will be performed on the validity of * paramter names supplied by CONFIG. [!] * DEFVAL = LITERAL (Read) * The value to return if no value can be obtained for the named * parameter, or if the value is "<undef>". [<***>] * NAME = LITERAL (Read) * The name of the configuration parameter to display. If set to * null (!), then all parameters defined in the configuration are * displayed. * NDF = NDF (Read) * An NDF file containing history entries which include * configuration parameters. If not null (!) the history * of the NDF will be searched for a component corresponding * to the Parameter APPLICATION. The Parameter CONFIG * is then optional, but if it too is not null (!) then * the output will show the differences between the configuration * stored in the NDF history and the given configuration: * new parameters and those different from the reference * configuration (given by Parameter CONFIG) are prefixed * with "+" and those which are the same as the reference * configuration are prefixed with "-". [!] * SELECT = GROUP (Read) * A group that specifies any alternative prefixes that can be * included at the start of any parameter name. For instance, if * this group contains the two entries "450=1" and "850=0", then * either CONFIG or DEFAULTS can specify two values for any single * parameter -- one for the parameter prefixed by "450." and another * for the parameter prefixed by "850.". Thus, for instance, if * DEFAULTS defines a parameter called "filter", it could include * "450.filter=300" and "850.filter=600". The CONFIG parameter could * then either set the filter parameter for a specific prefix (as * in "450.filter=234"); or it could leave the prefix unspecified, * in which case the prefix used is the first one with a * non-zero value in SELECT (450 in the case of this example - 850 * has a value zero in SELECT). Thus the names of the items in * SELECT define the set of allowed alternative prefixes, and the * values indicate which one of these alternatives is to be used * (the first one with non-zero value). [!] * SORT = _LOGICAL (Read) * If TRUE then sort the listed parameters in to alphabetical order. * Otherwise, retain the order they have in the supplied * configuration. Only used if a null (!) value is supplied for * Parameter NAME. [FALSE] * VALUE = LITERAL (Write) * The value of the configuration parameter, or "<***>" if the * parameter has no value in CONFIG and DEFAULTS. * Examples: * configecho m81 ^myconf * Report the value of configuration parameter "m81" defined within * the file "myconf". If the file does not contain a value for * "m81", then "<***>" is displayed. * configecho type ^myconf select="m57=0,m31=1,m103=0" * Report the value of configuration parameter "type" defined within * the file "myconf". If the file does not contain a value for * "type", then the value of "m31.type" will be reported instead. If * neither is present, then "<***>" is displayed. * configecho flt.filt_edge_largescale \ * config=^/star/share/smurf/dimmconfig.lis \ * defaults=/star/bin/smurf/smurf_makemap.def \ * select="450=1,850=0" * Report the value of configuration parameter "flt.filt_edge_largescale" * defined within the file "/star/share/smurf/dimmconfig.lis", using * defaults from the file "/star/bin/smurf/smurf_makemap.def". If * dimmconfig.lis does not contain a value for "flt.filt_edge_largescale" * then it is searched for "450.flt.filt_edge_largescale" instead. An * error is reported if dimmconfig.lis contains values for any * items that are not defined in smurf_makemap.def. * configecho ndf=omc1 config=^/star/share/smurf/dimmconfig.lis \ * defaults=/star/bin/smurf/smurf_makemap.def \ * application=makemap name=! sort select="450=0,850=1" * Show how the configuration used to generate the 850um map * of OMC1 differs from the basic dimmconfig.lis file. * Copyright: * Copyright (C) 2012-3 Science & Technology Facilities 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 * GSB: Graham S. Bell * {enter_new_authors_here} * History: * 10-DEC-2012 (DSB): * Original version. * 6-FEB-2013 (DSB): * Added parameter DEFVAL. * 11-FEB-2013 (DSB): * Added parameter SORT and allow all parameters to be listed by * providing a null value for NAME. * 11-FEB-2013 (GSB): * Added ability to read configuration from history entries. * 13-FEB-2013 (DSB): * Nullify AST object pointers when the objects are annulled, * to avoid re-use of dead pointers. * 14-FEB-2013 (DSB): * Allow the SELECT feature to be used even if no DEFAULTS file is * supplied (see the new entry in the "Examples:" section). * 15-FEB-2013 (DSB): * Expand the prologue docs, and use NULL in place of zero for pointers. * 22-FEB-2013 (DSB): * Guard against seg fault in HistoryKeymap when the NDF does * not contain the required CONFIG entry in the History * component. * {enter_further_changes_here} *- */ GENPTR_INTEGER(STATUS) /* Local Variables: */ AstKeyMap *keymap2; AstKeyMap *keymap; Grp *grp = NULL; char *dot; char *pname; char defs[250]; char defval[250]; char name[250]; const char *value; const char *historyValue = NULL; int showall; int sort; size_t size; int indf = 0; int nrec; int i; char application[NDF__SZAPP]; char applicationi[NDF__SZAPP]; /* Abort if an error has already occurred. */ if( *STATUS != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Get the value to return if no value can be obtained for the named parameter, of it it has a value of <undef>. */ parGet0c( "DEFVAL", defval, sizeof(defval), STATUS ); /* Get any defaults file, annuling the error if null (!) is supplied. */ if( *STATUS == SAI__OK ) { parGet0c( "DEFAULTS", defs, sizeof(defs), STATUS ); if( *STATUS == PAR__NULL ) { errAnnul( STATUS ); defs[0] = 0; } } /* Get the NDF identifier if requested. */ ndfBegin(); if (*STATUS == SAI__OK) { ndfAssoc("NDF", "READ", &indf, STATUS); if (*STATUS == PAR__NULL) { errAnnul(STATUS); indf = 0; } else { parGet0c("APPLICATION", application, sizeof(application), STATUS); /* Check now for error because the block below allowing an undefined * CONFIG clears this status otherwise. */ if (*STATUS != SAI__OK) goto L999; } } /* See if any alternate keyword prefixes are allowed, and if so determine which of the alternatices is to be displayed. */ kpg1Gtgrp( "SELECT", &grp, &size, STATUS ); if( *STATUS == PAR__NULL ) { grpDelet( &grp, STATUS ); errAnnul( STATUS ); keymap2 = NULL; } else { kpg1Kymap( grp, &keymap2, STATUS ); grpDelet( &grp, STATUS ); } /* Create a KeyMap holding the selected alternative for each keyword, and also supply defaults for any missing values (if a defaults file was supplied by the user). */ keymap = kpg1Config( "CONFIG", defs[0]?defs:NULL, keymap2, 0, STATUS ); /* Allow it to be NULL if we're reading an NDF because we'll replace keymap with historyConfig later if necessary. */ if( indf && *STATUS == PAR__NULL ) { errAnnul(STATUS); keymap = NULL; } /* Abort if an error has occurred. */ if( *STATUS != SAI__OK ) goto L999; /* Get the name of the required parameter, and convert to upper case (if supplied). If not supplied, set a flag indicating that all parameters should be displayed. */ parGet0c( "NAME", name, sizeof(name), STATUS ); if( *STATUS == PAR__NULL ) { errAnnul( STATUS ); showall = 1; } else { showall = 0; astChrCase( NULL, name, 1, 0 ); } /* Attempt to find the NDF's corresponding history record. */ if (indf && *STATUS == SAI__OK) { ndfHnrec(indf, &nrec, STATUS); for (i = 0; i < nrec; i ++) { ndfHinfo(indf, "APPLICATION", i + 1, applicationi, sizeof(applicationi), STATUS); if (! strncasecmp(application, applicationi, strlen(application))) { ndfHout(indf, i + 1, HistoryKeyMap, STATUS); break; } } if (*STATUS == SAI__OK && ! historyConfig) { *STATUS = SAI__ERROR; errRepf("CONFIGECHO_ERR", "CONFIGECHO: Failed to find %s " "configuration in NDF history.", STATUS, application); } else if (! keymap) { keymap = historyConfig; historyConfig = NULL; } } if( *STATUS == SAI__OK ) { /* First deal with cases where we are displaying a single parameter value. */ if( !showall ) { /* Loop round each section of the name that ends with a dot. */ value = defval; pname = name; dot = strchr( pname, '.' ); while( dot && keymap ) { /* Get a nested keymap with the name that occurs prior to the dot. If found, use it in place of the parent keymap. */ pname[ dot - pname ] = 0; if( astMapGet0A( keymap, pname, &keymap2 ) ) { astAnnul( keymap ); keymap = keymap2; } else { keymap = astAnnul( keymap ); } /* If historyConfig exists, do the same there. */ if (historyConfig) { if (astMapGet0A(historyConfig, pname, &keymap2)) { astAnnul(historyConfig); historyConfig = keymap2; } else { historyConfig = astAnnul(historyConfig); } } /* Re-instate the original dot, and move on to find the next dot. */ pname[ dot - pname ] = '.'; pname = dot + 1; dot = strchr( pname, '.' ); } /* Ensure no error is reported if the parameter is not found in the KeyMap. */ if( keymap ) { astClear( keymap, "KeyError" ); /* Get the parameter value as a string. */ astMapGet0C( keymap, pname, &value ); } if (historyConfig) { astClear(historyConfig, "KeyError"); astMapGet0C(historyConfig, pname, &historyValue); /* In NDF history mode we only want to return a value if it was found in the configuration from the history. */ if (historyValue) { if (strcmp(value, historyValue)) { msgOutf("", "+ %s", STATUS, historyValue); } else { msgOutf("", "- %s", STATUS, historyValue); } parPut0c("VALUE", historyValue, STATUS); } } else { /* Display it. */ msgOut( "", value, STATUS ); /* Write it to the output parameter. */ parPut0c( "VALUE", value, STATUS ); } /* Now deal with cases were we are displaying all parameter values. */ } else { /* See if the values should be sorted. */ parGet0l( "SORT", &sort, STATUS ); /* Display them. */ if (historyConfig) { DisplayKeyMap( historyConfig , sort, "", keymap, STATUS ); } else { DisplayKeyMap( keymap, sort, "", NULL, STATUS ); } } } /* Tidy up. */ L999:; /* End the AST context */ astEnd; /* Close the NDF if open. */ ndfEnd(STATUS); /* If an error has occurred, issue another error report identifying the program which has failed (i.e. this one). */ if( *STATUS != SAI__OK ) { errRep( "CONFIGECHO_ERR", "CONFIGECHO: Failed to echo configuration " "parameters.", STATUS ); } }