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 smurf_sc2expandmodel( int *status ) { smf_expmodelptr expptr=NULL;/* Pointer to current model calc function */ size_t i=0; /* Counter, index */ smfData *idata=NULL; /* Input data */ Grp *igrp = NULL; /* Input group of files */ smfData *odata=NULL; /* Output data */ 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 */ /* Main routine */ ndfBegin(); /* Read the input file */ kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); /* Loop over input files */ for( i=1; (*status==SAI__OK) && (i<=size); i++ ) { /* Open file */ smf_open_file(NULL, igrp, i, "READ", 0, &idata, status); /* Check to see if this is a DIMM model component before expanding */ if( idata && idata->hdr && (idata->hdr->mtype != SMF__NUL) ) { expptr = smf_model_getexpptr( idata->hdr->mtype, status ); if( *status == SAI__OK ) { (*expptr)( idata, &odata, status ); smf_write_smfData( NULL, odata, NULL, NULL, ogrp, i, 0, MSG__VERB, 0, NULL, NULL, status ); } } } /* Cleanup */ grpDelet( &igrp, status); grpDelet( &ogrp, status); ndfEnd( status ); }
void smf_stripsuffix( const char *instr, const char *suffix, char *outstr, int *status) { /* Local Variables */ int added; /* Number of names added to group */ int flag; /* Flag */ char grpex[GRP__SZNAM+1]; /* String for holding grpex */ Grp *inname=NULL; /* 1-element group to hold input string */ size_t len; /* Length of buffer */ size_t msize; /* Size of group */ Grp *outname = NULL; /* 1-element group to hold output string */ char *pname=NULL; /* Poiner to name */ const char *p; /* Pointer to first character after next "/" */ const char *q; /* Pointer to next "/" */ /* Main routine */ if (*status != SAI__OK) return; inname = grpNew( "GRP", status ); outname = grpNew( "GRP", status ); p = instr; q = strchr( p, '/' ); while( q ) { p = q + 1; q = strchr( p, '/' ); } grpPut1( inname, p, 1, status ); len = sizeof(grpex); one_strlcpy( grpex, "*|", len, status ); one_strlcat( grpex, suffix, len, status ); one_strlcat( grpex, "||", len, status ); grpGrpex( grpex, inname, outname, &msize, &added, &flag, status ); pname = outstr; grpGet( outname, 1, 1, &pname, GRP__SZNAM, status ); grpDelet( &inname, status ); grpDelet( &outname, status ); }
void smurf_rawrewrtsc2wcs( int * status ) { size_t i; Grp *igrp = NULL; /* Input group */ size_t size; /* Number of files in input group */ if (*status != SAI__OK) return; ndfBegin(); /* Read the input file group */ kpg1Rgndf( "NDF", 0, 1, "", &igrp, &size, status ); for (i=1; i<=size && ( *status == SAI__OK ); i++) { int indf = NDF__NOID; smfData *data = NULL; AstFrameSet * fixedwcs = NULL; int isok = 1; /* First open in READ mode as a sanity check */ smf_open_file( igrp, i, "READ", 0, &data, status ); if (*status != SAI__OK) break; if (data->hdr->instrument != INST__SCUBA2) { isok = 0; msgOut( "", "This command only works on SCUBA-2 data files", status ); } /* Get a fixed WCS frameset */ if (isok) { smf_create_tswcs( data->hdr, &fixedwcs, status ); } /* close up and skip if this is not a good file */ smf_close_file( &data, status ); if (!isok) continue; /* Now we try to update the file using NDF */ ndgNdfas( igrp, i, "UPDATE", &indf, status ); ndfPtwcs( fixedwcs, indf, status ); ndfAnnul( &indf, status ); } /* Cleanup */ grpDelet( &igrp, status); ndfEnd( status ); }
/* Main entry */ void smurf_checkcoords( int *status ) { /* Local Variables */ Grp *igrp = NULL; size_t i; size_t size; smfData *data = NULL; /* Check inherited status */ if (*status != SAI__OK) return; /* begin an NDF context. */ ndfBegin(); /* Get a group of input files */ kpg1Rgndf( "IN", 0, 1, " Give more NDFs...", &igrp, &size, status ); /* Loop round each one. */ for( i = 1; i <= size; i++ ) { /* Open the file. */ smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status ); /* Check the detector positions (for ACSIS data). */ msgBlank( status ); smf_check_detpos( data, 1, status ); /* Calculate and display statistics of the AZEL <> TRACKING separations in the current file. */ smf_check_coords( data, status ); /* Close the file. */ smf_close_file( NULL, &data, status); } /* Free resources. */ grpDelet( &igrp, status ); /* End the NDF context. */ ndfEnd( status ); /* If anything went wrong issue a context message. */ if( *status != SAI__OK ) msgOutif( MSG__VERB, " ", "CHECKCOORDS failed.", status ); }
void smf_request_mask( ThrWorkForce *wf, const char *param, smfArray ** bbms, int *status) { Grp * bbmgrp = NULL; size_t nbbm; /* initialise return value */ *bbms = NULL; if (*status != SAI__OK) return; kpg1Rgndf( param, 0, 1, "", &bbmgrp, &nbbm, status ); if (*status == PAR__NULL) { bbms = NULL; errAnnul( status ); } else { smf_open_group( wf, bbmgrp, NULL, bbms, status ); } if (bbmgrp) grpDelet( &bbmgrp, status ); }
void smf_write_flagmap( ThrWorkForce *wf, smf_qual_t mask, smfArray *lut, smfArray *qua, smfDIMMData *dat, const Grp *flagrootgrp, size_t contchunk, const int *lbnd_out, const int *ubnd_out, AstFrameSet *outfset, int *status ) { AstFrameSet *tfset; /* Temporary FrameSet pointer */ Grp *mgrp=NULL; /* Temporary group for map names */ char *pname=NULL; /* Poiner to name */ char name[GRP__SZNAM+1]; /* Buffer for storing names */ char tempstr[20]; /* Temporary string */ char tmpname[GRP__SZNAM+1]; /* temp name buffer */ dim_t nbolo; /* Number of bolometers */ dim_t ntslice; /* Number of time slices */ double shift[ 1 ]; /* Shift from GRID to bit number */ int *flagmap=NULL; /* pointer to flagmap data */ int *lut_data=NULL; /* Pointer to DATA component of lut */ int ibit; /* Quality bit number */ int lbnd3d[3]; /* Lower bounds for 3D output */ int npix; /* Number of pixels per plane */ int target; /* Target value for incrementing pixel count */ int ubnd3d[3]; /* Upper bounds for 3D output */ size_t bstride; /* Bolometer stride */ size_t i; /* loop counter */ size_t idx=0; /* index within subgroup */ size_t ii; /* array offset index */ size_t j; /* loop counter */ size_t tstride; /* Time stride */ smfData *mapdata=NULL; /* smfData for new map */ smf_qual_t *qua_data=NULL; /* Pointer to DATA component of qua */ if( *status != SAI__OK ) return; if( !lut || !qua || !dat || !flagrootgrp || !lbnd_out || !ubnd_out || !outfset ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL inputs supplied", status ); return; } /* Create a name for the flagmap, taking into account the chunk number. Only required if we are using a single output container. */ pname = tmpname; grpGet( flagrootgrp, 1, 1, &pname, sizeof(tmpname), status ); one_strlcpy( name, tmpname, sizeof(name), status ); one_strlcat( name, ".", sizeof(name), status ); sprintf(tempstr, "CH%02zd", contchunk); one_strlcat( name, tempstr, sizeof(name), status ); mgrp = grpNew( "flagmap", status ); grpPut1( mgrp, name, 0, status ); msgOutf( "", "*** Writing flagmap %s", status, name ); /* If a non-zero mask value wassupplied, the flagmap is 2-dimensional and each pixel value counts the number of samples flagged by any of the qualities included in the mask. */ if( mask ) { smf_open_newfile( wf, mgrp, 1, SMF__INTEGER, 2, lbnd_out, ubnd_out, 0, &mapdata, status); flagmap = mapdata->pntr[0]; /* Loop over subgroup index (subarray) */ for( idx=0; (idx<qua->ndat)&&(*status==SAI__OK); idx++ ) { smf_get_dims( qua->sdata[idx], NULL, NULL, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); qua_data = (qua->sdata[idx]->pntr)[0]; lut_data = (lut->sdata[idx]->pntr)[0]; /* Loop over bolometer and time slice and create map */ for( i=0; i<nbolo; i++ ) { /* Skip bolometers only if SMF__Q_BADB is set both in the data and the mask */ if( !(qua_data[i*bstride] & mask & SMF__Q_BADB) ) { for( j=0; j<ntslice; j++ ) { ii = i*bstride + j*tstride; if( (qua_data[ii] & mask) && (lut_data[ii] != VAL__BADI) ) { flagmap[lut_data[ii]]++; } } } } } /* Write WCS */ smf_set_moving( (AstFrame *) outfset, NULL, status ); ndfPtwcs( outfset, mapdata->file->ndfid, status ); /* If the mask is zero, the flagmap is 3-dimensional and contains a plane for each quality bit, plus an additional plane (plane 1) containing the number of unflagged samples in each pixel. */ } else { lbnd3d[ 0 ] = lbnd_out[ 0 ]; lbnd3d[ 1 ] = lbnd_out[ 1 ]; lbnd3d[ 2 ] = -1; ubnd3d[ 0 ] = ubnd_out[ 0 ]; ubnd3d[ 1 ] = ubnd_out[ 1 ]; ubnd3d[ 2 ] = SMF__NQBITS_TSERIES - 1; smf_open_newfile( wf, mgrp, 1, SMF__INTEGER, 3, lbnd3d, ubnd3d, 0, &mapdata, status); flagmap = mapdata->pntr[0]; /* No. of pixels in one plane */ npix = ( ubnd3d[ 1 ] - lbnd3d[ 1 ] + 1 )*( ubnd3d[ 0 ] - lbnd3d[ 0 ] + 1 ); /* Loop over each quality bit (-1 == "no flags"). */ for( ibit = -1; ibit < SMF__NQBITS_TSERIES; ibit++ ) { /* The test of each sample is performed by checking if the sample's quality value ANDed with "mask" is equal to "target". This is requires since ibit==-1 (i.e. "count all samples that have no flags set") requires a different logic to the other ibit values. */ if( ibit == -1 ) { mask = SMF__Q_GOOD; target = 0; } else { mask = BIT_TO_VAL(ibit); target = mask; } /* Loop over subgroup index (subarray) */ for( idx=0; (idx<qua->ndat)&&(*status==SAI__OK); idx++ ) { smf_get_dims( qua->sdata[idx], NULL, NULL, &nbolo, &ntslice, NULL, &bstride, &tstride, status ); qua_data = (qua->sdata[idx]->pntr)[0]; lut_data = (lut->sdata[idx]->pntr)[0]; /* Loop over bolometer and time slice and create map */ for( i=0; i<nbolo; i++ ) { /* Skip bolometers only if SMF__Q_BADB is set both in the data and the mask */ for( j=0; j<ntslice; j++ ) { ii = i*bstride + j*tstride; if( ( (qua_data[ii] & mask) == target ) && (lut_data[ii] != VAL__BADI) ) { flagmap[lut_data[ii]]++; } } } } /* Move the pointer on to th enext plane. */ flagmap += npix; /* Next quality bit. */ } /* Take a copy of the supplied FrameSet so we do not modify it. */ tfset = astCopy( outfset ); /* Set atributes for moving target if necessary. */ smf_set_moving( (AstFrame *) tfset, NULL, status ); /* Modify the WCS FrameSet so that the base and current Frames are 3-dimensional. The current Frame is expanded by adding in a simple 1D Frame representing quality bit, and the base Frame is expanded by adding in a 3rd GRID axis. Other Frames are left unchanged. The quality bit Frame and the new GRID axis are connected using a ShiftMap that gives the right zero-based bit numbers (which also correspond to PIXEL indices). */ shift[ 0 ] = -2.0; atlAddWcsAxis( tfset, (AstMapping *) astShiftMap( 1, shift, " " ), astFrame( 1, "Label(1)=Quality bit,Domain=QUALITY" ), NULL, NULL, status ); /* Store the FrameSet in the 3D NDF. */ ndfPtwcs( tfset, mapdata->file->ndfid, status ); tfset = astAnnul( tfset ); } /* Clean up */ if( mgrp ) grpDelet( &mgrp, status ); smf_close_file( wf, &mapdata, status ); }
void smf_grp_related( const Grp *igrp, const size_t grpsize, const int grouping, const int checksubinst, double maxlen_s, double *srate_maxlen, AstKeyMap *keymap, dim_t *maxconcatlen, dim_t *maxfilelen, smfGroup **group, Grp **basegrp, dim_t *pad, int *status ) { /* Local variables */ size_t *chunk=NULL; /* Array of flags for continuous chunks */ dim_t * chunklen = NULL; /* Length of continuous chunk */ size_t currentindex = 0; /* Counter */ char cwave[10]; /* String containing wavelength */ smfData *data = NULL; /* Current smfData */ double downsampscale=0; /* Angular scale downsampling size */ double downsampfreq=0; /* Target downsampling frequency */ AstKeyMap * grouped = NULL; /* Primary AstKeyMap for grouping */ size_t i; /* Loop counter for index into Grp */ int isFFT=0; /* Set if data are 4d FFT */ size_t j; /* Loop counter */ int *keepchunk=NULL; /* Flag for chunks that will be kept */ dim_t maxconcat=0; /* Longest continuous chunk length */ dim_t maxflen=0; /* Max file length in time steps */ dim_t maxlen=0; /* Maximum concat length in samples */ int maxlen_scaled=0; /* Set once maxlen has been scaled, if needed */ dim_t maxpad=0; /* Maximum padding neeed for any input file */ size_t maxrelated = 0; /* Keep track of max number of related items */ size_t *new_chunk=NULL; /* keeper chunks associated with subgroups */ dim_t *new_tlen=NULL; /* tlens for new_subgroup */ size_t ngroups = 0; /* Counter for subgroups to be stored */ size_t nkeep = 0; /* Number of chunks to keep */ dim_t * piecelen = NULL; /* Length of single file */ smf_subinst_t refsubinst; /* Subinst of first file */ size_t **subgroups = NULL; /* Array containing index arrays to parent Grp */ smf_subinst_t subinst; /* Subinst of current file */ if ( *status != SAI__OK ) return; if( maxlen_s < 0 ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": maxlen_s cannot be < 0!", status ); return; } /* Get downsampling parameters */ if( keymap ) { smf_get_cleanpar( keymap, 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, NULL, NULL, NULL, &downsampscale, &downsampfreq, NULL, NULL, NULL, NULL, status ); if( downsampscale && downsampfreq ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": both downsampscale and downsampfreq are set", status ); return; } } /* Initialize refcwave */ refsubinst = SMF__SUBINST_NONE; /* Loop over files in input Grp: remember Grps are indexed from 1 */ grouped = astKeyMap( "SortBy=KeyUp" ); for (i=1; i<=grpsize; i++) { char newkey[128]; char dateobs[81]; char subarray[10]; size_t nrelated = 0; AstKeyMap * filemap = NULL; AstKeyMap * indexmap = NULL; /* First step: open file and harvest metadata */ smf_open_file( NULL, igrp, i, "READ", SMF__NOCREATE_DATA, &data, status ); if (*status != SAI__OK) break; if( i==1 ) { isFFT = smf_isfft( data, NULL, NULL, NULL, NULL, NULL, status ); } else if( smf_isfft(data, NULL, NULL, NULL, NULL, NULL, status) != isFFT ){ *status = SAI__ERROR; errRep( "", FUNC_NAME ": mixture of time-series and FFT data encountered!", status ); break; } /* If maxlen has not been set, do it here */ if( !maxlen && maxlen_s && data->hdr->steptime) { maxlen = (dim_t) (maxlen_s / data->hdr->steptime ); } /* Return srate_maxlen if requested: may want to know this number even if maxlen_s is not set. Only calculate once, although it gets overwritten once later if down-sampling. */ if( (i==1) && srate_maxlen && data->hdr->steptime ) { *srate_maxlen = 1. / (double) data->hdr->steptime; } /* If requested check to see if we are mixing wavelengths */ if( checksubinst ) { if( refsubinst == SMF__SUBINST_NONE ) { refsubinst = smf_calc_subinst( data->hdr, status ); } subinst = smf_calc_subinst( data->hdr, status ); if( subinst != refsubinst ) { const char *refsubstr = smf_subinst_str( refsubinst, status ); const char *substr = smf_subinst_str( subinst, status ); *status = SAI__ERROR; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSetc( "REFSUB", refsubstr ); msgSetc( "SUB", substr ); errRep( "", FUNC_NAME ": ^FILE uses sub-instrument ^SUB which doesn't match " "reference ^REFSUB", status ); } } /* Want to form a key that will be unique for a particular subscan We know that DATE-OBS will be set for SCUBA-2 files and be the same for a single set. Prefix by wavelength if we are grouping by wavelength. */ newkey[0] = '\0'; smf_find_subarray( data->hdr, subarray, sizeof(subarray), NULL, status ); if( grouping == 1 ) { /* Group different wavelengths separately */ smf_fits_getS( data->hdr, "WAVELEN", cwave, sizeof(cwave), status); one_strlcat( newkey, cwave, sizeof(newkey), status ); one_strlcat( newkey, "_", sizeof(newkey), status ); } if( grouping == 2 ) { /* Group different subarrays separately */ one_strlcat( newkey, subarray, sizeof(newkey), status ); } smf_fits_getS( data->hdr, "DATE-OBS", dateobs, sizeof(dateobs), status ); one_strlcat( newkey, dateobs, sizeof(newkey), status ); /* Include the dimentionality of the time series in the primary key so that we do not end up doing something confusing like relating a truncated file with a full length file */ if (*status == SAI__OK) { dim_t dims[3]; char formatted[32]; smf_get_dims( data, &dims[0], &dims[1], NULL, &dims[2], NULL, NULL, NULL, status ); sprintf(formatted, "_%" DIM_T_FMT "_%" DIM_T_FMT "_%" DIM_T_FMT, dims[0], dims[1], dims[2]); one_strlcat( newkey, formatted, sizeof(newkey), status ); } /* May want to read the dimensionality of the file outside of loop so that we can compare values when storing in the keymap */ /* Now we want to create a keymap based on this key */ if (!astMapGet0A( grouped, newkey, &filemap ) ) { int itemp = 0; double steptime = data->hdr->steptime; dim_t ntslice = 0; dim_t thispad; /* Padding neeed for current input file */ filemap = astKeyMap( " " ); astMapPut0A( grouped, newkey, filemap, NULL ); /* Fill up filemap with general information on this file */ smf_find_seqcount( data->hdr, &itemp, status ); astMapPut0I( filemap, "SEQCOUNT", itemp, NULL ); smf_fits_getI( data->hdr, "NSUBSCAN", &itemp, status ); astMapPut0I( filemap, "NSUBSCAN", itemp, NULL ); /* Number of time slices */ smf_get_dims( data, NULL, NULL, NULL, &ntslice, NULL, NULL, NULL, status ); /* Find length of down-sampled data, new steptime and maxlen */ if( (downsampscale || downsampfreq) && data->hdr && (*status==SAI__OK) ) { double scalelen; if( downsampscale ) { if( data->hdr->scanvel != VAL__BADD ) { double oldscale = steptime * data->hdr->scanvel; scalelen = oldscale / downsampscale; } else if( *status == SAI__OK ) { *status = SAI__ERROR; scalelen = VAL__BADD; smf_smfFile_msg( data->file, "FILE", 1, "" ); errRep( "", FUNC_NAME ": can't resample ^FILE because it has " "unknown scan velocity", status ); } } else { if( steptime ) { double oldsampfreq = 1./steptime; scalelen = downsampfreq / oldsampfreq; } else { *status = SAI__ERROR; scalelen = VAL__BADD; smf_smfFile_msg( data->file, "FILE", 1, "" ); errRep( "", FUNC_NAME ": can't resample ^FILE because it has " "unknown sample rate", status ); } } /* only down-sample if it will be a reasonable factor */ if( (*status==SAI__OK) && (scalelen <= SMF__DOWNSAMPLIMIT) ) { smf_smfFile_msg(data->file, "FILE", 1, "" ); msgOutiff( MSG__VERB, "", FUNC_NAME ": will down-sample file ^FILE from %5.1lf Hz to " "%5.1lf Hz", status, (1./steptime), (scalelen/steptime) ); ntslice = round(ntslice * scalelen); /* If maxlen has been requested, and we have not already worked out a scaled version (just uses the sample rates for the first file... should be close enough -- the alternative is a 2-pass system). */ if( !maxlen_scaled ) { maxlen = round(maxlen*scalelen); maxlen_scaled = 1; msgOutiff( MSG__VERB, "", FUNC_NAME ": requested maxlen %g seconds = %" DIM_T_FMT " down-sampled " "time-slices", status, maxlen_s, maxlen ); /* Return updated srate_maxlen for down-sampling if requested */ if( srate_maxlen ) { *srate_maxlen = scalelen/steptime; } } } } /* Check that an individual file is too long (we assume related files are all the same) */ if( maxlen && (ntslice > maxlen) && *status == SAI__OK) { *status = SAI__ERROR; msgSeti("NTSLICE",ntslice); msgSeti("MAXLEN",maxlen); smf_smfFile_msg( data->file, "FILE", 1, "" ); errRep(FUNC_NAME, "Number of time steps in file ^FILE time exceeds maximum " "(^NTSLICE>^MAXLEN)", status); } /* Scaled values of ntslice and maximum length */ astMapPut0I( filemap, "NTSLICE", ntslice, NULL ); /* Work out the padding needed for this file including downsampling. */ if( keymap ) { thispad = smf_get_padding( keymap, 0, data->hdr, VAL__BADD, status ); if( thispad > maxpad ) maxpad = thispad; } else { thispad = 0; } astMapPut0I( filemap, "PADDING", thispad, NULL ); /* Update maxflen */ if( ntslice > maxflen ) { maxflen = ntslice; } /* Store OBSID or OBSIDSS depending on whether we are grouping by wavelength */ if (grouping) { astMapPut0C( filemap, "OBSID", data->hdr->obsidss, NULL ); } else { char obsid[81]; smf_getobsidss( data->hdr->fitshdr, obsid, sizeof(obsid), NULL, 0, status ); astMapPut0C( filemap, "OBSID", obsid, NULL ); } } /* Store the file index in another keymap indexed by subarray */ if ( !astMapGet0A( filemap, "GRPINDICES", &indexmap ) ) { indexmap = astKeyMap( "SortBy=KeyUp" ); astMapPut0A( filemap, "GRPINDICES", indexmap, NULL ); } astMapPut0I( indexmap, subarray, i, NULL ); /* Need to track the largest number of related subarrays in a single slot */ nrelated = astMapSize( indexmap ); if (nrelated > maxrelated) maxrelated = nrelated; /* Free resources */ filemap = astAnnul( filemap ); indexmap = astAnnul( indexmap ); smf_close_file( NULL, &data, status ); } /* We now know how many groups there are */ ngroups = astMapSize( grouped ); /* Sort out chunking. The items are sorted by date and then by wavelength. We define a continuous chunk if it has the same OBSID, the same SEQCOUNT and NSUBSCAN increments by one from the previous entry. Also count number of related items in each slot. */ if (*status == SAI__OK) { typedef struct { /* somewhere to store the values easily */ char obsid[81]; char related[81]; /* for concatenated subarrays */ int nsubscan; int seqcount; } smfCompareSeq; smfCompareSeq current; smfCompareSeq previous; dim_t totlen = 0; size_t thischunk; /* Get the chunk flags and also store the size of the chunk */ chunk = astCalloc( ngroups, sizeof(*chunk) ); chunklen = astCalloc( ngroups, sizeof(*chunklen) ); piecelen = astCalloc( ngroups, sizeof(*piecelen) ); thischunk = 0; /* The current chunk */ for (i=0; i<ngroups; i++) { AstKeyMap * thismap = NULL; AstKeyMap * grpindices = NULL; const char * tempstr = NULL; int thistlen = 0; size_t nsubarrays = 0; /* Get the keymap entry for this slot */ astMapGet0A( grouped, astMapKey(grouped, i), &thismap ); /* Get info for length limits */ astMapGet0I( thismap, "NTSLICE", &thistlen ); piecelen[i] = thistlen; if (isFFT) { /* Never concatenate FFT data */ thismap = astAnnul(thismap); chunk[i] = i; chunklen[i] = thistlen; continue; } /* Get indices information and retrieve the sub-instrument names in sort order to concatenate for comparison. We only store in a continuous chunk if we have the same subarrays for the whole chunk. */ astMapGet0A( thismap, "GRPINDICES", &grpindices ); nsubarrays = astMapSize( grpindices ); (current.related)[0] = '\0'; for (j = 0; j < nsubarrays; j++ ) { one_strlcat( current.related, astMapKey(grpindices, j), sizeof(current.related), status ); } grpindices = astAnnul( grpindices ); /* Fill in the current struct */ astMapGet0I( thismap, "SEQCOUNT", &(current.seqcount) ); astMapGet0I( thismap, "NSUBSCAN", &(current.nsubscan) ); astMapGet0C( thismap, "OBSID", &tempstr ); one_strlcpy( current.obsid, tempstr, sizeof(current.obsid), status ); /* First chunk is special, else compare */ if (i == 0) { totlen = thistlen; } else { if ( ( current.seqcount == previous.seqcount ) && ( current.nsubscan - previous.nsubscan == 1 ) && ( strcmp( current.obsid, previous.obsid ) == 0 ) && ( strcmp( current.related, previous.related ) == 0 ) ) { /* continuous - check length */ totlen += thistlen; if ( maxlen && totlen > maxlen ) { thischunk++; totlen = thistlen; /* reset length */ } else { /* Continuous */ } } else { /* discontinuity */ thischunk++; totlen = thistlen; /* Update length of current chunk */ } } chunklen[thischunk] = totlen; chunk[i] = thischunk; memcpy( &previous, ¤t, sizeof(current) ); thismap = astAnnul( thismap ); } } /* Decide if we are keeping a chunk by looking at the length. */ maxconcat = 0; nkeep = 0; keepchunk = astMalloc( ngroups*sizeof(*keepchunk) ); for (i=0; i<ngroups; i++) { size_t thischunk; thischunk = chunk[i]; if ( chunklen[thischunk] < SMF__MINCHUNKSAMP ) { /* Warning message */ msgSeti("LEN",chunklen[thischunk]); msgSeti("MIN",SMF__MINCHUNKSAMP); msgOut( " ", "SMF_GRP_RELATED: ignoring short chunk (^LEN<^MIN)", status); keepchunk[i] = 0; } else { keepchunk[i] = 1; if (maxconcat < chunklen[thischunk]) maxconcat = chunklen[thischunk]; nkeep++; } } /* If no useful chunks generate an error */ if( (*status==SAI__OK) && (!nkeep) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": No useful chunks.", status ); goto CLEANUP; } /* Allocate a subgroup array of the right size and fill it. They keymap is sorted by date (and wavelength) so we can always index into it by using indices from the subgroup. */ subgroups = astCalloc( nkeep, sizeof(*subgroups) ); new_chunk = astCalloc( nkeep, sizeof(*new_chunk) ); new_tlen = astCalloc( nkeep, sizeof(*new_tlen) ); currentindex = 0; for (i=0;i<ngroups;i++) { AstKeyMap * thismap = NULL; AstKeyMap * grpindices = NULL; size_t nsubarrays = 0; size_t *indices = astCalloc( maxrelated, sizeof(*indices) ); /* skip if we are dropping this chunk */ if (!keepchunk[i]) continue; /* Get the keymap entry for this slot */ astMapGet0A( grouped, astMapKey(grouped, i), &thismap ); /* Get the indices keymap */ astMapGet0A( thismap, "GRPINDICES", &grpindices ); nsubarrays = astMapSize( grpindices ); for (j=0; j<nsubarrays; j++) { int myindex; astMapGet0I( grpindices, astMapKey(grpindices, j), &myindex ); indices[j] = myindex; } grpindices = astAnnul( grpindices ); thismap = astAnnul( thismap ); subgroups[currentindex] = indices; new_chunk[currentindex] = chunk[i]; new_tlen[currentindex] = piecelen[i]; currentindex++; } /* Create the smfGroup */ *group = smf_construct_smfGroup( igrp, subgroups, new_chunk, new_tlen, nkeep, maxrelated, 0, status ); /* Return maxfilelen if requested */ if( maxfilelen ) { *maxfilelen = maxflen; } /* Return maxconcatlen if requested */ if( maxconcatlen ) { *maxconcatlen = maxconcat; } /* Create a base group for output files if required */ /* Create a base group of filenames */ if (*status == SAI__OK && basegrp ) { *basegrp = smf_grp_new( (*group)->grp, "Base Group", status ); /* Loop over time chunks */ for( i=0; (*status==SAI__OK)&&(i<(*group)->ngroups); i++ ) { size_t idx; /* Check for new continuous chunk */ if( i==0 || ( (*group)->chunk[i] != (*group)->chunk[i-1]) ) { /* Loop over subarray */ for( idx=0; idx<(*group)->nrelated; idx++ ) { size_t grpindex = (*group)->subgroups[i][idx]; if ( grpindex > 0 ) { ndgCpsup( (*group)->grp, grpindex, *basegrp, status ); } } } } } CLEANUP: keepchunk = astFree( keepchunk ); chunk = astFree( chunk ); chunklen = astFree( chunklen ); piecelen = astFree( piecelen ); grouped = astAnnul( grouped ); if( *status != SAI__OK ) { /* free the group */ if (basegrp && *basegrp) grpDelet( basegrp, status ); if (group && *group) { smf_close_smfGroup( group, status ); } else { /* have to clean up manually */ new_chunk = astFree( new_chunk ); new_tlen = astFree( new_tlen ); if( subgroups ) { size_t isub; for( isub=0; isub<nkeep; isub++ ) { subgroups[isub] = astFree( subgroups[isub] ); } subgroups = astFree( subgroups ); } } } /* Return the maximum padding if required. */ if( pad ) *pad = maxpad; }
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_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); } }
/* Main entry */ void smf_jsadicer( int indf, const char *base, int trim, smf_inst_t instrument, smf_jsaproj_t proj, size_t *ntile, Grp *grp, int *status ){ /* Local Variables: */ AstBox *box; AstFitsChan *fc; AstFrame *specfrm = NULL; AstFrame *tile_frm = NULL; AstFrameSet *iwcs; AstFrameSet *tfs = NULL; AstFrameSet *tile_wcs; AstMapping *ndf_map = NULL; AstMapping *p2pmap = NULL; AstMapping *specmap = NULL; AstMapping *tile_map = NULL; AstRegion *region; Grp *grpt = NULL; char *path; char dtype[ NDF__SZFTP + 1 ]; char jsatile_comment[45]; char type[ NDF__SZTYP + 1 ]; const char *dom = NULL; const char *keyword; const char *latsys = NULL; const char *lonsys = NULL; double *pd; double dlbnd[3]; double dubnd[3]; double gcen[3]; double lbnd_in[3]; double lbnd_out[3]; double ubnd_in[3]; double ubnd_out[3]; float *pf; int *created_tiles = NULL; int *tiles; int axlat; int axlon; int axspec; int bbox[ 6 ]; int i; int ifrm; int igrid; int indfo; int indfs; int indfx; int inperm[3]; int ipixel; int ishpx; int isxph; int itile; int ix; int iy; int iz; int junk; int latax = -1; int lbnd[3]; int lbnd_tile[ 3 ]; int lbndx[ NDF__MXDIM ]; int lonax = -1; int nbase; int ndim; int ndimx; int nfrm; int nsig; int ntiles; int olbnd[ 3 ]; int oubnd[ 3 ]; int outperm[ 3 ]; int place; int qual; int tile_index; int tile_lbnd[2]; int tile_ubnd[2]; int ubnd[3]; int ubnd_tile[ 3 ]; int ubndx[ NDF__MXDIM ]; int var; size_t iext; size_t size; smfJSATiling tiling; unsigned char *ipq = NULL; void *ipd = NULL; void *ipv = NULL; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Note the used length of the supplied base string. If it ends with ".sdf", reduce it by 4. */ nbase = astChrLen( base ); if( !strcmp( base + nbase - 4, ".sdf" ) ) nbase -= 4; /* Allocate a buffer large enough to hold the full path for an output NDF. */ path = astMalloc( nbase + 25 ); /* Get the WCS from the NDF. */ kpg1Gtwcs( indf, &iwcs, status ); /* Note if the NDF projection is HPX or XPH. */ ishpx = astChrMatch( astGetC( iwcs, "Projection" ), "HEALPix" ); isxph = astChrMatch( astGetC( iwcs, "Projection" ), "polar HEALPix" ); /* Report an error if the NDFs projection is neither of these. */ if( !ishpx && !isxph && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( "", "The input NDF (^N) does not appear to be gridded " "on the JSA all-sky pixel grid.", status ); } /* Get the bounds of the NDF in pixel indices and the the corresponding double precision GRID bounds (reduce the size of the grid by a small amount to avoid problems with tiles that are on the edge of the valid sky regions - astMapRegion can report an error for such tiles). Also store the GRID coords of the centre. Also count the number of significant pixel axes. */ ndfBound( indf, 3, lbnd, ubnd, &ndim, status ); nsig = 0; for( i = 0; i < ndim; i++ ) { dlbnd[ i ] = 0.5 + 0.1; dubnd[ i ] = ubnd[ i ] - lbnd[ i ] + 1.5 - 0.1; gcen[ i ] = 0.5*( dlbnd[ i ] + dubnd[ i ] ); if( ubnd[ i ] > lbnd[ i ] ) nsig++; } /* Find the one-based indices of the RA, Dec and spectral axes in the current Frame of the NDF. */ axlon = 0; if( astGetI( iwcs, "IsLonAxis(1)" ) ) { axlon = 1; lonsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLonAxis(2)" ) ) { axlon = 2; lonsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLonAxis(3)" ) ) { axlon = 3; lonsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the longitude axis in the " "input NDF.", status ); } axlat = 0; if( astGetI( iwcs, "IsLatAxis(1)" ) ) { axlat = 1; latsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLatAxis(2)" ) ) { axlat = 2; latsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLatAxis(3)" ) ) { axlat = 3; latsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the latitude axis in the " "input NDF.", status ); } axspec = 6 - axlon - axlat; /* Report an error if the spatial axes are not ICRS RA and Dec. */ if( ( lonsys && strcmp( lonsys, "ICRS" ) ) || ( latsys && strcmp( latsys, "ICRS" ) ) ) { if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: The spatial axes in '^N' are not " "ICRS RA and Dec.", status ); } } /* Create a Box describing the region covered by the NDF pixel grid in GRID coords. */ box = astBox( astGetFrame( iwcs, AST__BASE ), 1, dlbnd, dubnd, AST__NULL, " " ); /* Map this Box into the current WCS Frame of the NDF. */ region = astMapRegion( box, iwcs, iwcs ); /* If no instrument was specified, we will determine the instrument from the contexts of the FITS extension. Copy the NDF FITS extension to a FitsChan. Annul the error if the NDF no FITS extension. */ if( instrument == SMF__INST_NONE && *status == SAI__OK ) { kpgGtfts( indf, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = NULL; } } else { fc = NULL; } /* Get the parameters of the required tiling scheme. */ smf_jsainstrument( NULL, fc, instrument, &tiling, status ); /* Get a list of the JSA tiles touched by the supplied NDF. */ tiles = smf_jsatiles_region( region, &tiling, &ntiles, status ); if( ntiles == 0 && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: No JSA tiles found touching supplied NDF " "(programming error).", status ); } /* Does the input NDF have a Variance component? */ ndfState( indf, "Variance", &var, status ); /* Does the input NDF have a Quality component? */ ndfState( indf, "Quality", &qual, status ); /* Decide on the data type to use: _REAL or _DOUBLE. */ ndfMtype( "_REAL,_DOUBLE", indf, indf, "Data", type, sizeof(type), dtype, sizeof(dtype), status ); /* Tell the user what is happening. */ msgBlank( status ); msgOutf( "", "Dicing %s into JSA tiles:", status, ( nsig == 2 ) ? "map" : "cube" ); /* Loop round all tiles that overlap the supplied NDF. */ for( itile = 0; itile < ntiles && *status == SAI__OK; itile++ ) { tile_index = tiles[ itile ]; /* Get the spatial pixel bounds of the current tile within the requested JSA all-sky projection. Also get the (2D) WCS FrameSet for the tile. */ smf_jsatile( tile_index, &tiling, 0, proj, NULL, &tile_wcs, NULL, tile_lbnd, tile_ubnd, status ); /* Extract the tile pixel->WCS mapping and WCS Frame. We know the indices of the required Frames because they are hard-wired in smf_jsatile. */ tile_map = astGetMapping( tile_wcs, 3, 2 ); tile_frm = astGetFrame( tile_wcs, 2 ); /* Find the indices of the grid and pixel frames in the input NDF. */ ipixel = -1; igrid = astGetI( iwcs, "Base" ); nfrm = astGetI( iwcs, "NFrame" ); for( ifrm = 0; ifrm < nfrm; ifrm++ ) { dom = astGetC( astGetFrame( iwcs, ifrm + 1 ), "Domain" ); if( astChrMatch( dom, "PIXEL" ) ) ipixel = ifrm + 1; } /* If required, extract the pixel->spectral mapping and spectral frame in the input NDF, and add it in parallel with the above tile mapping. */ if( ndim == 3 ) { astSetI( iwcs, "Base", ipixel ); tfs = atlFrameSetSplit( iwcs, "DSBSPECTRUM SPECTRUM", NULL, NULL, status ); astSetI( iwcs, "Base", igrid ); if( tfs ) { specmap = astGetMapping( tfs, AST__BASE, AST__CURRENT ); specfrm = astGetFrame( tfs, AST__CURRENT ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: Cannot find the spectral axis " "in '^N'.", status ); } tile_map = (AstMapping *) astCmpMap( tile_map, specmap, 0, " " ); tile_frm = (AstFrame *) astCmpFrame( tile_frm, specfrm, " " ); } /* Ensure the Epoch is inherited form the input NDF. */ astSetD( tile_frm, "Epoch", astGetD( iwcs, "Epoch" ) ); /* Currently tile axis 1 is RA, axis 2 is Dec and axis 3 (if present) is spectral. Append a PermMap that re-orders these tile WCS axes to match those of the NDF. */ outperm[ axlon - 1 ] = 1; outperm[ axlat - 1 ] = 2; outperm[ axspec - 1 ] = 3; inperm[ 0 ] = axlon; inperm[ 1 ] = axlat; inperm[ 2 ] = axspec; tile_map = (AstMapping *) astCmpMap( tile_map, astPermMap( ndim, inperm, ndim, outperm, NULL, " " ), 1, " " ); tile_map = astSimplify( tile_map ); /* Also re-order the WCS axes in the tile frame. */ astPermAxes( tile_frm, outperm ); /* We want the zero-based indicies of the input pixel axes corresponding to ra, dec and spectral. So find the indicies of the pixel axes in the supplied NDF that are most closely aligned with each WCS axis. */ atlPairAxes( iwcs, NULL, gcen, NULL, inperm, status ); if( inperm[ 0 ] == axlon ) { lonax = 0; } else if( inperm[ 1 ] == axlon ) { lonax = 1; } else { lonax = 2; } if( inperm[ 0 ] == axlat ) { latax = 0; } else if( inperm[ 1 ] == axlat ) { latax = 1; } else { latax = 2; } /* To get the mapping from pixel coords in the input NDF to pixel coords in the output NDF, we invert the above mapping so that it goes from WCS to pixel, and append it to the end of the NDF pixel->WCS mapping. */ ndf_map = astGetMapping( iwcs, ipixel, AST__CURRENT ); astInvert( tile_map ); p2pmap = (AstMapping *) astCmpMap( ndf_map, tile_map, 1, " " ); p2pmap = astSimplify( p2pmap ); astInvert( tile_map ); /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the output NDF.", status, tile_index, tile_lbnd[ 0 ], tile_ubnd[ 0 ], tile_lbnd[ 1 ], tile_ubnd[ 1 ] ); /* Next job is to find the pixel bounds of the output NDF to create which will hold data for the current tile. First map the pixel bounds of the whole tile from output to input. */ lbnd_in[ 0 ] = tile_lbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = tile_lbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = lbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = tile_ubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = tile_ubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = ubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); lbnd_tile[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; lbnd_tile[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; lbnd_tile[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; ubnd_tile[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; ubnd_tile[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; ubnd_tile[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the input NDF.", status, tile_index, lbnd_tile[ 0 ], ubnd_tile[ 0 ], lbnd_tile[ 1 ], ubnd_tile[ 1 ] ); /* If required, trim the bounds to the extent of the input NDF. */ if( trim ) { if( lbnd_tile[ 0 ] < lbnd[ 0 ] ) lbnd_tile[ 0 ] = lbnd[ 0 ]; if( lbnd_tile[ 1 ] < lbnd[ 1 ] ) lbnd_tile[ 1 ] = lbnd[ 1 ]; if( lbnd_tile[ 2 ] < lbnd[ 2 ] ) lbnd_tile[ 2 ] = lbnd[ 2 ]; if( ubnd_tile[ 0 ] > ubnd[ 0 ] ) ubnd_tile[ 0 ] = ubnd[ 0 ]; if( ubnd_tile[ 1 ] > ubnd[ 1 ] ) ubnd_tile[ 1 ] = ubnd[ 1 ]; if( ubnd_tile[ 2 ] > ubnd[ 2 ] ) ubnd_tile[ 2 ] = ubnd[ 2 ]; } /* Check there is some overlap. */ if( lbnd_tile[ 0 ] <= ubnd_tile[ 0 ] && lbnd_tile[ 1 ] <= ubnd_tile[ 1 ] && lbnd_tile[ 2 ] <= ubnd_tile[ 2 ] ){ /* Now need to check if this section of the input NDF contains any good values. We also find the bounding box of the good values (within the input pixel coordinate system). So first obtain and map the required section of the input NDF. */ ndfSect( indf, ndim, lbnd_tile, ubnd_tile, &indfs, status ); ndfMap( indfs, "Data", type, "Read", &ipd, &junk, status ); if( var ) ndfMap( indfs, "Variance", type, "Read", &ipv, &junk, status ); if( qual ) ndfMap( indfs, "Quality", "_UBYTE", "Read", (void **) &ipq, &junk, status ); /* Initialise the pixel bounds (within the input NDF) of the box holding good data values for the current tile. */ bbox[ 0 ] = INT_MAX; bbox[ 1 ] = INT_MAX; bbox[ 2 ] = INT_MAX; bbox[ 3 ] = -INT_MAX; bbox[ 4 ] = -INT_MAX; bbox[ 5 ] = -INT_MAX; /* Loop round all pixels in the section. */ if( *status == SAI__OK ) { if( !strcmp( type, "_REAL" ) ) { pf = (float *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pf++) != VAL__BADR ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } else { pd = (double *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pd++) != VAL__BADD ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } /* Skip empty tiles. */ if( bbox[ 0 ] != INT_MAX ) { msgOutf( "", " tile %d", status, tile_index ); /* If required, trim the bounds to the edges of the bounding box. */ if( trim >= 2 ) { olbnd[ 0 ] = bbox[ 0 ]; olbnd[ 1 ] = bbox[ 1 ]; olbnd[ 2 ] = bbox[ 2 ]; oubnd[ 0 ] = bbox[ 3 ]; oubnd[ 1 ] = bbox[ 4 ]; oubnd[ 2 ] = bbox[ 5 ]; } else { olbnd[ 0 ] = lbnd_tile[ 0 ]; olbnd[ 1 ] = lbnd_tile[ 1 ]; olbnd[ 2 ] = lbnd_tile[ 2 ]; oubnd[ 0 ] = ubnd_tile[ 0 ]; oubnd[ 1 ] = ubnd_tile[ 1 ]; oubnd[ 2 ] = ubnd_tile[ 2 ]; } /* Modify these pixel bounds so that they refer to the output NDF. */ lbnd_in[ 0 ] = olbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = olbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = olbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = oubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = oubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = oubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); olbnd[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; olbnd[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; olbnd[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; oubnd[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; oubnd[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; oubnd[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Get the full path to the output NDF for the current tile, and create an NDF placeholder for it. */ sprintf( path, "%.*s_%d", nbase, base, tile_index ); ndfPlace( NULL, path, &place, status ); /* Create a new output NDF by copying the meta-data from the input NDF section. */ ndfScopy( indfs, "Units", &place, &indfo, status ); /* Set the pixel bounds of the output NDF to the values found above and copy the input data for the current tile into it. */ smf1_jsadicer( indfo, olbnd, oubnd, tile_map, tile_frm, p2pmap, ipd, ipv, ipq, status ); /* Add the name of this output NDF to the group holding the names of the output NDFs that have actually been created. */ if( grp ) grpPut1( grp, path, 0, status ); /* Add a TILENUM header to the output FITS extension. */ kpgGtfts( indfo, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = astFitsChan( NULL, NULL, " " ); /* If the last card is "END", remove it. */ } else { astSetI( fc, "Card", astGetI( fc, "NCARD" ) ); keyword = astGetC( fc, "CardName" ); if( keyword && !strcmp( keyword, "END" ) ) astDelFits( fc ); } one_snprintf(jsatile_comment, 45, "JSA all-sky tile index (Nside=%i)", status, tiling.ntpf); atlPtfti( fc, "TILENUM", tile_index, jsatile_comment, status ); kpgPtfts( indfo, fc, status ); fc = astAnnul( fc ); /* Now store an STC-S polygon that describes the shortest boundary enclosing the good data in the output NDF, and store it as an NDF extension. */ kpgPutOutline( indfo, 0.5, 1, status ); /* We now reshape any extension NDFs contained within the output NDF to have the same spatial bounds as the main NDF (but only for extension NDFs that originally have the same spatial bounds as the supplied NDF). Get a group containing paths to all extension NDFs in the output NDF. */ ndgMoreg( indfo, &grpt, &size, status ); /* Loop round each output extension NDF. */ for( iext = 1; iext <= size && *status == SAI__OK; iext++ ) { ndgNdfas( grpt, iext, "Update", &indfx, status ); /* Get its bounds. */ ndfBound( indfx, NDF__MXDIM, lbndx, ubndx, &ndimx, status ); /* See if this extension NDF has the same bounds on the spatial axes as the supplied NDF. */ if( ndimx > 1 && lbndx[ lonax ] == lbnd[ lonax ] && lbndx[ latax ] == lbnd[ latax ] && ubndx[ lonax ] == ubnd[ lonax ] && ubndx[ latax ] == ubnd[ latax ] ) { /* If so, change the bounds of the output extension NDF so that they are the same as the main NDF on the spatial axes, and map the original contents of the NDF onto the new pixel grid. */ smf1_jsadicer( indfx, olbnd, oubnd, tile_map, tile_frm, p2pmap, NULL, NULL, NULL, status ); } /* Annul the extension NDF identifier. */ ndfAnnul( &indfx, status ); } /* Free resources associated with the current tile. */ grpDelet( &grpt, status ); ndfAnnul( &indfo, status ); /* Issue warnings about empty tiles. */ } else { msgOutiff( MSG__VERB, "", " tile %d is empty and so will not be " "created", status, tile_index ); } } /* Free the section of the input NDF. */ ndfAnnul( &indfs, status ); /* Append the index of this tile in the list of tiles to be created. */ created_tiles = astGrow( created_tiles, ++(*ntile), sizeof( *created_tiles ) ); if( *status == SAI__OK ) created_tiles[ *ntile - 1 ] = tile_index; } else { msgOutiff( MSG__DEBUG, "", " Tile %d does not overlap the input " "NDF after trimming.", status, tile_index ); } } msgBlank( status ); /* Write the indicies of the created tiles out to a parameter. */ if( *ntile ) parPut1i( "JSATILELIST", *ntile, created_tiles, status ); /* Free resources. */ created_tiles = astFree( created_tiles ); tiles = astFree( tiles ); path = astFree( path ); /* End the NDF context. */ ndfEnd( status ); /* End the AST context. */ astEnd; }
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 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; }
/* 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 ); }
void findback( int *status ){ /* *+ * Name: * FINDBACK * Purpose: * Estimate the background in an NDF by removing small scale structure. * Language: * C * Type of Module: * ADAM A-task * Synopsis: * void findback( int *status ); * Description: * This application uses spatial filtering to remove structure with a * scale size less than a specified size from a 1, 2, or 3 dimensional * NDF, thus producing an estimate of the local background within the NDF. * * The algorithm proceeds as follows. A filtered form of the input data * is first produced by replacing every input pixel by the minimum of * the input values within a rectangular box centred on the pixel. * This filtered data is then filtered again, using a filter that * replaces every pixel value by the maximum value in a box centred on * the pixel. This produces an estimate of the lower envelope of the data, * but usually contains unacceptable sharp edges. In addition, this * filtered data has a tendency to hug the lower envelope of the * noise, thus under-estimating the true background of the noise-free * data. The first problem is minimised by smoothing the background * estimate using a filter that replaces every pixel value by the mean * of the values in a box centred on the pixel. The second problem * is minimised by estimating the difference between the input data * and the background estimate within regions well removed from any * bright areas. This difference is then extrapolated into the bright * source regions and used as a correction to the background estimate. * Specifically, the residuals between the input data and the initial * background estimate are first formed, and residuals which are more * than three times the RMS noise are set bad. The remaining residuals * are smoothed with a mean filter. This smoothing will replace a lot * of the bad values rejected above, but may not remove them all. Any * remaining bad values are estimated by linear interpolation between * the nearest good values along the first axis. The interpolated * residuals are then smoothed again using a mean filter, to get a * surface representing the bias in the initial background estimate. * This surface is finally added onto the initial background estimate * to obtain the output NDF. * Usage: * findback in out box * ADAM Parameters: * BOX() = _INTEGER (Read) * The dimensions of each of the filters, in pixels. Each value * should be odd (if an even value is supplied, the next higher odd * value will be used). The number of values supplied should not * exceed the number of significant (i.e. more than one element) * pixel axes in the input array. If any trailing values of 1 are * supplied, then each pixel value on the corresponding axes * will be fitted independently of its neighbours. For instance, * if the data array is 3-dimensional, and the third BOX value is 1, * then each x-y plane will be fitted independently of the neighbouring * planes. If the NDF has more than 1 pixel axis but only 1 value is * supplied, then the same value will be used for the both the first * and second pixel axes (a value of 1 will be assumed for the third * axis if the input array is 3-dimensional). * MSG_FILTER = _CHAR (Read) * Controls the amount of diagnostic information reported. This is the * standard messaging level. The default messaging level is NORM (2). * A value of NONE or 0 will suppress all screen output. VERB (3) will * indicate progress through the various stages of the algorithm. [NORM] * IN = NDF (Read) * The input NDF. * RMS = _DOUBLE (Read) * Specifies a value to use as the global RMS noise level in the * supplied data array. The suggested default value is the square root * of the mean of the values in the input NDF's Variance component. * If the NDF has no Variance component, the suggested default * is based on the differences between neighbouring pixel values, * measured over the entire input NDF. If multiple slices within the * NDF are to be processed independently (see parameter BOX), it * may be more appropriate for a separate default RMS to be calculated * for each slice. This will normally be the case if the noise could * be different in each of the slices. In such cases a null (!) can * be supplied for the RMS parameter, which forces a separate * default RMS value to be found and used for each slice. Any * pixel-to-pixel correlation in the noise can result in these * defaults being too low. * SUB = _LOGICAL (Read) * If a TRUE value is supplied, the output NDF will contain the * difference between the supplied input data and the estimated * background. If a FALSE value is supplied, the output NDF will * contain the estimated background itself. [FALSE] * OUT = NDF (Write) * The output NDF containing either the estimated background, or the * background-subtracted input data, as specified by parameter SUB. * Notes: * - Smoothing cubes in 3 dimensions can be very slow. * Copyright: * Copyright (C) 2009 Science and Technology Facilities Council. * Copyright (C) 2006, 2007 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: * 13-SEP-2006 (DSB): * Original version. * 19-MAR-2007 (DSB): * - Added parameters SUB and RMS. * - Fix bug that left the output NDF uninitialised if ILEVEL is set * non-zero. * - Use generic data type handling as in FINDCLUMPS. * 14-JAN-2009 (TIMJ): * Use MERS for message filtering. * 29-JUL-2009 (TIMJ): * Rename ILEVEL to MSG_FILTER * 17-MAY-2011 (DSB): * Use sqrt rather than sqrtf when calculating RMS. * 12-SEP-2011 (DSB): * Process slices in separate threads. * {enter_further_changes_here} *- */ /* Local Variables: */ CupidFindback0Data *job_data; /* Pointer to data for all jobs */ CupidFindback0Data *pdata; /* Pointer to data for current job */ Grp *grp; /* GRP identifier for configuration settings */ ThrWorkForce *wf = NULL; /* Pool of persistent worker threads */ char dtype[ 21 ]; /* HDS data type for output NDF */ char itype[ 21 ]; /* HDS data type to use when processing */ double *ipv; /* Pointer to Variance array */ double *pd1; /* Pointer to double precision input data */ double *pd2; /* Pointer to double precision output data */ double rms; /* Global rms error in data */ double sum; /* Sum of variances */ float *pf1; /* Pointer to single precision input data */ float *pf2; /* Pointer to single precision output data */ int *old_status; /* Pointer to original status value */ int box[ 3 ]; /* Dimensions of each cell in pixels */ int dim[ NDF__MXDIM ]; /* Dimensions of each NDF pixel axis */ int el; /* Number of elements mapped */ int i; /* Loop count */ int indf1; /* Identifier for input NDF */ int indf2; /* Identifier for output NDF */ int islice; /* Slice index */ int iystep; /* Index of slice in ydirection */ int izstep; /* Index of slice in z direction */ int lbnd[ NDF__MXDIM ]; /* Lower pixel bounds of slice */ int n; /* Number of values summed in "sum" */ int ndim; /* Total number of pixel axes in NDF */ int newalg; /* Use experimental algorithm variations? */ int nsdim; /* Number of significant pixel axes in NDF */ int nslice; /* Number of slices to process */ int nval; /* Number of values supplied */ int nystep; /* Number of independent y slices */ int nzstep; /* Number of slices in z direction */ int sdim[ 3 ]; /* Dimensions of each significant NDF axis */ int slice_dim[ 3 ]; /* Dimensions of each significant slice axis */ int slice_lbnd[ 3 ]; /* Lower bounds of each significant slice axis */ int slice_size; /* Number of pixels in each slice */ int state; /* Parameter state */ int sub; /* Output the background-subtracted input data? */ int type; /* Integer identifier for data type */ int ubnd[ NDF__MXDIM ]; /* Upper pixel bounds of slice */ int var; /* Does i/p NDF have a Variance component? */ size_t size; /* Size of GRP group */ void *ipd1; /* Pointer to input Data array */ void *ipd2; /* Pointer to output Data array */ void *ipdin; /* Pointer to input Data array */ void *ipdout; /* Pointer to output Data array */ /* Abort if an error has already occurred. */ if( *status != SAI__OK ) return; /* Start an NDF context */ ndfBegin(); /* Record the existing AST status pointer, and ensure AST uses the supplied status pointer instead. */ old_status = astWatch( status ); /* Get an identifier for the input NDF. We use NDG (via kpg1_Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &grp, &size, status ); ndgNdfas( grp, 1, "READ", &indf1, status ); grpDelet( &grp, status ); /* Get the pixel index bounds of the input NDF. */ ndfBound( indf1, NDF__MXDIM, lbnd, ubnd, &ndim, status ); /* Identify and count the number of significant axes (i.e. axes spanning more than 1 pixel). Also record their dimensions. */ nsdim = 0; for( i = 0; i < ndim; i++ ) { dim[ i ] = ubnd[ i ] - lbnd[ i ] + 1; if( dim[ i ] > 1 ) sdim[ nsdim++ ] = dim[ i ]; } /* If there are too many significant axes, report an error. */ if( nsdim > 3 && *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf1 ); msgSeti( "NS", nsdim ); errRep( "", "The NDF '^N' has ^NS significant pixel axes, but this" "application requires 1, 2 or 3.", status ); } /* Ensure we have 3 values in sdim (pad with trailings 1's if required). */ if( nsdim < 3 ) sdim[ 2 ] = 1; if( nsdim < 2 ) sdim[ 1 ] = 1; /* See if the output is to contain the background-subtracted data, or the background estimate itself. */ parGet0l( "SUB", &sub, status ); /* Create the output by propagating everything except the Data and (if we are outputting the background itself) Variance arrays. */ if( sub ) { ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY,VARIANCE", "OUT", &indf2, status ); } else { ndfProp( indf1, "UNITS,AXIS,WCS,QUALITY", "OUT", &indf2, status ); } msgBlankif( MSG__VERB, status ); /* Get the dimensions of each of the filters, in pixels. If only one value is supplied, duplicate it as the second value if the second axis is significant. If fewer than 3 values were supplied, use 1 for the 3rd value (whether or not it is significant). This results in each plane being fitted independently of the adjacent planes by default. */ parGet1i( "BOX", nsdim, box, &nval, status ); if( *status != SAI__OK ) goto L999; if( nval < 2 ) box[ 1 ] = ( nsdim > 1 ) ? box[ 0 ] : 1; if( nval < 3 ) box[ 2 ] = 1; /* Ensure box sizes are odd. */ box[ 0 ] = 2*( box[ 0 ] / 2 ) + 1; box[ 1 ] = 2*( box[ 1 ] / 2 ) + 1; box[ 2 ] = 2*( box[ 2 ] / 2 ) + 1; msgOutiff( MSG__VERB, "", "Using box sizes [%d,%d,%d].", status, box[0], box[1], box[2]); /* If any trailing axes have a cell size of 1, then we apply the algorithm independently to every pixel index on the trailing axes. First of all set things up assuming that there are no trailing axes with cell size of 1. */ nystep = 1; nzstep = 1; slice_dim[ 0 ] = sdim[ 0 ]; slice_dim[ 1 ] = sdim[ 1 ]; slice_dim[ 2 ] = sdim[ 2 ]; slice_lbnd[ 0 ] = lbnd[ 0 ]; slice_lbnd[ 1 ] = lbnd[ 1 ]; slice_lbnd[ 2 ] = lbnd[ 2 ]; /* If the 3rd pixel axis has a cell size of 1, arrange that each slice contains a single plane. */ if( box[ 2 ] == 1 ) { nzstep = sdim[ 2 ]; slice_dim[ 2 ] = 1; /* If the 2nd pixel axis also has a cell size of 1, arrange that each slice contains a single row. */ if( box[ 1 ] == 1 ) { nystep = sdim[ 1 ]; slice_dim[ 1 ] = 1; } } /* Determine the number of pixels in each independent slice. */ slice_size = slice_dim[ 0 ]*slice_dim[ 1 ]*slice_dim[ 2 ]; /* Decide what numeric data type to use, and set the output NDF data type. */ ndfMtype( "_REAL,_DOUBLE", indf1, indf1, "Data,Variance", itype, 20, dtype, 20, status ); if( !strcmp( itype, "_DOUBLE" ) ) { type = CUPID__DOUBLE; } else { type = CUPID__FLOAT; } ndfStype( dtype, indf2, "Data,Variance", status ); /* Map the input and output arrays. */ ndfMap( indf1, "Data", itype, "READ", &ipdin, &el, status ); ndfMap( indf2, "Data", itype, "WRITE", &ipdout, &el, status ); /* If the rms value is supplied on the command, there is no need to calculate a default value. */ parState( "RMS", &state, status ); if( state == PAR__GROUND ) { /* Calculate the default RMS value. If the NDF has a Variance component it is the square root of the mean Variance value. Otherwise, it is found by looking at differences between adjacent pixel values in the Data component. */ ndfState( indf1, "VARIANCE", &var, status ); if( *status == SAI__OK && var ) { ndfMap( indf1, "VARIANCE", "_DOUBLE", "READ", (void *) &ipv, &el, status ); sum = 0.0; n = 0; for( i = 0; i < el; i++ ) { if( ipv[ i ] != VAL__BADD ) { sum += ipv[ i ]; n++; } } if( n > 0 ) { rms = sqrt( sum/n ); } else { *status = SAI__ERROR; errRep( "", "The supplied data contains insufficient " "good Variance values to continue.", status ); } } else { ipv = NULL; rms = cupidRms( type, ipdin, el, sdim[ 0 ], status ); } /* Set the default RMS noise level. */ parDef0d( "RMS", rms, status ); } /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Get the RMS noise level. */ parGet0d( "RMS", &rms, status ); /* Annul the error and use an RMS value of VAL__BAD if a null parameter value was supplied. This causes an independent default noise estimate to be used for each slice of the base NDF. */ if( *status == PAR__NULL ) { errAnnul( status ); rms = VAL__BADD; } /* See if any experimental algorithm variations are to be used. */ parGet0l( "NEWALG", &newalg, status ); /* Create a pool of worker threads. */ wf = thrCreateWorkforce( thrGetNThread( "CUPID_THREADS", status ), status ); /* Get memory to hold a description of each job passed to a worker. There is one job for each slice. */ nslice = nystep*nzstep; job_data = astMalloc( nslice*sizeof( *job_data ) ); if( *status == SAI__OK ) { /* Loop round all slices to be processed. */ ipd1 = ipdin; ipd2 = ipdout; islice = 0; pdata = job_data; for( izstep = 0; izstep < nzstep ; izstep++ ) { slice_lbnd[ 1 ] = lbnd[ 1 ]; for( iystep = 0; iystep < nystep; iystep++, islice++,pdata++ ) { /* Store the information needed by the function (cupidFindback0) that does the work in a thread. */ pdata->islice = islice; pdata->nslice = nslice; pdata->type = type; pdata->ndim = ndim; pdata->box[ 0 ] = box[ 0 ]; pdata->box[ 1 ] = box[ 1 ]; pdata->box[ 2 ] = box[ 2 ]; pdata->rms = rms; pdata->ipd1 = ipd1; pdata->ipd2 = ipd2; pdata->slice_dim[ 0 ] = slice_dim[ 0 ]; pdata->slice_lbnd[ 0 ] = slice_lbnd[ 0 ]; pdata->slice_dim[ 1 ] = slice_dim[ 1 ]; pdata->slice_lbnd[ 1 ] = slice_lbnd[ 1 ]; pdata->slice_dim[ 2 ] = slice_dim[ 2 ]; pdata->slice_lbnd[ 2 ] = slice_lbnd[ 2 ]; pdata->newalg = newalg; pdata->slice_size = slice_size; /* Submit a job to the workforce to process the current slice. */ thrAddJob( wf, 0, pdata, cupidFindback0, 0, NULL, status ); /* Update pointers to the start of the next slice in the input and output arrays. */ if( type == CUPID__FLOAT ) { ipd1 = ( (float *) ipd1 ) + slice_size; ipd2 = ( (float *) ipd2 ) + slice_size; } else { ipd1 = ( (double *) ipd1 ) + slice_size; ipd2 = ( (double *) ipd2 ) + slice_size; } /* Increment the lower bound on the 2nd pixel axis. */ slice_lbnd[ 1 ]++; } /* Increment the lower bound on the 3rd pixel axis. */ slice_lbnd[ 2 ]++; } /* Wait until all jobs have finished. */ thrWait( wf, status ); } /* The output currently holds the background estimate. If the user has requested that the output should hold the background-subtracted input data, then do the arithmetic now. */ if( sub && *status == SAI__OK ) { if( type == CUPID__FLOAT ) { pf1 = (float *) ipdin; pf2 = (float *) ipdout; for( i = 0; i < el; i++, pf1++, pf2++ ) { if( *pf1 != VAL__BADR && *pf2 != VAL__BADR ) { *pf2 = *pf1 - *pf2; } else { *pf2 = VAL__BADR; } } } else { pd1 = (double *) ipdin; pd2 = (double *) ipdout; for( i = 0; i < el; i++, pd1++, pd2++ ) { if( *pd1 != VAL__BADD && *pd2 != VAL__BADD ) { *pd2 = *pd1 - *pd2; } else { *pd2 = VAL__BADD; } } } } /* Tidy up */ L999:; msgBlankif( MSG__VERB, status ); /* Free workspace. */ job_data = astFree( job_data ); wf = thrDestroyWorkforce( wf ); /* Reinstate the original AST inherited status value. */ astWatch( old_status ); /* End the NDF context */ 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( "FINDBACK_ERR", "FINDBACK: Failed to find the background " "of an NDF.", status ); } }
void smurf_unmakemap( int *status ) { /* Local Variables */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *skymap; /* GRID->SkyFrame Mapping from input WCS */ AstSkyFrame *abskyfrm; /* Input SkyFrame (always absolute) */ AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */ Grp *igrp1 = NULL; /* Group of input sky files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *igrpc = NULL; /* Group of input COM files */ Grp *igrpg = NULL; /* Group of input GAI files */ Grp *igrpq = NULL; /* Group of input Q sky files */ Grp *igrpu = NULL; /* Group of input U sky files */ Grp *ogrp = NULL; /* Group containing output file */ HDSLoc *cloc = NULL; /* HDS locator for component ipdata structure */ HDSLoc *iploc = NULL; /* HDS locator for top level ipdata structure */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ char ipdata[ 200 ]; /* Text buffer for IPDATA value */ char pabuf[ 10 ]; /* Text buffer for parameter value */ char subarray[ 5 ]; /* Name of SCUBA-2 subarray (s8a,s8b,etc) */ dim_t iel; /* Index of next element */ dim_t ndata; /* Number of elements in array */ dim_t ntslice; /* Number of time slices in array */ double *ang_data = NULL; /* Pointer to the FP orientation angles */ double *angc_data = NULL; /* Pointer to the instrumental ANGC data */ double *c0_data = NULL; /* Pointer to the instrumental C0 data */ double *gai_data = NULL; /* Pointer to the input GAI map */ double *in_data = NULL; /* Pointer to the input I sky map */ double *inc_data = NULL; /* Pointer to the input COM data */ double *inq_data = NULL; /* Pointer to the input Q sky map */ double *inu_data = NULL; /* Pointer to the input U sky map */ double *outq_data = NULL; /* Pointer to the Q time series data */ double *outu_data = NULL; /* Pointer to the U time series data */ double *p0_data = NULL; /* Pointer to the instrumental P0 data */ double *p1_data = NULL; /* Pointer to the instrumental P1 data */ double *pd; /* Pointer to next element */ double *pq = NULL; /* Pointer to next Q time series value */ double *pu = NULL; /* Pointer to next U time series value */ double *qinst_data = NULL; /* Pointer to the instrumental Q data */ double *uinst_data = NULL; /* Pointer to the instrumental U data */ double amp16; /* Amplitude of 16 Hz signal */ double amp2; /* Amplitude of 2 Hz signal */ double amp4; /* Amplitude of 4 Hz signal */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ double params[ 4 ]; /* astResample parameters */ double phase16; /* Phase of 16 Hz signal */ double phase2; /* Phase of 2 Hz signal */ double phase4; /* Phase of 4 Hz signal */ double sigma; /* Standard deviation of noise to add to output */ int alignsys; /* Align data in the map's system? */ int cdims[ 3 ]; /* Common-mode NDF dimensions */ int dims[ NDF__MXDIM ]; /* NDF dimensions */ int flag; /* Was the group expression flagged? */ int gdims[ 3 ]; /* GAI model NDF dimensions */ int harmonic; /* The requested harmonic */ int ifile; /* Input file index */ int indf; /* Input sky map NDF identifier */ int indfangc; /* IP ANGC values NDF identifier */ int indfc0; /* IP C0 values NDF identifier */ int indfc; /* Input COM NDF identifier */ int indfcs; /* NDF identifier for matching section of COM */ int indfg; /* Input GAI NDF identifier */ int indfin; /* Input template cube NDF identifier */ int indfiq; /* Input instrumental Q NDF */ int indfiu; /* Input instrumental U NDF */ int indfout; /* Output cube NDF identifier */ int indfp0; /* IP P0 values NDF identifier */ int indfp1; /* IP P1 values NDF identifier */ int indfq; /* Input Q map NDF identifier */ int indfu; /* Input U map NDF identifier */ int interp = 0; /* Pixel interpolation method */ int lbndc[ 3 ]; /* Array of lower bounds of COM NDF */ int moving; /* Is the telescope base position changing? */ int ndim; /* Number of pixel axes in NDF */ int ndimc; /* Number of pixel axes in common-mode NDF */ int ndimg; /* Number of pixel axes in GAI NDF */ int nel; /* Number of elements in array */ int nelc; /* Number of elements in COM array */ int nelg; /* Number of elements in GAI array */ int nelqu; /* Number of elements in Q or U array */ int ngood; /* No. of good values in putput cube */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int pasign; /* Indicates sense of POL_ANG value */ int sdim[ 2 ]; /* Array of significant pixel axes */ int slbnd[ 2 ]; /* Array of lower bounds of input map */ int subnd[ 2 ]; /* Array of upper bounds of input map */ int ubndc[ 3 ]; /* Array of upper bounds of COM NDF */ size_t ncom; /* Number of com files */ size_t ngai; /* Number of gai files */ size_t nskymap; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *odata = NULL; /* Pointer to output data struct */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ 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 an identifier for the input NDF. We use NDG (via kpg1Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status ); ndgNdfas( igrp1, 1, "READ", &indf, status ); /* Map the data array in the input sky map. */ ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel, status ); /* Get the WCS FrameSet from the sky map, together with its pixel index bounds. */ kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status ); /* Check the current Frame is a SKY frame. */ skyfrm = astGetFrame( wcsin, AST__CURRENT ); if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( " ", " Current Frame in ^N is not a SKY Frame.", status ); } /* Get a copy of the current frame that represents absolute coords rather than offsets. We assume the target is moving if the map represents offsets. */ moving = ( *status == SAI__OK && !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; abskyfrm = astCopy( skyfrm ); astClear( abskyfrm, "SkyRefIs" ); /* If the ALIGNSYS parameter is TRUE then we align the raw data with the map in the current system of the map, rather than the default ICRS. */ parGet0l( "ALIGNSYS", &alignsys, status ); if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm, "System" ) ); /* Get the Mapping from the Sky Frame to grid axis in the iput map. */ skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE ); /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get he noise level to add to the output data. */ parGet0d( "SIGMA", &sigma, status ); /* Get any Q and U input maps. */ if( *status == SAI__OK ) { kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status ); ndgNdfas( igrpq, 1, "READ", &indfq, status ); ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "Q", indfq ); *status = SAI__ERROR; errRep( "", "Q image '^Q' is not the same size as the I image.", status ); } kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status ); ndgNdfas( igrpu, 1, "READ", &indfu, status ); ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "U", indfu ); *status = SAI__ERROR; errRep( "", "U image '^U' is not the same size as the I image.", status ); } if( *status == PAR__NULL ) { ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); inq_data = NULL; inu_data = NULL; errAnnul( status ); } else { parGet0d( "ANGROT", &angrot, status ); parGet0d( "PAOFF", &paoff, status ); parGet0l( "PASIGN", &pasign, status ); } } /* Get any common-mode files. */ if( *status == SAI__OK ) { kpg1Rgndf( "COM", size, size, "", &igrpc, &ncom, status ); if( *status == PAR__NULL ) { errAnnul( status ); ncom = 0; } } /* Get any GAI files. */ if( *status == SAI__OK ) { kpg1Rgndf( "GAI", size, size, "", &igrpg, &ngai, status ); if( *status == PAR__NULL ) { errAnnul( status ); ngai = 0; } } /* Get any instrumental polarisation files. */ if( *status == SAI__OK ) { /* First see if the user wants to use the "INSTQ/INSTU" scheme for specifying instrumental polarisation. */ ndfAssoc( "INSTQ", "Read", &indfiq, status ); ndfAssoc( "INSTU", "Read", &indfiu, status ); if( *status == PAR__NULL ) { ndfAnnul( &indfiq, status ); ndfAnnul( &indfiu, status ); errAnnul( status ); } else { msgOut( " ", "Using user-defined IP model", status ); ndfDim( indfiq, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiq ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiq, "DATA", "_DOUBLE", "READ", (void **) &qinst_data, &nel, status ); } ndfDim( indfiu, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfiu ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfiu, "DATA", "_DOUBLE", "READ", (void **) &uinst_data, &nel, status ); } } /* If not, see if the user wants to use the Johnstone/Kennedy instrumental polarisation model. The IPDATA parameter gives the path to an HDS container file contining NDFs holding the required IP data for all subarrays. */ if( !qinst_data ) { parGet0c( "IPDATA", ipdata, sizeof(ipdata), status ); if( *status == PAR__NULL ) { errAnnul( status ); } else { msgOutf( " ", "Using Johnstone/Kennedy IP model in %s", status, ipdata ); hdsOpen( ipdata, "READ", &iploc, status ); } } } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Create the output NDF by propagating everything from the input, except for quality and variance. */ ndgNdfas( igrp2, ifile, "READ", &indfin, status ); ndfMsg( "FILE", indfin ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)", ogrp, ifile, &indfout, status ); ndfAnnul( &indfin, status ); ndfAnnul( &indfout, status ); /* We now re-open the output NDF and then modify its data values. */ smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( odata->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( odata->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Check the reference time series contains double precision values. */ smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status ); /* Get the total number of data elements, and the number of time slices. */ smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status ); /* Get the subarray name */ smf_fits_getS( odata->hdr, "SUBARRAY", subarray, sizeof(subarray), status ); /* If we are using the Johnstone/Kennedy IP model, open and map the relevant parameter NDFs within the IPDATA container file. */ if( iploc ) { datFind( iploc, subarray, &cloc, status ); ndfFind( cloc, "C0", &indfc0, status ); ndfDim( indfc0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfc0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfc0, "DATA", "_DOUBLE", "READ", (void **) &c0_data, &nel, status ); } ndfFind( cloc, "P0", &indfp0, status ); ndfDim( indfp0, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp0 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp0, "DATA", "_DOUBLE", "READ", (void **) &p0_data, &nel, status ); } ndfFind( cloc, "P1", &indfp1, status ); ndfDim( indfp1, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfp1 ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfp1, "DATA", "_DOUBLE", "READ", (void **) &p1_data, &nel, status ); } ndfFind( cloc, "ANGC", &indfangc, status ); ndfDim( indfangc, 2, dims, &ndim, status ); if( dims[ 0 ] != 32 || dims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "N", indfangc ); errRep( " ", "Instrumental polarisation file ^N has bad " "dimensions - should be 32x40.", status ); } else { ndfMap( indfangc, "DATA", "_DOUBLE", "READ", (void **) &angc_data, &nel, status ); } } /* Open any COM file. */ if( ncom ) { ndgNdfas( igrpc, ifile, "READ", &indfc, status ); ndfDim( indfc, 3, cdims, &ndimc, status ); /* Check its dimensions. */ if( *status == SAI__OK ) { if( ndimc == 1 ) { if( cdims[ 0 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 0 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 0 ] = lbndc[ 0 ] + ntslice - 1; ndfSect( indfc, 1, lbndc, ubndc, &indfcs, status ); } } else if( ndimc == 3 ) { if( cdims[ 0 ] != 1 || cdims[ 1 ] != 1 ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); errRep( " ", "Supplied 3D COM file (^C) has bad " "dimensions for axis 1 and/or 2 (should " "both be 1 pixel long).", status ); } else if( cdims[ 2 ] < (int) ntslice ) { *status = SAI__ERROR; ndfMsg( "C", indfc ); ndfMsg( "R", indfin ); msgSeti( "N", cdims[ 2 ] ); msgSeti( "M", ntslice ); errRep( " ", "Supplied COM file (^C) has ^N time-slices, but " "the reference NDF (^R) has ^M time-slices.", status ); } else { ndfBound( indfc, 3, lbndc, ubndc, &ndimc, status ); ubndc[ 2 ] = lbndc[ 2 ] + ntslice - 1; ndfSect( indfc, 3, lbndc, ubndc, &indfcs, status ); } } else { *status = SAI__ERROR; ndfMsg( "C", indfc ); msgSeti( "N", ndimc ); errRep( " ", "Supplied COM file (^C) has ^N dimensions - " "must be 3.", status ); } } ndfMap( indfcs, "DATA", "_DOUBLE", "READ", (void **) &inc_data, &nelc, status ); } else { indfcs = NDF__NOID; inc_data = NULL; } /* Open any GAI files. */ if( ngai ) { ndgNdfas( igrpg, ifile, "READ", &indfg, status ); ndfDim( indfg, 3, gdims, &ndimg, status ); /* Check its dimensions, and map it if OK. */ if( *status == SAI__OK ) { if( ndimg != 2 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); msgSeti( "N", ndimg ); errRep( " ", "Supplied GAI file (^C) has ^N dimensions - " "must be 2.", status ); } else if( gdims[ 0 ] != 32 || gdims[ 1 ] != 40 ) { *status = SAI__ERROR; ndfMsg( "C", indfg ); errRep( " ", "Supplied GAI file (^C) has has bad " "dimensions - should be 32x40.", status ); } } ndfMap( indfg, "DATA", "_DOUBLE", "READ", (void **) &gai_data, &nelg, status ); } else { indfg = NDF__NOID; gai_data = NULL; } /* Fill the output with bad values. */ if( *status == SAI__OK ) { pd = odata->pntr[ 0 ]; for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD; } /* Resample the sky map data into the output time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, in_data, odata->pntr[ 0 ], NULL, &ngood, status ); /* Add on any COM data. */ smf_addcom( wf, odata, inc_data, status ); /* Issue a wrning if there is no good data in the output cube. */ if( ngood == 0 ) msgOutif( MSG__NORM, " ", " Output contains no " "good data values.", status ); /* If Q and U maps have been given, allocate room to hold resampled Q and U values, and fill them with bad values. */ if( inq_data && inu_data ) { pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) ); pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) ); if( *status == SAI__OK ) { for( iel = 0; iel < ndata; iel++ ) { *(pu++) = VAL__BADD; *(pq++) = VAL__BADD; } } /* Determine the harmonic to use. */ parGet0i( "HARMONIC", &harmonic, status ); /* If producing the normal 8 Hz harmonic, get the amplitude and phase of a other signals to add onto the 8 Hz signal. */ if( harmonic == 4 ) { parGet0d( "AMP2", &2, status ); parGet0d( "PHASE2", &phase2, status ); parGet0d( "AMP4", &4, status ); parGet0d( "PHASE4", &phase4, status ); parGet0d( "AMP16", &16, status ); parGet0d( "PHASE16", &phase16, status ); } else { amp2 = 0.0; phase2 = 0.0; amp4 = 0.0; phase4 = 0.0; amp16 = 0.0; phase16 = 0.0; } /* Allocate room for an array to hold the angle from the Y pixel axis in the sky map to the focal plane Y axis, in radians, at each time slice. Positive rotation is in the same sense as rotation from focal plane X to focal plane Y. */ ang_data = astMalloc( ntslice*sizeof( *ang_data ) ); /* Resample them both into 3D time series. These Q/U values arw with respect to the sky image Y axis. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inq_data, outq_data, ang_data, &ngood, status ); smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inu_data, outu_data, NULL, &ngood, status ); /* Combine these time series with the main output time series so that the main output is analysed intensity. */ smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data, ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot, amp2, AST__DD2R*phase2, amp4, AST__DD2R*phase4, amp16, AST__DD2R*phase16, qinst_data, uinst_data, c0_data, p0_data, p1_data, angc_data, harmonic, status ); /* Release work space. */ outq_data = astFree( outq_data ); outu_data = astFree( outu_data ); ang_data = astFree( ang_data ); } /* Factor in any GAI data. */ smf_addgai( wf, odata, gai_data, status ); /* Close the output time series file. */ smf_close_file( wf, &odata, status ); /* Close the IP data container for the current subarray, if it is open. */ if( cloc ) datAnnul( &cloc, status ); /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( odata != NULL ) { smf_close_file( wf, &odata, status ); odata = NULL; } /* Free remaining resources. */ if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( igrpq != NULL) grpDelet( &igrpq, status); if( igrpu != NULL) grpDelet( &igrpu, status); if( igrpc != NULL) grpDelet( &igrpc, status); if( igrpg != NULL) grpDelet( &igrpg, status); if( ogrp != NULL) grpDelet( &ogrp, status); if( iploc ) datAnnul( &iploc, status ); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
void smf_write_shortmap( ThrWorkForce *wf, int shortmap, smfArray *res, smfArray *lut, smfArray *qua, smfDIMMData *dat, dim_t msize, const Grp *shortrootgrp, size_t contchunk, int varmapmethod, const int *lbnd_out, const int *ubnd_out, AstFrameSet *outfset, int *status ) { dim_t dsize; /* Size of data arrays in containers */ size_t i; /* loop counter */ size_t idx=0; /* index within subgroup */ size_t istart; /* First useful timeslice */ size_t iend; /* Last useful timeslice */ int *lut_data=NULL; /* Pointer to DATA component of lut */ char name[GRP__SZNAM+1]; /* Buffer for storing names */ size_t nshort=0; /* Number of short maps */ dim_t ntslice; /* Number of time slices */ char *pname=NULL; /* Poiner to name */ smf_qual_t *qua_data=NULL; /* Pointer to DATA component of qua */ double *res_data=NULL; /* Pointer to DATA component of res */ size_t sc; /* Short map counter */ double *shortmapweight=NULL; /* buffer for shotmap weights */ double *shortmapweightsq=NULL;/* buffer for shotmap weights squared */ int *shorthitsmap=NULL; /* buffer for shotmap hits */ size_t shortstart; /* first time slice of short map */ size_t shortend; /* last time slice of short map */ size_t tstride; /* Time stride */ if( *status != SAI__OK ) return; if( !res || !lut || !qua || !dat || !shortrootgrp || !lbnd_out || !ubnd_out || !outfset || !shortmap ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL inputs supplied", status ); return; } if( !res || !res->sdata || !res->sdata[idx] || !res->sdata[idx]->hdr || !res->sdata[idx]->hdr->allState ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": RES does not contain JCMTState", status ); return; } /* Allocate space for the arrays */ shortmapweight = astMalloc( msize*sizeof(*shortmapweight) ); shortmapweightsq = astMalloc( msize*sizeof(*shortmapweightsq) ); shorthitsmap = astMalloc( msize*sizeof(*shorthitsmap) ); /* Use first subarray to figure out time dimension. Get the useful start and end points of the time series, and then determine "nshort" -- the number of complete blocks of shortmap time slices in the useful range. */ smf_get_dims( qua->sdata[0], NULL, NULL, NULL, &ntslice, NULL, NULL, &tstride, status ); qua_data = (qua->sdata[0]->pntr)[0]; smf_get_goodrange( qua_data, ntslice, tstride, SMF__Q_BOUND, &istart, &iend, status ); shortstart = istart; if( *status == SAI__OK ) { if( shortmap == -1 ) { nshort = res->sdata[idx]->hdr->allState[iend].tcs_index - res->sdata[idx]->hdr->allState[istart].tcs_index + 1; msgOutf( "", FUNC_NAME ": writing %zu short maps, once each time TCS_INDEX increments", status, nshort ); } else { nshort = (iend-istart+1)/shortmap; if( nshort ) { msgOutf( "", FUNC_NAME ": writing %zu short maps of length %i time slices.", status, nshort, shortmap ); } else { /* Generate warning message if requested short maps are too long*/ msgOutf( "", FUNC_NAME ": Warning! short maps of lengths %i requested, but " "data only %zu time slices.", status, shortmap, iend-istart+1 ); } } } /* Loop over short maps */ for( sc=0; (sc<nshort)&&(*status==SAI__OK); sc++ ) { Grp *mgrp=NULL; /* Temporary group for map names */ smfData *mapdata=NULL; /* smfData for new map */ char tempstr[20]; /* Temporary string */ char tmpname[GRP__SZNAM+1]; /* temp name buffer */ char thisshort[20]; /* name particular to this shortmap */ /* Create a name for the new map, take into account the chunk number. Only required if we are using a single output container. */ pname = tmpname; grpGet( shortrootgrp, 1, 1, &pname, sizeof(tmpname), status ); one_strlcpy( name, tmpname, sizeof(name), status ); one_strlcat( name, ".", sizeof(name), status ); /* Continuous chunk number */ sprintf(tempstr, "CH%02zd", contchunk); one_strlcat( name, tempstr, sizeof(name), status ); /* Shortmap number */ sprintf( thisshort, "SH%06zu", sc ); one_strlcat( name, thisshort, sizeof(name), status ); mgrp = grpNew( "shortmap", status ); grpPut1( mgrp, name, 0, status ); msgOutf( "", "*** Writing short map (%zu / %zu) %s", status, sc+1, nshort, name ); smf_open_newfile ( wf, mgrp, 1, SMF__DOUBLE, 2, lbnd_out, ubnd_out, SMF__MAP_VAR, &mapdata, status); /* Time slice indices for start and end of short map -- common to all subarrays */ if( shortmap > 0) { /* Evenly-spaced shortmaps in time */ shortstart = istart+sc*shortmap; shortend = istart+(sc+1)*shortmap-1; } else { /* One map each time TCS_INDEX increments -- just uses header for the first subarray */ for(i=shortstart+1; (i<=iend) && (res->sdata[0]->hdr->allState[i].tcs_index == res->sdata[0]->hdr->allState[shortstart].tcs_index); i++ ); shortend = i-1; } /* Bad status if we have invalid shortmap ranges. This might happen if there is ever a jump in TCS_INDEX for the shortmap=-1 case since the total number of shortmaps is calculated simply as the difference between the first and final TCS indices. */ if( !nshort || (iend<istart) || (iend>=ntslice) ) { *status = SAI__ERROR; errRepf( "", FUNC_NAME ": invalid shortmap range (%zu--%zu, ntslice=%zu)" "encountered", status, istart, iend, ntslice ); break; } /* Loop over subgroup index (subarray) */ for( idx=0; (idx<res->ndat)&&(*status==SAI__OK); idx++ ) { int rebinflag = 0; /* Pointers to everything we need */ res_data = (res->sdata[idx]->pntr)[0]; lut_data = (lut->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; smf_get_dims( res->sdata[idx], NULL, NULL, NULL, &ntslice, &dsize, NULL, &tstride, status ); /* Rebin the data for this range of tslices. */ if( idx == 0 ) { rebinflag |= AST__REBININIT; } if( idx == (res->ndat-1) ) { rebinflag |= AST__REBINEND; } smf_rebinmap1( NULL, res->sdata[idx], dat->noi ? dat->noi[0]->sdata[idx] : NULL, lut_data, shortstart, shortend, 1, NULL, 0, SMF__Q_GOOD, varmapmethod, rebinflag, mapdata->pntr[0], shortmapweight, shortmapweightsq, shorthitsmap, mapdata->pntr[1], msize, NULL, status ); /* Write out FITS header */ if( (*status == SAI__OK) && res->sdata[idx]->hdr && res->sdata[idx]->hdr->allState ) { AstFitsChan *fitschan=NULL; JCMTState *allState = res->sdata[idx]->hdr->allState; size_t midpnt = (shortstart + shortend) / 2; fitschan = astFitsChan ( NULL, NULL, " " ); atlPtfti( fitschan, "SEQSTART", allState[shortstart].rts_num, "RTS index number of first frame", status ); atlPtfti( fitschan, "SEQEND", allState[shortend].rts_num, "RTS index number of last frame", status); atlPtftd( fitschan, "MJD-AVG", allState[midpnt].rts_end, "Average MJD of this map", status ); atlPtfts( fitschan, "TIMESYS", "TAI", "Time system for MJD-AVG", status ); atlPtfti( fitschan, "TCSINDST", allState[shortstart].tcs_index, "TCS index of first frame", status ); atlPtfti( fitschan, "TCSINDEN", allState[shortend].tcs_index, "TCS index of last frame", status ); kpgPtfts( mapdata->file->ndfid, fitschan, status ); if( fitschan ) fitschan = astAnnul( fitschan ); } } /* Update shortstart in case we are counting steps in TCS_INDEX */ shortstart = shortend+1; /* Write WCS */ smf_set_moving( (AstFrame *) outfset, NULL, status ); ndfPtwcs( outfset, mapdata->file->ndfid, status ); /* Clean up */ if( mgrp ) grpDelet( &mgrp, status ); smf_close_file( wf, &mapdata, status ); } /* Free up memory */ shortmapweight = astFree( shortmapweight ); shortmapweightsq = astFree( shortmapweightsq ); shorthitsmap = astFree( shorthitsmap ); }
void smurf_fts2_split(int* status) { if( *status != SAI__OK ) { return; } const double STAGE_LENGTH = 450.0; /* mm */ int LR = 0; /* Treat as Low Resolution scan */ Grp* gIn = NULL; /* Input group */ Grp* gOut = NULL; /* Output group */ Grp* gTmp = NULL; /* Temporary group */ smfData* inData = NULL; /* Pointer to input data */ smfData* outData = NULL; /* Pointer to output data */ double* outData_pntr = NULL; /* Pointer to output data values array */ int nMirPos = 0; /* Number of frames where the mirror actually moves */ int nStart = 0; /* Frame index where the mirror starts moving */ int nStartNext = 0; /* Frame index where the mirror starts moving in the next scan */ int nStop = 0; /* Frame index where the mirror stops */ int lrStart = 0; /* Frame index where low resolution mirror limit starts */ int hrStop = 0; /* Frame index where high resolution mirror limit stops */ int hrStart = 0; /* Frame index where high resolution mirror limit starts */ int lrStop = 0; /* Frame index where low resolution mirror limit stops */ int lrCentre = 0; /* Frame index at centre of low resolution mirror positions */ int i = 0; /* Counter */ int j = 0; /* Counter */ int k = 0; /* Counter */ int n = 0; /* Counter */ double fNyquist = 0.0; /* Nyquist frequency */ double dz = 0.0; /* Step size in evenly spaced OPD grid */ double* MIRPOS = NULL; /* Mirror positions */ size_t nFiles = 0; /* Size of the input group */ size_t nOutFiles = 0; /* Size of the output group */ size_t fIndex = 0; /* File index */ size_t nWidth = 0; /* Data cube width */ size_t nHeight = 0; /* Data cube height */ size_t nFrames = 0; /* Data cube depth in input file */ size_t nFramesOut = 0; /* Data cube depth in output file */ size_t nFramesOutPrev = 0; /* Data cube depth in previous output file */ size_t hrFramesOut = 0; /* Data cube depth in high res output file */ size_t hrFramesOutPrev = 0; /* Data cube depth in previous high res output file */ size_t lrFramesOut = 0; /* Data cube depth in low res output file */ size_t lrFramesOutPrev = 0; /* Data cube depth in previous low res output file */ size_t nPixels = 0; /* Number of bolometers in the subarray */ char object[SZFITSTR]; char subarray[SZFITSTR]; char obsID[SZFITSTR]; char scanMode[SZFITSTR]; double scanVel = 0.0; /* Mirror speed in mm/sec */ double stepTime = 0.0; /* RTS step time, average sample rate */ double minOPD = 0; /* OPD minimum */ double maxOPD = 0; /* OPD maximum */ double ZPD = 0; double lrmmBandPass = 0.0; /* low res mm +/- offset from centre */ int lrBandPassFrames = 0; /* Number of low res band pass frames from centre +/- length of lrmmBandPass */ int nTmp = 0; int nMax = 0; int nOPD = 0; int bolIndex = 0; int index = 0; int indexIn = 0; int indexOut = 0; int badPixel = 0; int k0 = 0; int indexZPD = 0; int done = 0; /* Track completion of extracting multiple scans */ int outDataCount = 0; /* The number of output data files being written */ double lenLeft, lenRight, minLenLeft, minLenRight, minLen, minZPD, maxZPD, midZPD = 0.0; /* Mirror position half side measures */ int midZPDPos = 0; /* Middle ZPD position in mirror position array */ double EPSILON = 0.0; char fileName[SMF_PATH_MAX+1]; char scanNumStr[5+1]; /* String form of scan number of the input file */ int scanNum = 0; /* Scan number of the input file */ int conNum = 0; /* Concatenation number of the input file (left shifted scanNum) */ int scanDir = 0; /* Scan direction: 1 -> back to front (positive), -1 -> front to back (negative) */ JCMTState *allState = NULL; /* Temporary buffer for reduced header allState array data */ /* Get Input, Output groups */ kpg1Rgndf("IN", 0, 1, "", &gIn, &nFiles, status); kpg1Wgndf("OUT", gOut, nFiles, nFiles, "More output files expected!", &gOut, &nOutFiles, status); /* Read in ADAM parameters */ parGet0d("BANDPASS", &lrmmBandPass, status); /* Low res mm band +/- offset from centre */ /* Treat as Low Resolution scan? */ if(lrmmBandPass > 0) { LR = 1; } /* Eliminate the first record in the output group, since it will be replaced later */ gTmp = grpCopy(gOut, 1, 1, 1, status); grpDelet(&gOut, status); gOut = gTmp; /* BEGIN NDF */ ndfBegin(); /* Loop through each input file */ for(fIndex = 1; fIndex <= nFiles; fIndex++) { /* Open Observation file */ smf_open_file(gIn, fIndex, "READ", 0, &inData, status); if(*status != SAI__OK) { *status = SAI__ERROR; errRep(FUNC_NAME, "Unable to open the source file!", status); goto CLEANUP; } smf_fits_getS(inData->hdr, "OBJECT", object, sizeof(object), status); smf_fits_getS(inData->hdr, "SUBARRAY", subarray, sizeof(subarray), status); smf_fits_getS(inData->hdr, "OBSID", obsID, sizeof(obsID), status); smf_fits_getS(inData->hdr, "FTS_MODE", scanMode, sizeof(scanMode), status); smf_fits_getD(inData->hdr, "SCANVEL", &scanVel, status); smf_fits_getD(inData->hdr, "STEPTIME", &stepTime, status); /* Nyquist frequency */ fNyquist = 10.0 / (8.0 * scanVel * stepTime); dz = 1.0 / (2.0 * fNyquist); EPSILON = scanVel * stepTime / 2; /* Extract the scan number from the input file to be incremented in the output files */ one_strlcpy(scanNumStr, &(inData->file->name[strlen(inData->file->name) - 8]), astMIN(SMF_PATH_MAX + 1, 5), status); if (*status == ONE__TRUNC) { errRep(FUNC_NAME, "Error extracting scanNumStr!", status); errAnnul(status); } /* Create a temporary base file name from input file name */ one_strlcpy(fileName, inData->file->name, astMIN(SMF_PATH_MAX + 1, strlen(inData->file->name) - 7), status); if (*status == ONE__TRUNC) { errRep(FUNC_NAME, "Error extracting base fileName!", status); errAnnul(status); } scanNum = (int) one_strtod(scanNumStr, status); if (*status != SAI__OK) { errRep(FUNC_NAME, "Error extracting scanNum!", status); errAnnul(status); } /* Left shift scanNum to conNum as a prefix to make output scan number unique */ if(scanNum < 100) { conNum = scanNum * 100; } else if(scanNum < 1000) { conNum = scanNum * 10; } /*printf("%s: Processing file: %s, having basename: %s and scanNumStr: %s, scanNum: %04d\n", TASK_NAME, inData->file->name, fileName, scanNumStr, scanNum);*/ /* Data cube dimensions */ nWidth = inData->dims[0]; nHeight = inData->dims[1]; nFrames = inData->dims[2]; nPixels = nWidth * nHeight; /* Mirror positions in mm */ nTmp = nFrames; MIRPOS = astCalloc(nFrames, sizeof(*MIRPOS)); fts2_getmirrorpositions(inData, MIRPOS, &nTmp, status); // (mm) if(*status != SAI__OK) { *status = SAI__ERROR; errRep( FUNC_NAME, "Unable to get the mirror positions!", status); goto CLEANUP; } nStart = -1; nStop = -1; nStartNext = 0; hrStart = -1; hrStop = -1; lrStart = -1; lrStop = -1; outDataCount = 0; done = 0; do { /* Find the next range of single scan mirror positions for which to extract corresponding NDF data */ for(n=nStartNext; n<nFrames-1; n++){ if(hrStart < 0 && fabs(MIRPOS[n+1] - MIRPOS[n]) >= EPSILON) { nStart = n; hrStart = n; /*printf("%s: Split nStart=%d\n", TASK_NAME, nStart);*/ } if(hrStart >= 0 && hrStop < 0 && (fabs(MIRPOS[n+1] - MIRPOS[n]) < EPSILON || n+1 == nFrames-1)) { hrStop = n+1; hrFramesOutPrev = hrFramesOut; hrFramesOut = abs(hrStop - hrStart) + 1; outDataCount++; nStop = hrStop; nFramesOutPrev = hrFramesOutPrev; nFramesOut = hrFramesOut; /*printf("%s: Split: %d of %d frames found at hrStart=%d, hrStop=%d\n", TASK_NAME, outDataCount, hrFramesOut, hrStart, hrStop);*/ break; } } /* Determine scan direction */ if(MIRPOS[hrStart] < MIRPOS[hrStop]) { scanDir = 1; /* Positive */ } else { scanDir = -1; /* Negative */ } /* Limit to specified mirror position range */ if(LR) { /* Calculate how many frames correspond to the given +/- mm of LR bandpass */ lrBandPassFrames = lrmmBandPass / dz; /* Find the centre of the current scan */ lrCentre = floor((abs(hrStop-hrStart)+1)/2); /* Set low res start and stop values at corresponding frame offsets from centre */ lrStart = lrCentre - lrBandPassFrames; lrStop = lrCentre + lrBandPassFrames; lrFramesOutPrev = lrFramesOut; lrFramesOut = abs(lrStop - lrStart) + 1; nStart = lrStart; nStop = lrStop; nFramesOutPrev = lrFramesOutPrev; nFramesOut = lrFramesOut; /*printf("%s: LR Split: %d of %d frames found at lrStart=%d, lrStop=%d\n", TASK_NAME, outDataCount, lrFramesOut, lrStart, lrStop);*/ } /* Check for end of data condition */ if(hrStop < hrStart || hrStop >= nFrames-1) { hrStop = nFrames-1; done = 1; } /* Output scan if there is a start and stop position found, and for the last scan if it's the only one and if it's not too short (compared to the previous one) */ /*printf("%s: nStart=%d, nStop=%d, nFramesOutPrev=%d, nFramesOut=%d\n", TASK_NAME, nStart, nStop, nFramesOutPrev, nFramesOut);*/ if(nStart >=0 && nStop > 0 && (nFramesOutPrev == 0 || (nFramesOutPrev > 0 && nFramesOut > 0 && (double)hrFramesOut/(double)hrFramesOutPrev >= 0.5))) { /* Copy single scan NDF data from input to output */ outData = smf_deepcopy_smfData(inData, 0, SMF__NOCREATE_DATA | SMF__NOCREATE_FTS, 0, 0, status); outData->dtype = SMF__DOUBLE; outData->ndims = 3; outData->dims[0] = nWidth; outData->dims[1] = nHeight; outData->dims[2] = nFramesOut; outData_pntr = (double*) astMalloc((nPixels * nFramesOut) * sizeof(*outData_pntr)); outData->pntr[0] = outData_pntr; outData->hdr->nframes = nFramesOut; for(i=0; i<nWidth; i++) { for(j=0; j<nHeight; j++) { bolIndex = i + j * nWidth; for(k=nStart; k<=nStop; k++) { indexIn = bolIndex + k * nPixels; indexOut = bolIndex + (k-nStart) * nPixels; *((double*)(outData->pntr[0]) + indexOut) = *((double*)(inData->pntr[0]) + indexIn); } } } /* Update the FITS headers */ outData->fts = smf_create_smfFts(status); /* Update FITS component */ smf_fits_updateD(outData->hdr, "FNYQUIST", fNyquist, "Nyquist frequency (cm^-1)", status); smf_fits_updateI(outData->hdr, "MIRSTART", 1, "Frame index in which the mirror starts moving", status); smf_fits_updateI(outData->hdr, "MIRSTOP", nFramesOut, "Frame index in which the mirror stops moving", status); smf_fits_updateI(outData->hdr, "SCANDIR", scanDir, "Scan direction", status); smf_fits_updateD(outData->hdr, "OPDMIN", 0.0, "Minimum OPD", status); smf_fits_updateD(outData->hdr, "OPDSTEP", 0.0, "OPD step size", status); /* Update the JCMTSTATE header */ /* Reallocate outData header array memory to reduced size */ allState = (JCMTState*) astRealloc(outData->hdr->allState, nFramesOut * sizeof(*(outData->hdr->allState))); if(*status == SAI__OK && allState) { outData->hdr->allState = allState; } else { errRepf(TASK_NAME, "Error reallocating allState JCMTState header", status); goto CLEANUP; } for(k=nStart; k<=nStop; k++) { /* Copy over JCMTstate */ /*printf("%s: memcpy allState: %d to: %p from: %p size: %d\n",TASK_NAME, k, (void *) &(outData->hdr->allState[k-nStart]), (void *) &(inData->hdr->allState[k]), sizeof(*(outData->hdr->allState)) );*/ memcpy( (void *) &(outData->hdr->allState[k-nStart]), (void *) &(inData->hdr->allState[k]), sizeof(*(outData->hdr->allState)) ); /*printf("%s: Scan: %d index: %d rts_num: %d\n", TASK_NAME, outDataCount, k-nStart, outData->hdr->allState[k-nStart].rts_num);*/ /*printf("%s: Scan: %d index: %d fts_pos: %f\n", TASK_NAME, outDataCount, k-nStart, outData->hdr->allState[k-nStart].fts_pos);*/ } /* Write output */ /* Append unique suffix to fileName */ /* This must be modified by the concatenation file scan number to improve uniqueness */ n = one_snprintf(outData->file->name, SMF_PATH_MAX, "%s%04d_scn.sdf", status, fileName, conNum+outDataCount); /*printf("%s: Writing outData->file->name: %s\n", TASK_NAME, outData->file->name);*/ if(n < 0 || n >= SMF_PATH_MAX) { errRepf(TASK_NAME, "Error creating outData->file->name", status); goto CLEANUP; } /* Update the list of output _scn file names */ grpPut1(gOut, outData->file->name, 0, status); if(*status != SAI__OK) { errRepf(TASK_NAME, "Error saving outData file name", status); goto CLEANUP; } smf_write_smfData(outData, NULL, outData->file->name, gOut, fIndex, 0, MSG__VERB, 0, status); if(*status != SAI__OK) { errRepf(TASK_NAME, "Error writing outData file", status); goto CLEANUP; } smf_close_file(&outData, status); if(*status != SAI__OK) { errRepf(TASK_NAME, "Error closing outData file", status); goto CLEANUP; } if(*status != SAI__OK) { errRepf(TASK_NAME, "Error closing outData file", status); goto CLEANUP; } }/* else { if(!(nStart >=0 && nStop)) printf("%s: Output scan condition failed: nStart(%d) >= nStop(%d) is FALSE\n",TASK_NAME, nStart, nStop); if(!(nFramesOutPrev == 0 || (nFramesOutPrev > 0 && nFramesOut > 0 && (double)nFramesOut/(double)nFramesOutPrev >= 0.5))) printf("%s: Output scan condition failed: nFramesOutPrev(%d) == 0 || (nFramesOutPrev(%d) > 0 && nFramesOut(%d) > 0 && nFramesOut/nFramesOutPrev (%f) >= 0.5) is FALSE\n", TASK_NAME, nFramesOutPrev, nFramesOutPrev, nFramesOut, (double)nFramesOut/(double)nFramesOutPrev); }*/ /* Prepare for next iteration */ nStartNext = hrStop + 1; hrStart = -1; hrStop = -1; } while (!done); /* Deallocate memory used by arrays */ if(MIRPOS) { MIRPOS = astFree(MIRPOS); } /* Close the file */ smf_close_file(&inData, status); } CLEANUP: /* Deallocate memory used by arrays */ if(inData) { smf_close_file(&inData, status); } if(outData) { smf_close_file(&outData, status); } /* END NDF */ ndfEnd(status); /* Write out the list of output NDF names, annulling the error if a null parameter value is supplied. */ if( *status == SAI__OK && gOut ) { grpList( "OUTFILES", 0, 0, NULL, gOut, status ); if( *status == PAR__NULL ) { errRep(FUNC_NAME, "Error writing OUTFILES!", status); errAnnul( status ); } } /* Delete groups */ if(gIn) { grpDelet(&gIn, status); } if(gOut) { grpDelet(&gOut, 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; }
void smf_write_bolomap( ThrWorkForce *wf, smfArray *res, smfArray *lut, smfArray *qua, smfDIMMData *dat, dim_t msize, const Grp *bolrootgrp, int varmapmethod, const int *lbnd_out, const int *ubnd_out, AstFrameSet *outfset, int *status ) { int addtomap=0; /* Set if adding to existing map */ size_t bstride; /* Bolometer stride */ double *curmap=NULL; /* Pointer to current map being rebinned */ double *curvar=NULL; /* Pointer to variance associate with curmap */ dim_t dsize; /* Size of data arrays in containers */ size_t idx=0; /* index within subgroup */ size_t k; /* loop counter */ int *lut_data=NULL; /* Pointer to DATA component of lut */ char name[GRP__SZNAM+1]; /* Buffer for storing names */ dim_t nbolo; /* Number of bolometers */ size_t nbolomaps = 0; /* Number of bolomaps written */ char *pname=NULL; /* Poiner to name */ smf_qual_t *qua_data=NULL; /* Pointer to DATA component of qua */ double *res_data=NULL; /* Pointer to DATA component of res */ if( *status != SAI__OK ) return; if( !res || !lut || !qua || !dat || !bolrootgrp || !lbnd_out || !ubnd_out || !outfset ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL inputs supplied", status ); return; } /* Loop over subgroup index (subarray) */ for( idx=0; idx<res->ndat; idx++ ) { smf_qual_t *bolomask = NULL; double *bmapweight = NULL; double *bmapweightsq = NULL; int *bhitsmap = NULL; /* Pointers to everything we need */ res_data = res->sdata[idx]->pntr[0]; lut_data = lut->sdata[idx]->pntr[0]; qua_data = qua->sdata[idx]->pntr[0]; smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, NULL, &dsize, &bstride, NULL, status ); /* Make a copy of the quality at first time slice as a good bolo mask, and then set quality to SMF__Q_BADB. Later we will unset BADB for one bolo at a time to make individual maps. */ bolomask = astMalloc( nbolo*sizeof(*bolomask) ); bmapweight = astMalloc( msize*sizeof(*bmapweight) ); bmapweightsq = astMalloc( msize*sizeof(*bmapweightsq) ); bhitsmap = astMalloc( msize*sizeof(*bhitsmap) ); if( *status == SAI__OK ) { for( k=0; k<nbolo; k++ ) { bolomask[k] = qua_data[k*bstride]; qua_data[k*bstride] = SMF__Q_BADB; } /* Identify good bolos in the copied mask and produce a map */ for( k=0; (k<nbolo)&&(*status==SAI__OK); k++ ) { if( !(bolomask[k]&SMF__Q_BADB) ) { Grp *mgrp=NULL; /* Temporary group to hold map names */ smfData *mapdata=NULL;/* smfData for new map */ char tmpname[GRP__SZNAM+1]; /* temp name buffer */ char thisbol[20]; /* name particular to this bolometer */ size_t col, row; char subarray[10]; nbolomaps++; /* Set the quality back to good for this single bolometer */ qua_data[k*bstride] = bolomask[k]; /* Create a name for the new map, take into account the chunk number and subarray. Only required if we are using a single output container. */ pname = tmpname; grpGet( bolrootgrp, 1, 1, &pname, sizeof(tmpname), status ); one_strlcpy( name, tmpname, sizeof(name), status ); one_strlcat( name, ".", sizeof(name), status ); /* Subarray, column and row. HDS does not care about case but we convert to upper case anyhow. */ smf_find_subarray( res->sdata[idx]->hdr, subarray, sizeof(subarray), NULL, status ); if (*status == SAI__OK) { size_t len = strlen(subarray); size_t n = 0; for (n=0; n<len; n++) { subarray[n] = toupper(subarray[n]); } } col = (k % res->sdata[idx]->dims[1])+1; row = (k / res->sdata[idx]->dims[1])+1; sprintf( thisbol, "%3sC%02zuR%02zu", subarray, col, /* x-coord */ row ); /* y-coord */ one_strlcat( name, thisbol, sizeof(name), status ); mgrp = grpNew( "bolomap", status ); grpPut1( mgrp, name, 0, status ); msgOutf( "", "*** Writing single bolo map %s", status, name ); /* Try to open an existing extention first. Create a new map array, and then later we'll add it to the existing one. If it isn't there, create it. */ smf_open_file( mgrp, 1, "UPDATE", 0, &mapdata, status ); if( *status == SAI__OK ) { /* Allocate memory for the new rebinned data */ curmap = astCalloc( msize, sizeof(*curmap) ); curvar = astCalloc( msize, sizeof(*curvar) ); addtomap = 1; } else if( *status == DAT__NAMIN ) { /* Create a new extension */ errAnnul( status ); smf_open_newfile ( mgrp, 1, SMF__DOUBLE, 2, lbnd_out, ubnd_out, SMF__MAP_VAR, &mapdata, status); /* Rebin directly into the newly mapped space */ if( *status == SAI__OK ) { curmap = mapdata->pntr[0]; curvar = mapdata->pntr[1]; addtomap = 0; } } /* Rebin the data for this single bolometer. Don't care about variance weighting because all samples from same detector are about the same. */ smf_rebinmap1( wf, res->sdata[idx], dat->noi ? dat->noi[0]->sdata[idx] : NULL, lut_data, 0, 0, 0, NULL, 0, SMF__Q_GOOD, varmapmethod, AST__REBININIT | AST__REBINEND, curmap, bmapweight, bmapweightsq, bhitsmap, curvar, msize, NULL, status ); /* If required, add this new map to the existing one */ if( addtomap ) { size_t i; double *oldmap=NULL; double *oldvar=NULL; double weight; if( *status == SAI__OK ) { oldmap = mapdata->pntr[0]; oldvar = mapdata->pntr[1]; for( i=0; i<msize; i++ ) { if( oldmap[i]==VAL__BADD ) { /* No data in this pixel in the old map, just copy */ oldmap[i] = curmap[i]; oldvar[i] = curvar[i]; } else if( curmap[i]!=VAL__BADD && oldvar[i]!=VAL__BADD && oldvar[i]!=0 && curvar[i]!=VAL__BADD && curvar[i]!=0 ) { /* Both old and new values available */ weight = 1/oldvar[i] + 1/curvar[i]; oldmap[i] = (oldmap[i]/oldvar[i] + curmap[i]/curvar[i]) / weight; oldvar[i] = 1/weight; } } } /* Free up temporary arrays */ curmap = astFree( curmap ); curvar = astFree( curvar ); } /* Write out COLNUM and ROWNUM to FITS header */ if( *status == SAI__OK ) { AstFitsChan *fitschan=NULL; fitschan = astFitsChan ( NULL, NULL, " " ); atlPtfti( fitschan, "COLNUM", col, "bolometer column", status); atlPtfti( fitschan, "ROWNUM", row, "bolometer row", status ); atlPtfts( fitschan, "SUBARRAY", subarray, "Subarray identifier", status ); kpgPtfts( mapdata->file->ndfid, fitschan, status ); if( fitschan ) fitschan = astAnnul( fitschan ); /* Set the bolo to bad quality again */ qua_data[k*bstride] = SMF__Q_BADB; /* Write WCS */ smf_set_moving(outfset,NULL,status); ndfPtwcs( outfset, mapdata->file->ndfid, status ); } /* Clean up */ if( mgrp ) grpDelet( &mgrp, status ); if( mapdata ) smf_close_file( &mapdata, status ); } } /* Set quality back to its original state */ for( k=0; k<nbolo; k++ ) { qua_data[k*bstride] = bolomask[k]; } } /* Free up memory */ bolomask = astFree( bolomask ); bmapweight = astFree( bmapweight ); bmapweightsq = astFree( bmapweightsq ); bhitsmap = astFree( bhitsmap ); } msgOutf( "", "*** Wrote %zu bolo maps", status, nbolomaps ); }
void smurf_unmakecube( int *status ) { /* Local Variables */ AstFrame *tfrm = NULL; /* Current Frame from input WCS */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *tmap = NULL; /* Base->current Mapping from input WCS */ AstSkyFrame *iskyfrm = NULL; /* SkyFrame from the input WCS Frameset */ Grp *detgrp = NULL; /* Group of detector names */ Grp *igrp1 = NULL; /* Group of input sky cube files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *ogrp = NULL; /* Group containing output file */ NdgProvenance *oprov = NULL;/* Provenance for the output NDF */ SkyCube *sky_cubes = NULL; /* Pointer to array of sky cube descriptions */ SkyCube *skycube = NULL; /* Pointer to next sky cube description */ char pabuf[ 10 ]; /* Text buffer for parameter value */ double params[ 4 ]; /* astResample parameters */ int axes[ 2 ]; /* Indices of selected axes */ int blank; /* Was a blank line just output? */ int flag; /* Was the group expression flagged? */ int ifile; /* Input file index */ int interp = 0; /* Pixel interpolation method */ int iskycube; /* Index of current sky cube */ int nel; /* Number of elements in 3D array */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int ondf; /* Output time series NDF identifier */ int outax[ 2 ]; /* Indices of corresponding output axes */ int overlap; /* Does time series overlap sky cube? */ int sdim[3]; /* Array of significant pixel axes */ int usedetpos; /* Should the detpos array be used? */ size_t ndet; /* Number of detectors supplied for "DETECTORS" */ size_t nskycube; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *data = NULL; /* Pointer to data struct */ void *in_data = NULL; /* Pointer to the input cube data array */ void *out_data = NULL; /* Pointer to the output cube data array */ #if defined(FPTRAP) feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW); #endif /* Check inherited status */ if( *status != SAI__OK ) return; /* We have not yet displayed a blank line on stdout. */ blank = 0; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Get a group holding the input sky cubes. */ kpg1Rgndf( "IN", 0, 1, "", &igrp1, &nskycube, status ); /* Create an array of structures to hold information about each input sky cube. */ sky_cubes = astMalloc( sizeof( SkyCube )*(size_t) nskycube ); /* Store a description of each sky cube. */ if( sky_cubes ) { for( iskycube = 0; iskycube < nskycube; iskycube++ ) { skycube = sky_cubes + iskycube; /* Get an NDF identifier for the next sky cube. */ ndgNdfas( igrp1, iskycube + 1, "READ", &(skycube->indf), status ); /* Get the WCS FrameSet from the sky cube, together with its pixel index bounds. */ kpg1Asget( skycube->indf, 3, 0, 1, 1, sdim, skycube->slbnd, skycube->subnd, &wcsin, status ); /* Get the base->current Mapping from the input WCS FrameSet, and split it into two Mappings; one (iskymap) that maps the first 2 GRID axes into celestial sky coordinates, and one (ispecmap) that maps the third GRID axis into a spectral coordinate. Also extract the SpecFrame and SkyFrame from the current Frame. */ tmap = astGetMapping( wcsin, AST__BASE, AST__CURRENT ); tfrm = astGetFrame( wcsin, AST__CURRENT ); axes[ 0 ] = 1; axes[ 1 ] = 2; astMapSplit( tmap, 2, axes, outax, &(skycube->iskymap) ); iskyfrm = astPickAxes( tfrm, 2, outax, NULL ); axes[ 0 ] = 3; astMapSplit( tmap, 1, axes, outax, &(skycube->ispecmap) ); skycube->ispecfrm = astPickAxes( tfrm, 1, outax, NULL ); /* Create a copy of "iskyfrm" representing absolute coords rather than offsets. We assume the target is moving if the cube represents offsets. */ skycube->abskyfrm = astCopy( iskyfrm ); astClear( skycube->abskyfrm, "SkyRefIs" ); skycube->moving = ( *status == SAI__OK && !strcmp( astGetC( iskyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; /* Invert the Mappings (for the convenience of smf_resamplecube), so that they go from current Frame to grid axis. */ astInvert( skycube->ispecmap ); astInvert( skycube->iskymap ); /* For efficiency, annul manually the unneeded AST objects created in this loop. */ wcsin = astAnnul( wcsin ); tmap = astAnnul( tmap ); tfrm = astAnnul( tfrm ); iskyfrm = astAnnul( iskyfrm ); } } /* See if the detector positions are to be read from the RECEPPOS array in the template NDFs. Otherwise, they are calculated on the basis of the FPLANEX/Y arrays. */ parGet0l( "USEDETPOS", &usedetpos, status ); /* Get the detectors to use. If a null value is supplied, annull the error. Otherwise, make the group case insensitive. */ detgrp = NULL; if( *status == SAI__OK ) { kpg1Gtgrp( "DETECTORS", &detgrp, &ndet, status ); if( *status == PAR__NULL ) { errAnnul( status ); if (detgrp) { grpDelet( &detgrp, status ); } } else { grpSetcs( detgrp, 0, status ); } } /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Create a group holding the names of the corresponding output NDFs. */ ndgCreat ( "OUT", igrp2, &ogrp, &outsize, &flag, status ); if( outsize != size && *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "O", outsize ); msgSeti( "I", size ); errRep( "", "Numbers of input reference cubes (^I) and output " "cubes (^O) differ.", status ); } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Obtain information about the current template NDF, but do not map the arrays. */ smf_open_file( igrp2, ifile, "READ", SMF__NOCREATE_DATA, &data, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( data->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( data->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Report the name of the input template. */ smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); /* Create the output NDF by propagation from the input template NDF. Everything is copied except for the array components and any PROVENANCE extension. */ ndgNdfpr( data->file->ndfid, "TITLE,LABEL,UNITS,AXIS,WCS,HISTORY," "NOEXTENSION(PROVENANCE)", ogrp, ifile, &ondf, status ); /* Ensure the output NDF has a history component. */ ndfHcre( ondf, status ); /* Get a pointer to the mapped output data array. Set all values bad. */ ndfMap( ondf, "DATA", "_REAL", "WRITE/BAD", &out_data, &nel, status ); /* If the detector positions are to calculated on the basis of FPLANEX/Y rather than detpos, then free the detpos array in the templates smfHead structure. This will cause smf_tslice_ast to use the fplanex/y values. */ if( !usedetpos && data->hdr->detpos ) { astFree( (double *) data->hdr->detpos ); data->hdr->detpos = NULL; } /* Get a pointer to a structure holding provenance information for the output time series. */ oprov = ndgReadProv( ondf, "SMURF:UNMAKECUBE", status ); /* Record details of the template in the provenance structure for the output time series. */ ndgPutProv( oprov, data->file->ndfid, NULL, 0, status ); /* Loop round all input sky cubes. */ for( iskycube = 0; iskycube < nskycube; iskycube++ ) { skycube = sky_cubes + iskycube; /* Record details of the input cube in the provenance extension of the output time series. */ ndgPutProv( oprov, skycube->indf, NULL, 0, status ); /* See if the current time series overlaps the current sky cube. */ smf_resampcube( data, skycube->abskyfrm, skycube->iskymap, skycube->ispecfrm, skycube->ispecmap, detgrp, skycube->moving, skycube->slbnd, skycube->subnd, interp, params, NULL, NULL, &overlap, status ); /* If not, pass on to the next sky cube. */ if( overlap ) { /* Report the name of the sky cube. */ ndfMsg( "NDF", skycube->indf ); msgOutif( MSG__NORM, " ", " Re-sampling ^NDF", status ); /* Map the data array in the current sky cube. */ ndfMap( skycube->indf, "DATA", "_REAL", "READ", &in_data, &nel, status ); /* Resample the cube data into the output time series. */ smf_resampcube( data, skycube->abskyfrm, skycube->iskymap, skycube->ispecfrm, skycube->ispecmap, detgrp, skycube->moving, skycube->slbnd, skycube->subnd, interp, params, in_data, out_data, &overlap, status ); /* Unmap the data array. */ ndfUnmap( skycube->indf, "DATA", status ); } } /* Write the provenance structure to the output NDF, and then free it. */ ndgWriteProv( oprov, ondf, 1, status ); oprov =ndgFreeProv( oprov, status ); /* Close the input time series file. */ if( data != NULL ) { smf_close_file( &data, status ); data = NULL; } /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( data != NULL ) { smf_close_file( &data, status ); data = NULL; } /* Free remaining resources. */ if( detgrp != NULL) grpDelet( &detgrp, status); if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( ogrp != NULL) grpDelet( &ogrp, status); sky_cubes = astFree( sky_cubes ); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
void smf_write_itermap( ThrWorkForce *wf, const double *map, const double *mapvar, const smf_qual_t *mapqua, dim_t msize, const Grp *iterrootgrp, size_t contchunk, int iter, const int *lbnd_out, const int *ubnd_out, AstFrameSet *outfset, const smfHead *hdr, const smfArray *qua, int *status ) { int flags; /* Flags indicating required NDF components */ Grp *mgrp=NULL; /* Temporary group to hold map name */ smfData *imapdata=NULL; /* smfData for this iteration map */ char name[GRP__SZNAM+1]; /* Buffer for storing name */ char *pname=NULL; /* Poiner to name */ char tmpname[GRP__SZNAM+1]; /* temp name buffer */ char tempstr[20]; if( *status != SAI__OK ) return; if( !map || !mapvar || !iterrootgrp || !lbnd_out || !ubnd_out || !outfset ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": NULL inputs supplied", status ); return; } if( hdr && !qua ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": hdr supplied but qua is NULL", status ); return; } /* Create a name for this iteration map, take into account the chunk number. Only required if we are using a single output container. */ pname = tmpname; grpGet( iterrootgrp, 1, 1, &pname, sizeof(tmpname), status ); one_strlcpy( name, tmpname, sizeof(name), status ); one_strlcat( name, ".", sizeof(name), status ); /* Continuous chunk number */ sprintf(tempstr, "CH%02zd", contchunk); one_strlcat( name, tempstr, sizeof(name), status ); /* Iteration number */ sprintf( tempstr, "I%03i", iter+1 ); one_strlcat( name, tempstr, sizeof(name), status ); mgrp = grpNew( "itermap", status ); grpPut1( mgrp, name, 0, status ); msgOutf( "", "*** Writing map from this iteration to %s", status, name ); flags = SMF__MAP_VAR; if( mapqua ) flags |= SMF__MAP_QUAL; smf_open_newfile ( wf, mgrp, 1, SMF__DOUBLE, 2, lbnd_out, ubnd_out, flags, &imapdata, status); /* Copy over the signal and variance maps */ if( *status == SAI__OK ) { memcpy( imapdata->pntr[0], map, msize*sizeof(*map) ); memcpy( imapdata->pntr[1], mapvar, msize*sizeof(*mapvar) ); if( mapqua ) memcpy( imapdata->qual, mapqua, msize*sizeof(*mapqua) ); } /* Write out a FITS header */ if( (*status == SAI__OK) && hdr && hdr->allState ) { AstFitsChan *fitschan=NULL; JCMTState *allState = hdr->allState; char *obsidss=NULL; char obsidssbuf[SZFITSTR]; double iter_nboloeff; size_t nmap; size_t ngood_tslices; dim_t ntslice; /* Number of time slices */ fitschan = astFitsChan ( NULL, NULL, " " ); obsidss = smf_getobsidss( hdr->fitshdr, NULL, 0, obsidssbuf, sizeof(obsidssbuf), status ); if( obsidss ) { atlPtfts( fitschan, "OBSIDSS", obsidss, "Unique observation subsys identifier", status ); } atlPtfti( fitschan, "SEQSTART", allState[0].rts_num, "RTS index number of first frame", status ); ntslice = hdr->nframes; atlPtfti( fitschan, "SEQEND", allState[ntslice-1].rts_num, "RTS index number of last frame", status ); /* calculate the effective number of bolometers for this iteration */ smf_qualstats_model( wf, SMF__QFAM_TSERIES, 1, qua, NULL, NULL, &nmap, NULL, NULL, &ngood_tslices, NULL, NULL, status ); iter_nboloeff = (double)nmap / (double)ngood_tslices; atlPtftd( fitschan, "NBOLOEFF", iter_nboloeff, "Effective bolometer count", status ); kpgPtfts( imapdata->file->ndfid, fitschan, status ); if( fitschan ) fitschan = astAnnul( fitschan ); } /* Write WCS (protecting the pointer dereference) */ smf_set_moving(outfset,NULL,status); if (*status == SAI__OK && imapdata) { ndfPtwcs( outfset, imapdata->file->ndfid, status ); } /* Clean up */ if( mgrp ) grpDelet( &mgrp, status ); smf_close_file( wf, &imapdata, 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 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 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_dreamweights ( int *status ) { /* Local Variables */ Grp *confgrp = NULL; /* Group containing configuration file */ smfData *data = NULL; /* Input data */ const int defgridminmax[] = { -4, 4, -4, 4 }; /* Default extent xmin,xmax,ymin,ymax */ int gridminmax[4]; /* Extent of grid points array */ int gridpts[DREAM__MXGRID][2]; /* Array of points for reconstruction grid */ double gridstep; /* Size of reconstruction grid in arcsec */ size_t i; /* Loop counter */ Grp *igrp = NULL; /* Input group of NDFs */ size_t size; /* Size of input Grp of files */ AstKeyMap *keymap = NULL; /* Pointer to keymap of config settings */ size_t ksize; /* Size of group containing CONFIG file */ int ngrid; /* Number of points in reconstruction grid */ Grp *ogrp = NULL; /* Group of output weights files */ size_t outsize; /* Size of output Grp of files */ /* Main routine */ ndfBegin(); /* Get group of input raw data NDFs */ kpg1Rgndf( "NDF", 0, 1, "", &igrp, &size, status ); /* Get group of output files from user: assume 1 output file for every input file */ kpg1Wgndf( "OUT", igrp, size, size, "More output files required...", &ogrp, &outsize, status ); /* Read configuration settings into keymap */ if (*status == SAI__OK) { kpg1Gtgrp( "CONFIG", &confgrp, &ksize, status ); if (*status == PAR__NULL) { /* NULL value so provide defaults */ errAnnul( status ); msgOutif(MSG__VERB, " ", "No config file specified - assuming default configuration parameters", status); keymap = astKeyMap(" " ); astMapPut1I( keymap, "GRIDMINMAX", 4, (int*)defgridminmax, " " ); astMapPut0D( keymap, "GRIDSTEP", 6.28, " " ); } else { kpg1Kymap( confgrp, &keymap, status ); } if( confgrp ) grpDelet( &confgrp, status ); } /* Determine grid parameters from inputs given above */ smf_dream_getgrid( keymap, &gridstep, &ngrid, gridminmax, gridpts, status); /* Annul keymap immediately as it is no longer required */ if (keymap) keymap = astAnnul( keymap ); /* Loop over number of files */ for ( i=1; (i<= size) && (*status == SAI__OK); i++) { /* Open file */ smf_open_file( NULL, igrp, i, "READ", 0, &data, status ); /* Calculate weights based on this file */ smf_dream_calcweights( data, ogrp, i, gridstep, ngrid, gridminmax, &(gridpts[0]), status); /* Immediately check status on return and abort if an error occured */ if ( *status != SAI__OK ) { msgSeti("I",i); msgSeti("N",size); errRep(FUNC_NAME, "Unable to determine DREAM weights for file ^I of ^N", status); } smf_close_file( NULL, &data, status ); } /* Free up resources */ if ( ogrp != NULL ) { grpDelet( &ogrp, status); } if ( igrp != NULL ) { grpDelet( &igrp, status); } ndfEnd( status ); msgOutif(MSG__VERB," ", "DREAM weights calculation completed successfully", 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_unmakemap( int *status ) { /* Local Variables */ AstFrameSet *wcsin = NULL; /* WCS Frameset for input cube */ AstMapping *skymap; /* GRID->SkyFrame Mapping from input WCS */ AstSkyFrame *abskyfrm; /* Input SkyFrame (always absolute) */ AstSkyFrame *skyfrm = NULL;/* SkyFrame from the input WCS Frameset */ Grp *igrp1 = NULL; /* Group of input sky files */ Grp *igrp2 = NULL; /* Group of input template files */ Grp *igrpq = NULL; /* Group of input Q sky files */ Grp *igrpu = NULL; /* Group of input U sky files */ Grp *ogrp = NULL; /* Group containing output file */ ThrWorkForce *wf = NULL; /* Pointer to a pool of worker threads */ char pabuf[ 10 ]; /* Text buffer for parameter value */ dim_t iel; /* Index of next element */ dim_t ndata; /* Number of elements in array */ dim_t ntslice; /* Number of time slices in array */ double *ang_data = NULL; /* Pointer to the FP orientation angles */ double *in_data = NULL; /* Pointer to the input I sky map */ double *inq_data = NULL; /* Pointer to the input Q sky map */ double *inu_data = NULL; /* Pointer to the input U sky map */ double *outq_data = NULL; /* Pointer to the Q time series data */ double *outu_data = NULL; /* Pointer to the U time series data */ double *pd; /* Pointer to next element */ double *pq = NULL; /* Pointer to next Q time series value */ double *pu = NULL; /* Pointer to next U time series value */ double angrot; /* Angle from focal plane X axis to fixed analyser */ double paoff; /* WPLATE value corresponding to POL_ANG=0.0 */ double params[ 4 ]; /* astResample parameters */ double sigma; /* Standard deviation of noise to add to output */ int alignsys; /* Align data in the map's system? */ int flag; /* Was the group expression flagged? */ int harmonic; /* The requested harmonic */ int ifile; /* Input file index */ int indf; /* Input sky map NDF identifier */ int indfin; /* Input template cube NDF identifier */ int indfout; /* Output cube NDF identifier */ int indfq; /* Input Q map NDF identifier */ int indfu; /* Input U map NDF identifier */ int interp = 0; /* Pixel interpolation method */ int moving; /* Is the telescope base position changing? */ int nel; /* Number of elements in array */ int nelqu; /* Number of elements in Q or U array */ int ngood; /* No. of good values in putput cube */ int nparam = 0; /* No. of parameters required for interpolation scheme */ int pasign; /* Indicates sense of POL_ANG value */ int sdim[ 2 ]; /* Array of significant pixel axes */ int slbnd[ 2 ]; /* Array of lower bounds of input map */ int subnd[ 2 ]; /* Array of upper bounds of input map */ size_t nskymap; /* Number of supplied sky cubes */ size_t outsize; /* Number of files in output group */ size_t size; /* Number of files in input group */ smfData *odata = NULL; /* Pointer to output data struct */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context */ astBegin; /* Begin an NDF context. */ 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 an identifier for the input NDF. We use NDG (via kpg1Rgndf) instead of calling ndfAssoc directly since NDF/HDS has problems with file names containing spaces, which NDG does not have. */ kpg1Rgndf( "IN", 1, 1, "", &igrp1, &nskymap, status ); ndgNdfas( igrp1, 1, "READ", &indf, status ); /* Map the data array in the input sky map. */ ndfMap( indf, "DATA", "_DOUBLE", "READ", (void **) &in_data, &nel, status ); /* Get the WCS FrameSet from the sky map, together with its pixel index bounds. */ kpg1Asget( indf, 2, 0, 1, 1, sdim, slbnd, subnd, &wcsin, status ); /* Check the current Frame is a SKY frame. */ skyfrm = astGetFrame( wcsin, AST__CURRENT ); if( !astIsASkyFrame( skyfrm ) && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( " ", " Current Frame in ^N is not a SKY Frame.", status ); } /* Get a copy of the current frame that represents absolute coords rather than offsets. We assume the target is moving if the map represents offsets. */ moving = ( *status == SAI__OK && !strcmp( astGetC( skyfrm, "SkyRefIs" ), "Origin" ) ) ? 1 : 0; abskyfrm = astCopy( skyfrm ); astClear( abskyfrm, "SkyRefIs" ); /* If the ALIGNSYS parameter is TRUE then we align the raw data with the map in the current system of the map, rather than the default ICRS. */ parGet0l( "ALIGNSYS", &alignsys, status ); if( alignsys ) astSetC( abskyfrm, "AlignSystem", astGetC( abskyfrm, "System" ) ); /* Get the Mapping from the Sky Frame to grid axis in the iput map. */ skymap = astGetMapping( wcsin, AST__CURRENT, AST__BASE ); /* Get the pixel interpolation scheme to use. */ parChoic( "INTERP", "NEAREST", "NEAREST,LINEAR,SINC," "SINCSINC,SINCCOS,SINCGAUSS,SOMB,SOMBCOS", 1, pabuf, 10, status ); if( !strcmp( pabuf, "NEAREST" ) ) { interp = AST__NEAREST; nparam = 0; } else if( !strcmp( pabuf, "LINEAR" ) ) { interp = AST__LINEAR; nparam = 0; } else if( !strcmp( pabuf, "SINC" ) ) { interp = AST__SINC; nparam = 1; } else if( !strcmp( pabuf, "SINCSINC" ) ) { interp = AST__SINCSINC; nparam = 2; } else if( !strcmp( pabuf, "SINCCOS" ) ) { interp = AST__SINCCOS; nparam = 2; } else if( !strcmp( pabuf, "SINCGAUSS" ) ) { interp = AST__SINCGAUSS; nparam = 2; } else if( !strcmp( pabuf, "SOMB" ) ) { interp = AST__SOMB; nparam = 1; } else if( !strcmp( pabuf, "SOMBCOS" ) ) { interp = AST__SOMBCOS; nparam = 2; } else if( *status == SAI__OK ) { nparam = 0; *status = SAI__ERROR; msgSetc( "V", pabuf ); errRep( "", "Support not available for INTERP = ^V (programming " "error)", status ); } /* Get an additional parameter vector if required. */ if( nparam > 0 ) parExacd( "PARAMS", nparam, params, status ); /* Get a group of reference time series files to use as templates for the output time series files.*/ ndgAssoc( "REF", 1, &igrp2, &size, &flag, status ); /* Get output file(s) */ kpg1Wgndf( "OUT", igrp2, size, size, "More output files required...", &ogrp, &outsize, status ); /* Get he noise level to add to the output data. */ parGet0d( "SIGMA", &sigma, status ); /* Get any Q and U input maps. */ if( *status == SAI__OK ) { kpg1Rgndf( "QIN", 1, 1, "", &igrpq, &nskymap, status ); ndgNdfas( igrpq, 1, "READ", &indfq, status ); ndfMap( indfq, "DATA", "_DOUBLE", "READ", (void **) &inq_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "Q", indfq ); *status = SAI__ERROR; errRep( "", "Q image '^Q' is not the same size as the I image.", status ); } kpg1Rgndf( "UIN", 1, 1, "", &igrpu, &nskymap, status ); ndgNdfas( igrpu, 1, "READ", &indfu, status ); ndfMap( indfu, "DATA", "_DOUBLE", "READ", (void **) &inu_data, &nelqu, status ); if( nelqu != nel && *status == SAI__OK ) { ndfMsg( "U", indfu ); *status = SAI__ERROR; errRep( "", "U image '^U' is not the same size as the I image.", status ); } if( *status == PAR__NULL ) { ndfAnnul( &indfq, status ); ndfAnnul( &indfu, status ); inq_data = NULL; inu_data = NULL; errAnnul( status ); } else { parGet0d( "ANGROT", &angrot, status ); parGet0d( "PAOFF", &paoff, status ); parGet0l( "PASIGN", &pasign, status ); } } /* Loop round all the template time series files. */ for( ifile = 1; ifile <= (int) size && *status == SAI__OK; ifile++ ) { /* Start a new NDF context. */ ndfBegin(); /* Create the output NDF by propagating everything from the input, except for quality and variance. */ ndgNdfas( igrp2, ifile, "READ", &indfin, status ); ndfMsg( "FILE", indfin ); msgSeti( "THISFILE", ifile ); msgSeti( "NUMFILES", size ); msgOutif( MSG__NORM, " ", "Simulating ^THISFILE/^NUMFILES ^FILE", status ); ndgNdfpr( indfin, "DATA,HISTORY,LABEL,TITLE,WCS,UNITS,EXTENSION(*)", ogrp, ifile, &indfout, status ); ndfAnnul( &indfin, status ); ndfAnnul( &indfout, status ); /* We now re-open the output NDF and then modify its data values. */ smf_open_file( wf, ogrp, ifile, "UPDATE", 0, &odata, status ); /* Issue a suitable message and abort if anything went wrong. */ if( *status != SAI__OK ) { errRep( FUNC_NAME, "Could not open input template file.", status ); break; } else { if( odata->file == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfFile associated with smfData.", status ); break; } else if( odata->hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "No smfHead associated with smfData.", status ); break; } } /* Check the reference time series contains double precision values. */ smf_dtype_check_fatal( odata, NULL, SMF__DOUBLE, status ); /* Get the total number of data elements, and the number of time slices. */ smf_get_dims( odata, NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status ); /* Fill the output with bad values. */ if( *status == SAI__OK ) { pd = odata->pntr[ 0 ]; for( iel = 0; iel < ndata; iel++ ) *(pd++) = VAL__BADD; } /* Resample the sky map data into the output time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, in_data, odata->pntr[ 0 ], NULL, &ngood, status ); /* Issue a wrning if there is no good data in the output cube. */ if( ngood == 0 ) msgOutif( MSG__NORM, " ", " Output contains no " "good data values.", status ); /* If Q and U maps have been given, allocate room to hold resampled Q and U values, and fill them with bad values. */ if( inq_data && inu_data ) { pq = outq_data = astMalloc( ndata*sizeof( *outq_data ) ); pu = outu_data = astMalloc( ndata*sizeof( *outu_data ) ); if( *status == SAI__OK ) { for( iel = 0; iel < ndata; iel++ ) { *(pu++) = VAL__BADD; *(pq++) = VAL__BADD; } } /* Determine the harmonic to use. */ parGet0i( "HARMONIC", &harmonic, status ); /* Allocate room for an array to hold the anti-clockwise angle from the focal plane Y axis to the Y pixel axis in the reference map, at each time slice. */ ang_data = astMalloc( ntslice*sizeof( *ang_data ) ); /* Resample them both into 3D time series. */ smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inq_data, outq_data, ang_data, &ngood, status ); smf_resampmap( wf, odata, abskyfrm, skymap, moving, slbnd, subnd, interp, params, sigma, inu_data, outu_data, NULL, &ngood, status ); /* Combine these time series with the main output time series so that the main output is analysed intensity. */ smf_uncalc_iqu( wf, odata, odata->pntr[ 0 ], outq_data, outu_data, ang_data, pasign, AST__DD2R*paoff, AST__DD2R*angrot, harmonic, status ); /* Release work space. */ outq_data = astFree( outq_data ); outu_data = astFree( outu_data ); ang_data = astFree( ang_data ); } /* Close the output time series file. */ smf_close_file( wf, &odata, status ); /* End the NDF context. */ ndfEnd( status ); } /* Close any input data file that is still open due to an early exit from the above loop. */ if( odata != NULL ) { smf_close_file( wf, &odata, status ); odata = NULL; } /* Free remaining resources. */ if( igrp1 != NULL) grpDelet( &igrp1, status); if( igrp2 != NULL) grpDelet( &igrp2, status); if( igrpq != NULL) grpDelet( &igrpq, status); if( igrpu != NULL) grpDelet( &igrpu, status); if( ogrp != NULL) grpDelet( &ogrp, status); /* End the NDF context. */ ndfEnd( status ); /* End the tile's AST context. */ astEnd; /* Issue a status indication.*/ if( *status == SAI__OK ) { msgOutif(MSG__VERB," ",TASK_NAME " succeeded, time series written.", status); } else { msgOutif(MSG__VERB," ",TASK_NAME " failed.", status); } }
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 smf_import_array( smfData *refdata, const char *name, int bad, int expand, double *dataptr, int *status ) { /* Local Variables: */ Grp *igrp; /* Group holding NDF name */ dim_t nbolo; /* Number of bolometers */ dim_t ntslice; /* Number of time slices */ double *pin; /* Pointer to next input value */ double *pout; /* Pointer to next output value */ double mean; /* Mean value int he plane */ double vsum; /* Sum of good data values */ int nbad; /* Number of bad data values */ int ngood; /* Number of good data values */ size_t bstride; /* Stride between bolometer values */ size_t i; /* Loop count */ size_t j; /* Loop count */ dim_t nel; /* Number of elements in array */ size_t tstride; /* Stride between time slices */ smfData *data; /* Model for one sub-array */ /* Check inherited status. */ if( *status != SAI__OK ) return; /* Attempt to open the NDF. */ igrp = grpNew( " ", status ); grpPut1( igrp, name, 0, status ); smf_open_file( igrp, 1, "READ", 0, &data, status ); grpDelet( &igrp, status ); /* Ensure the smfData read from the NDF uses the same data ordering as the reference smfData. */ smf_dataOrder( data, refdata->isTordered, status ); if( *status == SAI__OK ) { /* Check the data type and dimensions of the NDF are the same as the reference NDF. */ if( data->dtype != SMF__DOUBLE ) { *status = SAI__ERROR; errRepf( " ", "NDF '%s' has incorrect data type - should be " "DOUBLE PRECISION.", status, name ); } else if( data->ndims != refdata->ndims ) { *status = SAI__ERROR; errRepf( " ", "NDF '%s' is %zu dimensional - must be %zu " "dimensional.", status, name, data->ndims, refdata->ndims ); } else if( !expand || refdata->ndims != 3 ) { expand = 0; for( i = 0; i < refdata->ndims; i++ ) { if( data->dims[i] != refdata->dims[i] && *status == SAI__OK ){ *status = SAI__ERROR; errRepf( " ", "NDF '%s' has incorrect dimension %zu on " "pixel axis %zu - should be %zu.", status, name, data->dims[i], i + 1, refdata->dims[i] ); } else if( data->lbnd[i] != refdata->lbnd[i] && *status == SAI__OK ){ *status = SAI__ERROR; errRepf( " ", "NDF '%s' has incorrect lower bound %d on " "pixel axis %zu - should be %d.", status, name, data->lbnd[i], i + 1, refdata->lbnd[i] ); } } } else { for( i = 0; i < refdata->ndims; i++ ) { if( data->dims[i] == 1 ) { } else if( data->dims[i] != refdata->dims[i] && *status == SAI__OK ){ *status = SAI__ERROR; errRepf( " ", "NDF '%s' has incorrect dimension %zu on " "pixel axis %zu - should be %zu.", status, name, data->dims[i], i + 1, refdata->dims[i] ); } else if( data->lbnd[i] != refdata->lbnd[i] && *status == SAI__OK ){ *status = SAI__ERROR; errRepf( " ", "NDF '%s' has incorrect lower bound %d on " "pixel axis %zu - should be %d.", status, name, data->lbnd[i], i + 1, refdata->lbnd[i] ); } } } /* Get the smfData dimensions and strides. */ smf_get_dims( refdata, NULL, NULL, &nbolo, &ntslice, &nel, &bstride, &tstride, status ); /* Copy the values into the model array, replacing bad values as required. */ if( *status == SAI__OK ) { pin = data->pntr[0]; pout = dataptr; if( data->ndims < 3 ) data->dims[2] = 1; if( data->ndims < 2 ) data->dims[1] = 1; /* Copy the data into the returned array unchanged. */ if( expand ) { pin = (double *) data->pntr[0]; for( i = 0; i < ntslice; i++,pin++ ) { pout = dataptr + i*tstride; for( j = 0; j < nbolo; j++ ) { if( *pin != VAL__BADD ) { *pout = *pin; } else { *pout = VAL__BADD; } pout += bstride; } } } else { memcpy( pout, pin, nel*sizeof(*pin) ); } /* Retain bad values. */ if( bad == 0 ) { /* Replace bad values with zero. */ } else if( bad == 1 ) { pout = dataptr; for( i = 0; i < nel; i++,pout++ ) { if( *pout == VAL__BADD ) *pout = 0.0; } /* Replace bad values with the mean value in the time slice. */ } else if( bad == 2 ) { pout = dataptr; mean = VAL__BADD; for( i = 0; i < ntslice; i++ ) { vsum = 0.0; ngood = 0; nbad = 0; pout = dataptr + i*tstride; for( j = 0; j < nbolo; j++ ) { if( *pout != VAL__BADD ) { vsum += *pout; ngood++; } else { nbad++; } pout += bstride; } if( ngood > 0 ) mean = vsum/ngood; if( nbad > 0 ) { if( mean != VAL__BADD ) { pout = dataptr + i*tstride; for( j = 0; j < nbolo; j++ ) { if( *pout == VAL__BADD ) *pout = mean; pout += bstride; } } else { *status = SAI__ERROR; errRepf( " ", "NDF '%s' has no good values in plane " "%zu.", status, name, i ); break; } } } } } /* Close the NDF. */ smf_close_file( &data, status ); } }